I'm using scala.js (v0.6.13) with the Highcharts facade, and I have run into a roadblock trying to access some variables that I would normally access in javascript using 'this' and 'chart'. Here's an example:
coffeescript:
tooltip: {
enabled: true,
positioner: (labelWidth, labelHeight, point) ->
return { x: chart.plotWidth - labelWidth + chart.plotLeft, y: 17 }
formatter: () ->
x = this.x
point = this.points.find (p) -> x == p.x
...
My question is how do I access "this.x" and "chart.plotWidth" in my formatter and positioner functions in scala.js? Here's my scala code thus far:
override val tooltip: Cfg[Tooltip] = Tooltip(
formatter = { () =>
"what?"
}: js.Function0[String],
positioner = { (labelWidth: Any, labelHeight: Any, point: Object) =>
js.Dynamic.literal(
x = labelWidth,
y = 17)
}: js.Function3[Any, Any, Object, Object]
)
edit: chart pertains to a highchart chart.
You need to use a js.ThisFunctionN to explicitly capture the special this of JavaScript as a normal parameter in Scala.js.
positioner = { (thiz: js.Dynamic, labelWidth: Any, labelHeight: Any, point: Object) =>
// here the variable `thiz` holds what would be `this` in JS
...
}: js.ThisFunction3[js.Dynamic, Any, Any, Object, Object]
When converting a Scala anonymous function to a js.ThisFunction, the this argument is passed in as the first parameter.
See https://www.scala-js.org/doc/interoperability/types.html for more details.
For chart, your question does not give enough context to know what chart is in your CoffeeScript code. But I guess just using chart in Scala.js will do whatever the original code does.
Related
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/
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/
I'm looking to revert the element to it's original position if it hasn't been dragged by more than a certain distance.
If the element has been dragged by more than the set distance on axis X I want to execute a function that will slide the dragged element out of the viewport and slide it back in. I got it to track the distance but now it somehow won't return function as true or false if the condition is not met... I get an error with D undefined. Does anyone know what could be wrong?
I have tried different solutions, but couldn't get neither one of them to work and wasn't able to find an answer on the web.
This what I have done so far: http://jsfiddle.net/DzEu2/4/
$(".video").draggable({
revert: $("#video").mousedown(function (e) {
$(this).data('p0', {
x: e.pageX,
y: e.pageY
});
}).mouseup(function (e) {
p0 = $(this).data('p0'),
p1 = {
x: e.pageX,
y: e.pageY
},
d = Math.sqrt(Math.pow(p1.x - p0.x, 2) + Math.pow(p1.y - p0.y, 2));
});
if (d > 100) {
return false;
} else {
return true;
}
start: function (event) {
x = event.originalEvent.pageX;
y = event.originalEvent.pageY;
console.log(x, y);
},
drag: function (event) {
if (x && y) {
axis = Math.abs(event.originalEvent.pageX - x) > Math.abs(event.originalEvent.pageY - y) ? 'x' : 'y';
$(".video").draggable('option', 'axis', axis);
x = y = null;
}
},
stop: function () {
x = y = null;
$(".video").draggable('option', 'axis', false);
},
distance: 20,
});
You are massively overdoing it. Measuring distance travelled on a single axis is rather trivial:
revert: function() {
var orig = $(this).data("uiDraggable").originalPosition.left;
var curr = $(this).data("uiDraggable").position.left;
return curr - orig > 100;
}
See a working update on your fiddle
Note that the name of the data object is different for different versions of jQueryUI. For the one you selected in the fiddle, it's draggable, for newer ones it's uiDraggable.
There are a number of other errors in your code, which I commented out in the fiddle:
You haven't actually surrounded the code you meant to put in revert in a function(){}
Event binders and asynchronous callbacks don't work like what you assume in mousedown and mouseup. The d variable in your d>100 condition will always be undefined
It's generally bad form to use a selector (".video") inside an event handler, when you can get the same object with this or event.currentTarget.
Don't end the last property in an object with a comma. Internet explorer randomly decides to hate that.
Summary? Code reviewing is your friend, don't forget about him! :)
I am using FLOT to display graphs.
To display tootip i am using https://github.com/krzysu/flot.tooltip.
Now I want to customize content of tooltip so I am using callback to set content of tooltip.
Code snippet:
tooltip: true,
tooltipOpts: {
content: function(label, xval, yval, flotItem){
var xAxis = plot.getXAxes();
return xval;
},
defaultTheme: false
}
But its giving me error
caught TypeError: Object function (label, xval, yval, flotItem){
var xAxis = plot.getXAxes();
return xval;
} has no method 'replace'
Could any one help me ?
Thanks in advance.
The documentation states that:
If you require even more control over how the tooltip is generated you can pass a callback function(label, xval, yval, flotItem) that must return a string with the format described.
xval is the numeric x axis value where the tooltip is located. It is not a string. The replace method it is failing on is the standard string.replace:
> "".replace
function replace() { [native code] }
I'm not using the flot tooltip. There is easier way to show tooltip and customize it. Check out this fiddler:
http://jsfiddle.net/Margo/yKG7X/5/
$("#placeholder").bind("plothover", function (event, pos, item) {
if (item) {
$("#tooltip").remove();
var x = item.datapoint[0],
y = item.datapoint[1];
showTooltip(item.pageX, item.pageY, x + " / " + y );
}
});
function showTooltip(x, y, contents) {
$('<div id="tooltip">' + contents + '</div>').css({
position: 'absolute',
display: 'none',
top: y + 5,
left: x + 5,
border: '1px solid #fdd',
padding: '2px',
'background-color': '#fee',
opacity: 0.80
}).appendTo("body").fadeIn(200).fadeOut(6000);
}
Hope it helps :)
Highcharts does a great job auto-formatting dates both on the x-axis and in tooltips. Unfortunately I need a custom function to format my y-values, and /plotOptions/line/tooltip/pointFormat accepts only a format string and not a function, so I have to set /tooltip/formatter instead. How do I get hold of the formatted date (e.g. Oct'13 or 20. Oct) as shown on the x axis? I don't seem to have access to point.key from there, only the raw millis value.
http://jsfiddle.net/9Fke4/
You can use dateFormat()
tooltip: {
formatter: function() {
return '<b>' + Highcharts.dateFormat('%b\' %d',this.x) + ':</b> ' + this.y;
}
},
http://jsfiddle.net/9Fke4/1/
FWIW, this was answered in a Highcharts issue on Github
formatter: function() {
var tooltip = this.series.chart.tooltip,
item = this.point.getLabelConfig(),
header = this.series.chart.tooltip.tooltipFooterHeaderFormatter(item);
return header + this.y;
}
Corresponding fiddle:
http://jsfiddle.net/9Fke4/12/
If you are using a shared series:
tooltip: {
formatter: function (tooltip) {
var header,
s = [];
$.each(this.points, function(i, point) {
if(header == null) {
var config = point.point.getLabelConfig();
header = tooltip.tooltipFooterHeaderFormatter(config);
}
s.push(point.y);
});
return header + s.reverse().join('<br>');
},
shared: true
}
conver the raw milliseconds to a date object using
var currentDate = new Date (tome in milliseconds)
in the tootip/formatter function you have defined.
this will give you good control over the date. you can use currentDate.getMonth(), getDate(), getDay(), etc methods to get the information you want from that date.
build a string with the above info and return.
I hope this will help you.
The solution I ended up with is to pre-format the y-values and store them as part of the series data; the pre-formatted values can then be referenced from the tooltip headerFormat and pointFormat, where they can be used along with {point.key}, which contains the auto-formatted date (and which is not available when providing a custom tooltip formatter):
http://jsfiddle.net/uG3sv/