bbc206b0fffbc28f371088e76ac10c9c530c9f0b.svn-base 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712
  1. /**
  2. * @license Highcharts JS v4.2.5 (2016-05-06)
  3. *
  4. * (c) 2011-2016 Torstein Honsi
  5. *
  6. * License: www.highcharts.com/license
  7. */
  8. /* eslint indent: [2, 4] */
  9. (function (factory) {
  10. if (typeof module === 'object' && module.exports) {
  11. module.exports = factory;
  12. } else {
  13. factory(Highcharts);
  14. }
  15. }(function (Highcharts) {
  16. var UNDEFINED,
  17. Axis = Highcharts.Axis,
  18. Chart = Highcharts.Chart,
  19. Color = Highcharts.Color,
  20. Legend = Highcharts.Legend,
  21. LegendSymbolMixin = Highcharts.LegendSymbolMixin,
  22. Series = Highcharts.Series,
  23. Point = Highcharts.Point,
  24. defaultOptions = Highcharts.getOptions(),
  25. each = Highcharts.each,
  26. extend = Highcharts.extend,
  27. extendClass = Highcharts.extendClass,
  28. isNumber = Highcharts.isNumber,
  29. merge = Highcharts.merge,
  30. pick = Highcharts.pick,
  31. seriesTypes = Highcharts.seriesTypes,
  32. wrap = Highcharts.wrap,
  33. noop = function () {};
  34. /**
  35. * The ColorAxis object for inclusion in gradient legends
  36. */
  37. var ColorAxis = Highcharts.ColorAxis = function () {
  38. this.isColorAxis = true;
  39. this.init.apply(this, arguments);
  40. };
  41. extend(ColorAxis.prototype, Axis.prototype);
  42. extend(ColorAxis.prototype, {
  43. defaultColorAxisOptions: {
  44. lineWidth: 0,
  45. minPadding: 0,
  46. maxPadding: 0,
  47. gridLineWidth: 1,
  48. tickPixelInterval: 72,
  49. startOnTick: true,
  50. endOnTick: true,
  51. offset: 0,
  52. marker: {
  53. animation: {
  54. duration: 50
  55. },
  56. color: 'gray',
  57. width: 0.01
  58. },
  59. labels: {
  60. overflow: 'justify'
  61. },
  62. minColor: '#EFEFFF',
  63. maxColor: '#003875',
  64. tickLength: 5
  65. },
  66. init: function (chart, userOptions) {
  67. var horiz = chart.options.legend.layout !== 'vertical',
  68. options;
  69. // Build the options
  70. options = merge(this.defaultColorAxisOptions, {
  71. side: horiz ? 2 : 1,
  72. reversed: !horiz
  73. }, userOptions, {
  74. opposite: !horiz,
  75. showEmpty: false,
  76. title: null,
  77. isColor: true
  78. });
  79. Axis.prototype.init.call(this, chart, options);
  80. // Base init() pushes it to the xAxis array, now pop it again
  81. //chart[this.isXAxis ? 'xAxis' : 'yAxis'].pop();
  82. // Prepare data classes
  83. if (userOptions.dataClasses) {
  84. this.initDataClasses(userOptions);
  85. }
  86. this.initStops(userOptions);
  87. // Override original axis properties
  88. this.horiz = horiz;
  89. this.zoomEnabled = false;
  90. },
  91. /*
  92. * Return an intermediate color between two colors, according to pos where 0
  93. * is the from color and 1 is the to color.
  94. * NOTE: Changes here should be copied
  95. * to the same function in drilldown.src.js and solid-gauge-src.js.
  96. */
  97. tweenColors: function (from, to, pos) {
  98. // Check for has alpha, because rgba colors perform worse due to lack of
  99. // support in WebKit.
  100. var hasAlpha,
  101. ret;
  102. // Unsupported color, return to-color (#3920)
  103. if (!to.rgba.length || !from.rgba.length) {
  104. ret = to.input || 'none';
  105. // Interpolate
  106. } else {
  107. from = from.rgba;
  108. to = to.rgba;
  109. hasAlpha = (to[3] !== 1 || from[3] !== 1);
  110. ret = (hasAlpha ? 'rgba(' : 'rgb(') +
  111. Math.round(to[0] + (from[0] - to[0]) * (1 - pos)) + ',' +
  112. Math.round(to[1] + (from[1] - to[1]) * (1 - pos)) + ',' +
  113. Math.round(to[2] + (from[2] - to[2]) * (1 - pos)) +
  114. (hasAlpha ? (',' + (to[3] + (from[3] - to[3]) * (1 - pos))) : '') + ')';
  115. }
  116. return ret;
  117. },
  118. initDataClasses: function (userOptions) {
  119. var axis = this,
  120. chart = this.chart,
  121. dataClasses,
  122. colorCounter = 0,
  123. options = this.options,
  124. len = userOptions.dataClasses.length;
  125. this.dataClasses = dataClasses = [];
  126. this.legendItems = [];
  127. each(userOptions.dataClasses, function (dataClass, i) {
  128. var colors;
  129. dataClass = merge(dataClass);
  130. dataClasses.push(dataClass);
  131. if (!dataClass.color) {
  132. if (options.dataClassColor === 'category') {
  133. colors = chart.options.colors;
  134. dataClass.color = colors[colorCounter++];
  135. // loop back to zero
  136. if (colorCounter === colors.length) {
  137. colorCounter = 0;
  138. }
  139. } else {
  140. dataClass.color = axis.tweenColors(
  141. Color(options.minColor),
  142. Color(options.maxColor),
  143. len < 2 ? 0.5 : i / (len - 1) // #3219
  144. );
  145. }
  146. }
  147. });
  148. },
  149. initStops: function (userOptions) {
  150. this.stops = userOptions.stops || [
  151. [0, this.options.minColor],
  152. [1, this.options.maxColor]
  153. ];
  154. each(this.stops, function (stop) {
  155. stop.color = Color(stop[1]);
  156. });
  157. },
  158. /**
  159. * Extend the setOptions method to process extreme colors and color
  160. * stops.
  161. */
  162. setOptions: function (userOptions) {
  163. Axis.prototype.setOptions.call(this, userOptions);
  164. this.options.crosshair = this.options.marker;
  165. this.coll = 'colorAxis';
  166. },
  167. setAxisSize: function () {
  168. var symbol = this.legendSymbol,
  169. chart = this.chart,
  170. x,
  171. y,
  172. width,
  173. height;
  174. if (symbol) {
  175. this.left = x = symbol.attr('x');
  176. this.top = y = symbol.attr('y');
  177. this.width = width = symbol.attr('width');
  178. this.height = height = symbol.attr('height');
  179. this.right = chart.chartWidth - x - width;
  180. this.bottom = chart.chartHeight - y - height;
  181. this.len = this.horiz ? width : height;
  182. this.pos = this.horiz ? x : y;
  183. }
  184. },
  185. /**
  186. * Translate from a value to a color
  187. */
  188. toColor: function (value, point) {
  189. var pos,
  190. stops = this.stops,
  191. from,
  192. to,
  193. color,
  194. dataClasses = this.dataClasses,
  195. dataClass,
  196. i;
  197. if (dataClasses) {
  198. i = dataClasses.length;
  199. while (i--) {
  200. dataClass = dataClasses[i];
  201. from = dataClass.from;
  202. to = dataClass.to;
  203. if ((from === UNDEFINED || value >= from) && (to === UNDEFINED || value <= to)) {
  204. color = dataClass.color;
  205. if (point) {
  206. point.dataClass = i;
  207. }
  208. break;
  209. }
  210. }
  211. } else {
  212. if (this.isLog) {
  213. value = this.val2lin(value);
  214. }
  215. pos = 1 - ((this.max - value) / ((this.max - this.min) || 1));
  216. i = stops.length;
  217. while (i--) {
  218. if (pos > stops[i][0]) {
  219. break;
  220. }
  221. }
  222. from = stops[i] || stops[i + 1];
  223. to = stops[i + 1] || from;
  224. // The position within the gradient
  225. pos = 1 - (to[0] - pos) / ((to[0] - from[0]) || 1);
  226. color = this.tweenColors(
  227. from.color,
  228. to.color,
  229. pos
  230. );
  231. }
  232. return color;
  233. },
  234. /**
  235. * Override the getOffset method to add the whole axis groups inside the legend.
  236. */
  237. getOffset: function () {
  238. var group = this.legendGroup,
  239. sideOffset = this.chart.axisOffset[this.side];
  240. if (group) {
  241. // Hook for the getOffset method to add groups to this parent group
  242. this.axisParent = group;
  243. // Call the base
  244. Axis.prototype.getOffset.call(this);
  245. // First time only
  246. if (!this.added) {
  247. this.added = true;
  248. this.labelLeft = 0;
  249. this.labelRight = this.width;
  250. }
  251. // Reset it to avoid color axis reserving space
  252. this.chart.axisOffset[this.side] = sideOffset;
  253. }
  254. },
  255. /**
  256. * Create the color gradient
  257. */
  258. setLegendColor: function () {
  259. var grad,
  260. horiz = this.horiz,
  261. options = this.options,
  262. reversed = this.reversed,
  263. one = reversed ? 1 : 0,
  264. zero = reversed ? 0 : 1;
  265. grad = horiz ? [one, 0, zero, 0] : [0, zero, 0, one]; // #3190
  266. this.legendColor = {
  267. linearGradient: { x1: grad[0], y1: grad[1], x2: grad[2], y2: grad[3] },
  268. stops: options.stops || [
  269. [0, options.minColor],
  270. [1, options.maxColor]
  271. ]
  272. };
  273. },
  274. /**
  275. * The color axis appears inside the legend and has its own legend symbol
  276. */
  277. drawLegendSymbol: function (legend, item) {
  278. var padding = legend.padding,
  279. legendOptions = legend.options,
  280. horiz = this.horiz,
  281. width = pick(legendOptions.symbolWidth, horiz ? 200 : 12),
  282. height = pick(legendOptions.symbolHeight, horiz ? 12 : 200),
  283. labelPadding = pick(legendOptions.labelPadding, horiz ? 16 : 30),
  284. itemDistance = pick(legendOptions.itemDistance, 10);
  285. this.setLegendColor();
  286. // Create the gradient
  287. item.legendSymbol = this.chart.renderer.rect(
  288. 0,
  289. legend.baseline - 11,
  290. width,
  291. height
  292. ).attr({
  293. zIndex: 1
  294. }).add(item.legendGroup);
  295. // Set how much space this legend item takes up
  296. this.legendItemWidth = width + padding + (horiz ? itemDistance : labelPadding);
  297. this.legendItemHeight = height + padding + (horiz ? labelPadding : 0);
  298. },
  299. /**
  300. * Fool the legend
  301. */
  302. setState: noop,
  303. visible: true,
  304. setVisible: noop,
  305. getSeriesExtremes: function () {
  306. var series;
  307. if (this.series.length) {
  308. series = this.series[0];
  309. this.dataMin = series.valueMin;
  310. this.dataMax = series.valueMax;
  311. }
  312. },
  313. drawCrosshair: function (e, point) {
  314. var plotX = point && point.plotX,
  315. plotY = point && point.plotY,
  316. crossPos,
  317. axisPos = this.pos,
  318. axisLen = this.len;
  319. if (point) {
  320. crossPos = this.toPixels(point[point.series.colorKey]);
  321. if (crossPos < axisPos) {
  322. crossPos = axisPos - 2;
  323. } else if (crossPos > axisPos + axisLen) {
  324. crossPos = axisPos + axisLen + 2;
  325. }
  326. point.plotX = crossPos;
  327. point.plotY = this.len - crossPos;
  328. Axis.prototype.drawCrosshair.call(this, e, point);
  329. point.plotX = plotX;
  330. point.plotY = plotY;
  331. if (this.cross) {
  332. this.cross
  333. .attr({
  334. fill: this.crosshair.color
  335. })
  336. .add(this.legendGroup);
  337. }
  338. }
  339. },
  340. getPlotLinePath: function (a, b, c, d, pos) {
  341. return isNumber(pos) ? // crosshairs only // #3969 pos can be 0 !!
  342. (this.horiz ?
  343. ['M', pos - 4, this.top - 6, 'L', pos + 4, this.top - 6, pos, this.top, 'Z'] :
  344. ['M', this.left, pos, 'L', this.left - 6, pos + 6, this.left - 6, pos - 6, 'Z']
  345. ) :
  346. Axis.prototype.getPlotLinePath.call(this, a, b, c, d);
  347. },
  348. update: function (newOptions, redraw) {
  349. var chart = this.chart,
  350. legend = chart.legend;
  351. each(this.series, function (series) {
  352. series.isDirtyData = true; // Needed for Axis.update when choropleth colors change
  353. });
  354. // When updating data classes, destroy old items and make sure new ones are created (#3207)
  355. if (newOptions.dataClasses && legend.allItems) {
  356. each(legend.allItems, function (item) {
  357. if (item.isDataClass) {
  358. item.legendGroup.destroy();
  359. }
  360. });
  361. chart.isDirtyLegend = true;
  362. }
  363. // Keep the options structure updated for export. Unlike xAxis and yAxis, the colorAxis is
  364. // not an array. (#3207)
  365. chart.options[this.coll] = merge(this.userOptions, newOptions);
  366. Axis.prototype.update.call(this, newOptions, redraw);
  367. if (this.legendItem) {
  368. this.setLegendColor();
  369. legend.colorizeItem(this, true);
  370. }
  371. },
  372. /**
  373. * Get the legend item symbols for data classes
  374. */
  375. getDataClassLegendSymbols: function () {
  376. var axis = this,
  377. chart = this.chart,
  378. legendItems = this.legendItems,
  379. legendOptions = chart.options.legend,
  380. valueDecimals = legendOptions.valueDecimals,
  381. valueSuffix = legendOptions.valueSuffix || '',
  382. name;
  383. if (!legendItems.length) {
  384. each(this.dataClasses, function (dataClass, i) {
  385. var vis = true,
  386. from = dataClass.from,
  387. to = dataClass.to;
  388. // Assemble the default name. This can be overridden by legend.options.labelFormatter
  389. name = '';
  390. if (from === UNDEFINED) {
  391. name = '< ';
  392. } else if (to === UNDEFINED) {
  393. name = '> ';
  394. }
  395. if (from !== UNDEFINED) {
  396. name += Highcharts.numberFormat(from, valueDecimals) + valueSuffix;
  397. }
  398. if (from !== UNDEFINED && to !== UNDEFINED) {
  399. name += ' - ';
  400. }
  401. if (to !== UNDEFINED) {
  402. name += Highcharts.numberFormat(to, valueDecimals) + valueSuffix;
  403. }
  404. // Add a mock object to the legend items
  405. legendItems.push(extend({
  406. chart: chart,
  407. name: name,
  408. options: {},
  409. drawLegendSymbol: LegendSymbolMixin.drawRectangle,
  410. visible: true,
  411. setState: noop,
  412. isDataClass: true,
  413. setVisible: function () {
  414. vis = this.visible = !vis;
  415. each(axis.series, function (series) {
  416. each(series.points, function (point) {
  417. if (point.dataClass === i) {
  418. point.setVisible(vis);
  419. }
  420. });
  421. });
  422. chart.legend.colorizeItem(this, vis);
  423. }
  424. }, dataClass));
  425. });
  426. }
  427. return legendItems;
  428. },
  429. name: '' // Prevents 'undefined' in legend in IE8
  430. });
  431. /**
  432. * Handle animation of the color attributes directly
  433. */
  434. each(['fill', 'stroke'], function (prop) {
  435. Highcharts.Fx.prototype[prop + 'Setter'] = function () {
  436. this.elem.attr(prop, ColorAxis.prototype.tweenColors(Color(this.start), Color(this.end), this.pos));
  437. };
  438. });
  439. /**
  440. * Extend the chart getAxes method to also get the color axis
  441. */
  442. wrap(Chart.prototype, 'getAxes', function (proceed) {
  443. var options = this.options,
  444. colorAxisOptions = options.colorAxis;
  445. proceed.call(this);
  446. this.colorAxis = [];
  447. if (colorAxisOptions) {
  448. new ColorAxis(this, colorAxisOptions); // eslint-disable-line no-new
  449. }
  450. });
  451. /**
  452. * Wrap the legend getAllItems method to add the color axis. This also removes the
  453. * axis' own series to prevent them from showing up individually.
  454. */
  455. wrap(Legend.prototype, 'getAllItems', function (proceed) {
  456. var allItems = [],
  457. colorAxis = this.chart.colorAxis[0];
  458. if (colorAxis) {
  459. // Data classes
  460. if (colorAxis.options.dataClasses) {
  461. allItems = allItems.concat(colorAxis.getDataClassLegendSymbols());
  462. // Gradient legend
  463. } else {
  464. // Add this axis on top
  465. allItems.push(colorAxis);
  466. }
  467. // Don't add the color axis' series
  468. each(colorAxis.series, function (series) {
  469. series.options.showInLegend = false;
  470. });
  471. }
  472. return allItems.concat(proceed.call(this));
  473. });
  474. /**
  475. * Mixin for maps and heatmaps
  476. */
  477. var colorPointMixin = {
  478. /**
  479. * Set the visibility of a single point
  480. */
  481. setVisible: function (vis) {
  482. var point = this,
  483. method = vis ? 'show' : 'hide';
  484. // Show and hide associated elements
  485. each(['graphic', 'dataLabel'], function (key) {
  486. if (point[key]) {
  487. point[key][method]();
  488. }
  489. });
  490. }
  491. };
  492. var colorSeriesMixin = {
  493. pointAttrToOptions: { // mapping between SVG attributes and the corresponding options
  494. stroke: 'borderColor',
  495. 'stroke-width': 'borderWidth',
  496. fill: 'color',
  497. dashstyle: 'dashStyle'
  498. },
  499. pointArrayMap: ['value'],
  500. axisTypes: ['xAxis', 'yAxis', 'colorAxis'],
  501. optionalAxis: 'colorAxis',
  502. trackerGroups: ['group', 'markerGroup', 'dataLabelsGroup'],
  503. getSymbol: noop,
  504. parallelArrays: ['x', 'y', 'value'],
  505. colorKey: 'value',
  506. /**
  507. * In choropleth maps, the color is a result of the value, so this needs translation too
  508. */
  509. translateColors: function () {
  510. var series = this,
  511. nullColor = this.options.nullColor,
  512. colorAxis = this.colorAxis,
  513. colorKey = this.colorKey;
  514. each(this.data, function (point) {
  515. var value = point[colorKey],
  516. color;
  517. color = point.options.color ||
  518. (value === null ? nullColor : (colorAxis && value !== undefined) ? colorAxis.toColor(value, point) : point.color || series.color);
  519. if (color) {
  520. point.color = color;
  521. }
  522. });
  523. }
  524. };
  525. /**
  526. * Extend the default options with map options
  527. */
  528. defaultOptions.plotOptions.heatmap = merge(defaultOptions.plotOptions.scatter, {
  529. animation: false,
  530. borderWidth: 0,
  531. nullColor: '#F8F8F8',
  532. dataLabels: {
  533. formatter: function () { // #2945
  534. return this.point.value;
  535. },
  536. inside: true,
  537. verticalAlign: 'middle',
  538. crop: false,
  539. overflow: false,
  540. padding: 0 // #3837
  541. },
  542. marker: null,
  543. pointRange: null, // dynamically set to colsize by default
  544. tooltip: {
  545. pointFormat: '{point.x}, {point.y}: {point.value}<br/>'
  546. },
  547. states: {
  548. normal: {
  549. animation: true
  550. },
  551. hover: {
  552. halo: false, // #3406, halo is not required on heatmaps
  553. brightness: 0.2
  554. }
  555. }
  556. });
  557. // The Heatmap series type
  558. seriesTypes.heatmap = extendClass(seriesTypes.scatter, merge(colorSeriesMixin, {
  559. type: 'heatmap',
  560. pointArrayMap: ['y', 'value'],
  561. hasPointSpecificOptions: true,
  562. pointClass: extendClass(Point, colorPointMixin),
  563. supportsDrilldown: true,
  564. getExtremesFromAll: true,
  565. directTouch: true,
  566. /**
  567. * Override the init method to add point ranges on both axes.
  568. */
  569. init: function () {
  570. var options;
  571. seriesTypes.scatter.prototype.init.apply(this, arguments);
  572. options = this.options;
  573. options.pointRange = pick(options.pointRange, options.colsize || 1); // #3758, prevent resetting in setData
  574. this.yAxis.axisPointRange = options.rowsize || 1; // general point range
  575. },
  576. translate: function () {
  577. var series = this,
  578. options = series.options,
  579. xAxis = series.xAxis,
  580. yAxis = series.yAxis,
  581. between = function (x, a, b) {
  582. return Math.min(Math.max(a, x), b);
  583. };
  584. series.generatePoints();
  585. each(series.points, function (point) {
  586. var xPad = (options.colsize || 1) / 2,
  587. yPad = (options.rowsize || 1) / 2,
  588. x1 = between(Math.round(xAxis.len - xAxis.translate(point.x - xPad, 0, 1, 0, 1)), -xAxis.len, 2 * xAxis.len),
  589. x2 = between(Math.round(xAxis.len - xAxis.translate(point.x + xPad, 0, 1, 0, 1)), -xAxis.len, 2 * xAxis.len),
  590. y1 = between(Math.round(yAxis.translate(point.y - yPad, 0, 1, 0, 1)), -yAxis.len, 2 * yAxis.len),
  591. y2 = between(Math.round(yAxis.translate(point.y + yPad, 0, 1, 0, 1)), -yAxis.len, 2 * yAxis.len);
  592. // Set plotX and plotY for use in K-D-Tree and more
  593. point.plotX = point.clientX = (x1 + x2) / 2;
  594. point.plotY = (y1 + y2) / 2;
  595. point.shapeType = 'rect';
  596. point.shapeArgs = {
  597. x: Math.min(x1, x2),
  598. y: Math.min(y1, y2),
  599. width: Math.abs(x2 - x1),
  600. height: Math.abs(y2 - y1)
  601. };
  602. });
  603. series.translateColors();
  604. // Make sure colors are updated on colorAxis update (#2893)
  605. if (this.chart.hasRendered) {
  606. each(series.points, function (point) {
  607. point.shapeArgs.fill = point.options.color || point.color; // #3311
  608. });
  609. }
  610. },
  611. drawPoints: seriesTypes.column.prototype.drawPoints,
  612. animate: noop,
  613. getBox: noop,
  614. drawLegendSymbol: LegendSymbolMixin.drawRectangle,
  615. alignDataLabel: seriesTypes.column.prototype.alignDataLabel,
  616. getExtremes: function () {
  617. // Get the extremes from the value data
  618. Series.prototype.getExtremes.call(this, this.valueData);
  619. this.valueMin = this.dataMin;
  620. this.valueMax = this.dataMax;
  621. // Get the extremes from the y data
  622. Series.prototype.getExtremes.call(this);
  623. }
  624. }));
  625. }));