Highcharts Plugin Recently Broke - "this.init is not a function" - highcharts

I have a js file that create a Highcharts chart object. I added a plugin to change label contrast. The plugin is below:
/**
* Override getContrast function; make threshold for showing white text very high
*/
(function(H) {
H.Renderer.prototype.getContrast = function(rgba) {
rgba = H.Color(rgba).rgba;
return rgba[0] + rgba[1] + rgba[2] > 210 ? '#000000' : '#FFFFFF';
};
}(Highcharts));
It was working just fine for several months. I recently noticed that the chart is no longer rendering, and my error console is showing this: Uncaught TypeError: this.init is not a function
at Object.e [as Color] (highcharts.src.js:2839)
I was able to trace the source back to this part of the Highcharts.src.js file:
/**
* Handle color operations. Some object methods are chainable.
*
* #param {Highcharts.ColorType} input
* The input color in either rbga or hex format
*/
function Color(input) {
// Collection of parsers. This can be extended from the outside by pushing
// parsers to Highcharts.Color.prototype.parsers.
this.parsers = [{
// RGBA color
// eslint-disable-next-line max-len
regex: /rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]?(?:\.[0-9]+)?)\s*\)/,
parse: function (result) {
return [
pInt(result[1]),
pInt(result[2]),
pInt(result[3]),
parseFloat(result[4], 10)
];
}
}, {
// RGB color
regex: /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/,
parse: function (result) {
return [pInt(result[1]), pInt(result[2]), pInt(result[3]), 1];
}
}];
this.rgba = [];
this.init(input); //<!--ERROR RIGHT HERE
}
It looks like something change internally on Highcharts' end, specifically the init() method for the Color subclass. Has anyone else had a similar error?

Since v8.0.1, Highcharts.Color must be instanciated with the new keyword.
Before
stops: [
[0, Highcharts.getOptions().colors[0]],
[1, Highcharts.Color(Highcharts.getOptions().colors[0]).setOpacity(0).get('rgba')]
]
Since v8.0.1
stops: [
[0, Highcharts.getOptions().colors[0]],
[1, new Highcharts.Color(Highcharts.getOptions().colors[0]).setOpacity(0).get('rgba')]
]
https://github.com/highcharts/highcharts/issues/13053

Related

Highcharts Heatmap background color when invalid data

When I render an invalid data like a string on Highcharts Heatmap, it shows the string with a black background. Below is an example of an invalid data:
data: [[0, 0, "A"], [1, 0, "B"], [2, 0, "C"], [3, 0, "D"], [4, 0, "E"]]
Can I change this default behavior and show another background color instead of black?
When you use a unsupported value the ColorAxis.toColor function returns rgb(NaN,NaN,NaN) as the color of the cell.
By writing a rather small wrapper for that function you could intercept string values and return a color of your choice.
For example (JSFiddle demo):
(function (H) {
H.wrap(H.ColorAxis.prototype, 'toColor', function (proceed, value, point) {
if(typeof value === 'string') // String value -> Return pink
return 'rgb(255,105,180)';
else // Normal value -> Proceed as usual
return proceed.apply(this, Array.prototype.slice.call(arguments, 1));
});
}(Highcharts));

Separate panes / or style maximum gridlines in Highstock

I built a widget with multiple y Axes very similar to the official sample here: http://www.highcharts.com/stock/demo/candlestick-and-volume
I'm trying to have some sort of visual separation between the chart panes, either by
applying a special style to the maximum grid line for each pane, or
adding a horizontal line in the whitespace between the panes
The only approach i got working is using PlotLines, but I'd rather have a separator that's independent of zoom levels. Any ideas on how to achieve this?
Use Renderer to draw a path between y axes.
function drawSeparator() {
let separator = this.separator;
const options = this.options.chart.separator;
if (options && options.enabled) {
if (!separator) {
this.separator = separator = this.renderer.path({
d: 'M 0 0',
'stroke-width': options.width === undefined ? 1 : options.width,
stroke: options.color || 'black'
}).add();
}
const topAxisBottom = this.yAxis[0].top + this.yAxis[0].height;
const bottomAxisTop = this.yAxis[1].top;
const y = topAxisBottom + (bottomAxisTop - topAxisBottom) / 2;
separator.attr({
d: `M 0 ${y} L ${this.chartWidth} ${y}`
});
}
}
Call the method on load/redraw event
chart: {
events: {
load: drawSeparator,
redraw: drawSeparator
},
separator: {
enabled: true,
width: 3,
color: 'blue'
}
},
You can modify the path's d attribute, the path starts from axis.left and stops on axis.left + axis.width
Live example and output
http://jsfiddle.net/L11uqxgq/

Highcharts bar chart with varied bar widths?

I want to make stacked bar chart where each portion has a width that encodes one value (say "Change" in the data below) and a height that encodes another value ("Share")
In some ways this is like a histogram with different bin sizes. There are a few "histogram" questions but none seem to address this. Plot Histograms in Highcharts
So given data like this:
Category Share Price Change
Apples 14.35 0.1314192423
Horseradish 46.168 0.1761474117
Figs 2.871 0.018874249
Tomatoes 13.954 0.0106121298
Mangoes 7.264 0.1217297011
Raisins 5.738 0.0206787136
Eggplant 6.31 0.0110160732
Other produce 3.344 0.0945377722
I can make a stacked bar that captures the "share" column in widths:
And another that captures the "change" column in heights:
And I can use an image editor to combine those into this histogram-like beast:
Which really captures that horseradish is a huge deal. So my question is, can I do that within Highcharts?
You can realise that by using snippet.
(function (H) {
var seriesTypes = H.seriesTypes,
each = H.each,
extendClass = H.extendClass,
defaultPlotOptions = H.getOptions().plotOptions,
merge = H.merge;
defaultPlotOptions.marimekko = merge(defaultPlotOptions.column, {
pointPadding: 0,
groupPadding: 0
});
seriesTypes.marimekko = extendClass(seriesTypes.column, {
type: 'marimekko',
pointArrayMap: ['y', 'z'],
parallelArrays: ['x', 'y', 'z'],
processData: function () {
var series = this;
this.totalZ = 0;
this.relZ = [];
seriesTypes.column.prototype.processData.call(this);
each(this.zData, function (z, i) {
series.relZ[i] = series.totalZ;
series.totalZ += z;
});
},
translate: function () {
var series = this,
totalZ = series.totalZ,
xAxis = series.xAxis;
seriesTypes.column.prototype.translate.call(this);
// Distort the points to reflect z dimension
each(this.points, function (point, i) {
var shapeArgs = point.shapeArgs,
relZ = series.relZ[i];
shapeArgs.x *= (relZ / totalZ) / (shapeArgs.x / xAxis.len);
shapeArgs.width *= (point.z / totalZ) / (series.pointRange / series.xAxis.max);
});
}
});
}(Highcharts));
Example: http://jsfiddle.net/highcharts/75oucp3b/

Highcharts line with null values

I'm trying to create a line diagram (datetime x-axis) with null values.
var rawData = [{
(...)
}, {
"PointOfTime": 1424991600,
"value": 6831.28806
}, {
"PointOfTime": 1425078000,
"value": null
}, {
"PointOfTime": 1425164400,
"value": null
}, {
(...)
}];
Adjust the data from a json source to an array:
rawData.forEach(function (d) {
var datetime = (d.PointOfTime + 3600) * 1000;
data.push([datetime, parseFloat(d.value)]);
});
As stated in the following fiddle, http://jsfiddle.net/wiesson/1m5hpLef there are no lines, only bullets. Any suggestions? I need the PointOfTime to create the range of the x-axis, even they are empty.
// Edit: As displayed in the following figure, the values in the future are unknown and not 0, therefore I would like to set them to null.
Add a condition, which check if your value is null. If yes then push this, instead of call parseFloat(null).
rawData.forEach(function (d) {
var datetime = d.PointOfTime * 1000;
if(d.value!==null)
data.push([datetime, parseFloat(d.value)]);
else
data.push([datetime, null]);
});
Example: http://jsfiddle.net/wiesson/1m5hpLef/

Highcharts - apply gradient on custom colors

This is my data model:
data = [{y: 123, color: "#FF7600"}, {y: 321, color: "#00FFE3"}, {y: 213,color: "#444444"}]
Then the series is added to a pie chart:
$http({ method: 'GET', url: /pie-chart, params: {})
.success(function (data) {
chart.addSeries({
type: 'pie',
data: data
})
});
Here's the official highcharts demo: http://www.highcharts.com/demo/pie-gradient
It loops through data, read colors, creates color array and uses this array when drawing chart.
But i'm thinking about solution which avoids extracting colors from JSON.
Any idea? Thanks a lot.
Edited, solved
Gave it up :).
I ended up creating color arrays as described in highcharts demo.
It works well.
// Get colors from received data, create color array,
var colors = [];
for (var i = 0; i < data[0].series.length; i++) {
colors.push(data[0].series[i].color);
// Delete original colors, so that new radialized are used
delete(data[i].color);
}
// Use color array and radialize each color
Highcharts.getOptions().colors = Highcharts.map(colors, function(color) {
return {
linearGradient: { x1: 0, y1: 0, x2: 1, y2: 0 },
stops: [
[0, color],
[1, Highcharts.Color(color).brighten(-0.3).get('rgb')] // darken
]
};
});
The solution mentioned above sets the colors in the global defaults. This is okay if you have only one chart, but if you have multiple it can be problematic, as the colors will apply for all charts.
You can colorize this on the individual chart level by remapping the colors just in the local data array. Here is what I do for my pie charts.
chartData is an array of data like:
[
{
"color": "#01080f",
"name": "No Status",
"y": 8570
},
{
"color": "#1A942C",
"name": "Deployed",
"y": 27952
},
...
{
"color": "#f36e20",
"name": "Out of sync",
"y": 241
}
]
In my javascript code it is retrieved from the server and applied to the Highcharts object's series.data element.
Just manipulate that data element before you add it to the highcharts object.
// Retrieve your chart data
$.getJSON('/api/endpoint/policystatus', function (chartData) {
// Function replaces flat colors with gradients
function colorizeData(data) {
data.color = {
radialGradient: {cx: 0.5, cy: 0.3, r: 0.7},
stops: [
[0, data.color],
[1, Highcharts.Color(data.color).brighten(-0.3).get('rgb')] // darken
]
};
}
// Call the function for each element in the retrieved data
chartData.forEach(colorizeData);
// Continue on to build your chart
$('#pie-general-status').highcharts({
// ....
The above 'colorizeData' takes the chart data input, looks for the 'color' element, then replaces it with the Highcharts gradient based on the same color.
Note that you must use hex or RGB values; it will not work with colors defined as the words 'green' or 'blue'.

Resources