I combined two charts together to export both charts.
<script defer="true"><![CDATA[
(function(){
var exportChart = function() {
Highcharts.getSVG = function(charts) {
var svgArr = [], top = 0, width = 0;
jq.each(charts, function(i, chart) {
var svg = chart.getSVG();
svg = svg.replace('<svg', '<g transform="translate(0,' + top
+ ')" ');
svg = svg.replace('</svg>', '</g>');
top += chart.chartHeight;
width = Math.max(width, chart.chartWidth);
svgArr.push(svg);
});
return '<svg height="' + top + '" width="' + width
+ '" version="1.1" xmlns="http://www.w3.org/2000/svg">'
+ svgArr.join('') + '</svg>';
};
Highcharts.exportCharts = function() {
var charts = [];
jq(".test-timeline-chart .highcharts-container")
.each(
function(i, e) {
charts
.push(Highcharts.charts[jq(e).parent()
.data().highchartsChart]);
});
var form;
svg = Highcharts.getSVG(charts);
// merge the options
var options = Highcharts.merge(Highcharts.getOptions().exporting, {});
// create the form
form = Highcharts.createElement('form', {
method : 'post',
action : options.url
}, {
display : 'none'
}, document.body);
// add the values
Highcharts.each([ 'filename', 'type', 'width', 'svg' ], function(name) {
Highcharts.createElement('input', {
type : 'hidden',
name : name,
value : {
filename : options.filename || 'chart',
type : options.type,
width : options.width,
svg : svg
}[name]
}, null, form);
});
// submit
form.submit();
// clean up
form.parentNode.removeChild(form);
};
// printing all the charts in single page.
function printAll() {
var origDisplay = [], origParent = [], body = document.body, childNodes = body.childNodes;
// hide all body content
Highcharts.each(childNodes, function(node, i) {
if (node.nodeType === 1) {
origDisplay[i] = node.style.display;
node.style.display = "none";
}
});
var charts = [];
jq(".test-timeline-chart .highcharts-container")
.each(
function(i, e) {
charts
.push(Highcharts.charts[jq(e).parent()
.data().highchartsChart]);
});
// put the charts back in
jq.each(charts, function(i, chart) {
origParent[i] = chart.container.parentNode;
body.appendChild(chart.container);
});
// print
window.print();
// allow the browser to prepare before reverting
setTimeout(function() {
// put the chart back in
jq.each(charts, function(i, chart) {
origParent[i].appendChild(chart.container);
});
// restore all body content
Highcharts.each(childNodes, function(node, i) {
if (node.nodeType === 1) {
node.style.display = origDisplay[i];
}
});
}, 500);
}
if(jq(".test-timeline-chart").length > 0) {
var index = jq(".test-timeline-chart .highcharts-container:first").parent().data().highchartsChart;
var instanceChart = Highcharts.charts[index+1];
instanceChart.exportChart = Highcharts.exportCharts;
instanceChart.options.exporting.buttons.contextButton.menuItems[0].onclick = function() {
printAll();
};
};
};
exportChart();
})();
]]>
</script>
The var options = Highcharts.merge(Highcharts.getOptions().exporting, {}); always returing the PNG image and the export chart option always download PNG images. How I can fix this?
I found a way to listen to the onclick event of high chart context menu items. Is there a way to get selected items?
Related
I would like to have the gauge chart update live dweet data, which is success.
The problem is that every time a new data is pushed to the array humidityData, a new pointer is added in the gauge chart as shown here:
guage chart Though I'd like to have one live-updating-pointer instead.
Could this be done by pop() the prev data?
<script language="JavaScript">
//Array to store sensor data
var humidityData = []
<!--START-->
$(document).ready(function() {
//My Dweet thing's name
var name = 'dweetThingName'
//Humidity chart
var setupSecondChart = function() {
var chart2 = {
type: 'gauge'
};
var title = {...};
var pane = {...};
var yAxis = {...};
var tooltip = {
formatter: function () {
return '<b>' + "Humidity: " + Highcharts.numberFormat(this.y, 2) + "%";
}
};
//Series_Humidity
humiditySeries = [{
name: 'Humidity %',
data: humidityData,
tooltip: {
valueSuffix: '%'
}
}]
//Json_Humidity
var humJson = {};
humJson.chart = chart2;
humJson.title = title;
humJson.tooltip = tooltip;
humJson.xAxis = xAxis;
humJson.yAxis = yAxis;
humJson.legend = legend;
humJson.exporting = exporting;
humJson.series = humiditySeries;
humJson.plotOptions = plotOptions;
console.log("Sereies: : " +humJson)
//Container_Humidity
$('#containerHumidity').highcharts(humJson);
}
var humiditySeries = [] ....
dweetio.get_all_dweets_for(name, function(err, dweets){
for(theDweet in dweets.reverse())
{
var dweet = dweets[theDweet];
//Dweet's variables' names
val2 = dweet.content["Humidity"]
//Add the vals into created arrayDatas
humidityData.push(val2)
console.log("HumidityData: " + humidityData)
}
//Call other charts
setupSecondChart()
});
When you initialize/update your chart make sure that data array contains only one element. The dial is created for every point in this array (to visualize it on the plot).
I am trying to get multiple images from an ajax source and load the on the page when they have all finished loading. The issue I was having that has caused me to try to find a solution was that some of the images weren't loading even though those images existed on the server.
I have tried to add code that now adds the image to an array
design_images.push({cid:designImg});
... and then when all the images have loaded will add that to the page, but I can't get that to work.
var counter = 0;
$(design_images).load(function() { // many or just one image(w) inside body or any other container
counter += 1;
}).each(function(key, value) {
this.complete && $(this).load();
console.log(value);
});
Nothing is outputted from the .each
This is the output of the array design_images
The value of design_images.length is 0 though.
Here is the complete function:
function matte_design_change_design_type(element)
{
var element_value = null;
var mattes_selected_type = get_mattes_selected_type();
matte_design_widths[mattes_selected_type] = [];
var mattes_selected_design = get_mattes_selected_design();
var count_matte_designs = 0;
var found = false;
$(document).ready(function()
{
$.ajax(
{
type: "GET",
url: SITE_URL + "/system/components/xml/" + mattes_selected_type,
dataType: 'xml',
success: function(xml)
{
var output = [];
var design_images = [];
$('component', xml).each(function(i, el)
{
matte_design_widths[mattes_selected_type][i] = 0;
count_matte_designs++;
var thumb = $("thumb", this).text(),
cid = $("cid", this).first().text(),
name = $("name", this).first().text().replace("Collage - ", ""),
alt = name,
description = $("description", this).first().text(),
if (parseInt(cid, 10) === mattes_selected_design)
{
found = true;
$("#matte_design_name").html(name);
$("#matte_design_description").html(description);
}
var designImg = new Image();
designImg.id = 'cid_' + cid;
designImg.alt = alt;
designImg.onclick = function() {
matte_design_change(cid, mattes_selected_type);
};
designImg.onload = function() {
output.push('<span class="matte_design_image_name" id="design_' + cid + '"><img id="cid_' + cid + '" />');
output.push('<br /><span class="matte_design_name" id="matte_design_name_' + mattes_selected_type + '_' + i + '">' + name + '</span></span>');
matte_design_increase_width(mattes_selected_type, this.width, i);
$('#matte_designs_strip_wrapper').html(output.join(''));
};
designImg.src = 'https://example.com/system/components/compimg/' + thumb + '/flashthumb';
});
var counter = 0;
var size = $('img').length;
$(design_images).load(function() {
counter += 1;
}).each(function(key, value) {
this.complete && $(this).load();
console.log(value);
});
}
});
});
}
I have tried waitForImages and imagesLoaded but I couldn't get them to work for me, but I'm not opposed to using either one.
Hide all images by default using CSS
img{
display: none;
}
Use Jquery to check if all loaded, then display images
JQuery
$(window).load(function(){
$('img').fadeIn(800); //or $('img').show('slow');
});
I wrote my code down to a small fiddle: http://jsfiddle.net/V8dyd/266/
I am able to render region labels as strings on the map but unable to render them as HTML.
$(function(){
var Jsondata = {
LK: " Region : Asia, Feedback : 228, Good : 34.00%, Normal : 33.00%, Bad : 30.00% ",
IN: "Total Responses : 228"
};
var map = $('#map').vectorMap({
map: 'world_mill_en',
zoomMin: 1,
zoomMax: 1,
regionLabelStyle: {
initial: {
fill: '#B90E32'
},
hover: {
fill: 'black'
}
},
labels: {
regions: {
render: function (code) {
if (code==="LK") {
var a = Jsondata[code];
var array = a.split(',');
var s = "<html><body><div><small>" + array[0] +"</small><br><small>" + array[1] + "</small><br><small>" + array[2] + "</small><br><small>" + array[3] + "</small></div</body></html>";
var htmlObject = document.createElement('div');
htmlObject.innerHTML = s;
return htmlObject;
// return s
}
}
}
}
});
});
The labels are rendered using svg TEXT-Tags and do not allow HTML.
In the current version this is not supported but i used a Hook in the Region Function for realize line breaks:
https://github.com/bjornd/jvectormap/blob/master/src/region.js
Like this:
jvm.Region = function(config){
var bbox,
text,
offsets,
labelDx,
labelDy;
this.config = config;
this.map = this.config.map;
this.shape = config.canvas.addPath({
d: config.path,
'data-code': config.code
}, config.style, config.canvas.rootElement);
this.shape.addClass('jvectormap-region jvectormap-element');
bbox = this.shape.getBBox();
myHook(bbox, text, offsets, config);
return false;
...
}
function myHook(bbox, text, offsets, config){
// Create your own Label like this
text = this.getLabelText(config.code);
if (this.config.label && text) {
offsets = this.getLabelOffsets(config.code);
this.labelX = bbox.x + bbox.width / 2 + offsets[0];
this.labelY = bbox.y + bbox.height / 2 + offsets[1];
this.label = config.canvas.addText({
text: text,
'text-anchor': 'middle',
'alignment-baseline': 'central',
x: this.labelX,
y: this.labelY,
'data-code': config.code
}, config.labelStyle, config.labelsGroup);
this.label.addClass('jvectormap-region jvectormap-element');
}
}
Thats not the best way but maybe it helps you!
I am trying to convert my Highchart into a .csv file. I am using the Export chart Data to CSV plugin for the same. I have pasted the full code at the bottom of the question.
I try to run my code with the above file in scope and it does not have any effect. On further investigation I find out two things
the getOptions() method does not seem to be standard for the Highcharts library as there is no documentation for it.
It does not return the exhaustive list of options available for my Highchart. Hence, the
if (Highcharts.getOptions().exporting)
at the end of the two functions (getDataRows() and getCSV()) returns undefinedas it does not recognize exporting. What seems to be the problem here?
/**
* A small plugin for getting the CSV of a rendered chart
*/
/*global Highcharts, document */
(function (Highcharts) {
'use strict';
var each = Highcharts.each,
downloadAttrSupported = document.createElement('a').download !== undefined;
/**
* Get the data rows as a two dimensional array
*/
Highcharts.Chart.prototype.getDataRows = function () {
var csv,
options = (this.options.exporting || {}).csv || {},
xAxis = this.xAxis[0],
rows = {},
rowArr = [],
dataRows,
names = [],
i,
x,
// Options
dateFormat = options.dateFormat || '%Y-%m-%d %H:%M:%S';
// Loop the series and index values
i = 0;
each(this.series, function (series) {
if (series.options.includeInCSVExport !== false) {
names.push(series.name);
each(series.points, function (point) {
if (!rows[point.x]) {
rows[point.x] = [];
}
rows[point.x].x = point.x;
// Pies, funnels etc. use point name in X row
if (!series.xAxis) {
rows[point.x].name = point.name;
}
rows[point.x][i] = point.y;
});
i += 1;
}
});
// Make a sortable array
for (x in rows) {
if (rows.hasOwnProperty(x)) {
rowArr.push(rows[x]);
}
}
// Sort it by X values
rowArr.sort(function (a, b) {
return a.x - b.x;
});
// Add header row
dataRows = [[xAxis.isDatetimeAxis ? 'DateTime' : 'Category'].concat(names)];
// Transform the rows to CSV
each(rowArr, function (row, i) {
// Add the X/date/category
row.unshift(row.name || (xAxis.isDatetimeAxis ? Highcharts.dateFormat(dateFormat, row.x) : xAxis.categories ? Highcharts.pick(xAxis.categories[row.x], row.x) : row.x));
dataRows.push(row);
});
return dataRows;
};
/**
* Get a CSV string
*/
Highcharts.Chart.prototype.getCSV = function () {
var csv = '',
rows = this.getDataRows(),
options = (this.options.exporting || {}).csv || {},
itemDelimiter = options.itemDelimiter || ';', // use ';' for direct import to Excel
lineDelimiter = options.lineDelimiter || '\n'; // '\n' isn't working with the js csv data extraction
// Transform the rows to CSV
each(rows, function (row, i) {
// Add the values
csv += row.join(itemDelimiter);
// Add the line delimiter
if (i < rows.length - 1) {
csv += lineDelimiter;
}
});
return csv;
};
/**
* Build a HTML table with the data
* /
Highcharts.Chart.prototype.getTable = function () {
var html = '<table>',
rows = this.getDataRows(),
options = (this.options.exporting || {}).csv || {};
// Transform the rows to HTML
each(rows, function (row, i) {
var tag = i ? 'td' : 'th';
html += '<tr><' + tag + '>';
// Add the cells
html += row.join('</' + tag + '><' + tag + '>');
html += '</' + tag + '></tr>';
});
html += '</table>';
return html;
};
*/
// Add "Download CSV" to the exporting menu. Use download attribute if supported, else
// run a simple PHP script that returns a file. The source code for the PHP script can be viewed at
// https://raw.github.com/highslide-software/highcharts.com/master/studies/csv-export/csv.php
if (Highcharts.getOptions().exporting) {
Highcharts.getOptions().exporting.buttons.contextButton.menuItems.push({
text: Highcharts.getOptions().lang.downloadCSV || 'Download CSV',
onclick: function () {
var a;
// Download attribute supported
if (downloadAttrSupported) {
// Client side extraction
a = document.createElement('a');
a.href = 'data:attachment/csv,' + this.getCSV().replace(/\n/g, '%0A');
a.target = '_blank';
a.download = (this.title ? this.title.textStr.replace(/ /g, '-').toLowerCase() : 'chart') + '.csv';
document.body.appendChild(a);
a.click();
a.remove();
// Fall back to server side handling
} else {
Highcharts.post('http://www.highcharts.com/studies/csv-export/csv.php', {
csv: this.getCSV()
});
}
}
});
}
}(Highcharts));
Working with some animated sprites with the canvas element.
I've managed to setup a test case that works splendidly, but only with one canvas element, there will be any given number of animating sprites in the end.
Can't figure out why?
Working demo.
Broken demo.
<canvas id="video1"></canvas>
<input type="button" onclick="PS.draw(1)" value="Play">
<input type="button" onclick="PS.stop(1)" value="Stop">
<canvas id="video2"></canvas>
<input type="button" onclick="PS.draw(2)" value="Play">
<input type="button" onclick="PS.stop(2)" value="Stop">
<script>
var PS = {
init: function(id) { // setup the premise for the canvases, this data is needed to animate the sprites.
var sprites = [];
var context = document.getElementById(src.entries[id].file.ios.filename);
$(context).attr('width', src.entries[id].file.ios.width);
$(context).attr('height', src.entries[id].file.ios.height);
context = context.getContext('2d');
var array = src.entries[id].file.ios.sprite;
for (var i = 0; i < array.length; i++) {
sprites[i] = new Sprite(array[i], { frameW: src.entries[id].file.ios.width, frameH: src.entries[id].file.ios.height, interval: src.entries[id].file.ios.framerate, useTimer: false }); // sprite.js, associates each sprite with given settings.
}
var activeSprite = sprites[0];
var framesDrawn = 0;
var f = 0;
var sprite = 0;
//var filename = src.entries[id].file.ios.filename
window['sprite' + id] = {
"context" : context,
"activeSprite" : activeSprite,
"framesDrawn" : framesDrawn,
"f" : f,
"sprites" : sprites,
"sprite" : sprite,
"framerate" : src.entries[id].file.ios.framerate,
"frames" : src.entries[id].file.ios.frames,
};
},
/*
^ How do I pass this on to draw()? Global vars?
*/
draw: function(id) {
//var filename = src.entries[id].file.ios.filename;
//console.log(window.filename.context);
//console.log(window.sprite+id);
window['sprite' + id ].player = setTimeout(function() { // I'm here to throttle to approriate framerate
requestAnimationFrame(function() {
PS.draw(id);
});
window['sprite' + id ].framesDrawn++;
window['sprite' + id ].f++;
window['sprite' + id ].activeSprite.draw(window['sprite' + id ].context, 0, 0);
if(window['sprite' + id ].f == window['sprite' + id ].frames) { // you've reached the total frames of animation, reset to first spritesheet
window['sprite' + id ].sprite = 0;
window['sprite' + id ].activeSprite = window['sprite' + id ].sprites[window['sprite' + id ].sprite];
window['sprite' + id ].framesDrawn = 0;
window['sprite' + id ].f = 0;
}
if(window['sprite' + id ].framesDrawn == 50) { // each sprite contains 50 images, thus change to new spritesheet
window['sprite' + id ].sprite++;
window['sprite' + id ].activeSprite = window['sprite' + id ].sprites[window['sprite' + id ].sprite];
window['sprite' + id ].framesDrawn = 0;
}
}, 1000 / window['sprite' + id ].framerate);
},
stop: function(id) {
//var filename = src.entries[id].file.ios.filename;
clearTimeout(window['sprite' + id ].player); // some other better way?
}
}
var src ={
"entries":{
"1":{
"file":{
"ios":{
"filename":"video1",
"sprite":["https:\/\/dl.dropboxusercontent.com\/u\/3618143\/sprite1.jpg","https:\/\/dl.dropboxusercontent.com\/u\/3618143\/sprite2.jpg","https:\/\/dl.dropboxusercontent.com\/u\/3618143\/sprite3.jpg"],
"frames":"115",
"framerate":"23.976",
"width":"426",
"height":"240"
}
}
},
"2":{
"file":{
"ios":{
"filename":"video2",
"sprite":["https:\/\/dl.dropboxusercontent.com\/u\/3618143\/sprite1.jpg","https:\/\/dl.dropboxusercontent.com\/u\/3618143\/sprite2.jpg","https:\/\/dl.dropboxusercontent.com\/u\/3618143\/sprite3.jpg"],
"frames":"115",
"framerate":"23.976",
"width":"426",
"height":"240"
}
}
},
}
}
setTimeout(function() {
PS.init(1);
PS.init(2);
},500)
</script>
I haven't been able to work out why exactly, but the problem is the way that sprite.js uses caching and preloading of images to initialise the Sprite. Because both canvas elements are displaying the same set of images, they are conflicting with each other when the second canvas is initialised, so it gets crippled somehow.
The easiest solution seems to be to just disable Sprite image caching:
// override Sprite caching to prevent images conflicting
Sprite.getImageFromCache = function(src) {
//return images[src] ? images[src] : null;
return null;
};
Sprite.saveImageToCache = function(src, image) {
//images[src] = image;
};
Here is an updated fiddle showing it working. http://jsfiddle.net/xbcw7sbb/4/
Note that I also renamed some of your variables to make it a bit more understandable what is what, and attached the filename as a data() object into the canvas to keep it all together.
Plus there was an issue with clicking Play twice causing the animation loop to run double in the canvas, so I created a start(id) method, which only calls draw() if it is not running. draw() itself only needs the context (your filename object), so you can pass that in each time so you don't have to retrieve it from other places on each loop.
/* override Sprite caching to prevent images conflicting */
// (original code commented)
Sprite.getImageFromCache = function(src) {
//return images[src] ? images[src] : null;
return null;
};
Sprite.saveImageToCache = function(src, image) {
//images[src] = image;
};
var PS = {
init: function (id) {
// setup the premise for the canvases,
// this data is needed to animate the sprites.
var sprites = [];
var ios = src.entries[id].file.ios;
var canvas = document.getElementById(ios.filename);
$(canvas).attr('width', ios.width);
$(canvas).attr('height', ios.height);
var context = canvas.getContext('2d');
var imgList = ios.sprite;
for (var i = 0; i < imgList.length; i++) {
sprites[i] = new Sprite(imgList[i], {
frameW: ios.width,
frameH: ios.height,
interval: ios.framerate,
useTimer: false
}); // sprite.js, associates each sprite with given settings.
}
// attach the context to the canvas so we can retrieve it again
$(canvas).data('spritecontext', {
"context": context,
"activeSprite": sprites[0],
"framesDrawn": 0,
"f": 0,
"sprites": sprites,
"sprite": 0,
"framerate": ios.framerate,
"frames": ios.frames
});
},
start: function(id) {
var canvasId = src.entries[id].file.ios.filename;
var canvas = $('#' + canvasId);
var spritecontext = canvas.data('spritecontext');
if(!spritecontext.player) {
PS.draw(id, spritecontext);
}
},
draw: function (id, spritecontext) {
spritecontext.player = setTimeout(function () {
// I'm here to throttle to approriate framerate
requestAnimationFrame(function () {
PS.draw(id, spritecontext);
});
spritecontext.framesDrawn++;
spritecontext.f++;
spritecontext.activeSprite.draw(spritecontext.context, 0, 0);
if (spritecontext.f == spritecontext.frames) {
// you've reached the total frames of animation,
// reset to first spritesheet
spritecontext.sprite = 0;
spritecontext.activeSprite = spritecontext.sprites[spritecontext.sprite];
spritecontext.framesDrawn = 0;
spritecontext.f = 0;
}
if (spritecontext.framesDrawn == 50) {
// each sprite contains 50 images, thus change to new spritesheet
spritecontext.sprite++;
spritecontext.activeSprite = spritecontext.sprites[spritecontext.sprite];
spritecontext.framesDrawn = 0;
}
}, 1000 / 23.976);
},
stop: function (id) {
var canvasId = src.entries[id].file.ios.filename;
var canvas = $('#' + canvasId);
var spritecontext = canvas.data('spritecontext');
// some other better way? - probably not
clearTimeout(spritecontext.player);
// so we know its stopped
spritecontext.player = null;
}
}
var src = {
"entries": {
"1": {
"file": {
"ios": {
"filename": "video1",
"sprite":
["https:\/\/dl.dropboxusercontent.com\/u\/3618143\/sprite1.jpg",
"https:\/\/dl.dropboxusercontent.com\/u\/3618143\/sprite2.jpg",
"https:\/\/dl.dropboxusercontent.com\/u\/3618143\/sprite3.jpg"],
"frames": "115",
"framerate": "23.976",
"width": "426",
"height": "240"
}
}
},
"2": {
"file": {
"ios": {
"filename": "video2",
"sprite":
["https:\/\/dl.dropboxusercontent.com\/u\/3618143\/sprite1.jpg",
"https:\/\/dl.dropboxusercontent.com\/u\/3618143\/sprite2.jpg",
"https:\/\/dl.dropboxusercontent.com\/u\/3618143\/sprite3.jpg"],
"frames": "115",
"framerate": "23.976",
"width": "426",
"height": "240"
}
}
},
}
}
setTimeout(function () {
PS.init(1);
PS.init(2);
}, 500);