Skip to content

Commit f40e25d

Browse files
author
Ruraj Joshi
committed
Updated Highstock to v5.0.10 and added data export modules. This shouldn't require any new test cases.
1 parent 4c28474 commit f40e25d

File tree

4 files changed

+928
-300
lines changed

4 files changed

+928
-300
lines changed

templates/assets/export-csv.js

Lines changed: 392 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,392 @@
1+
/**
2+
* A Highcharts plugin for exporting data from a rendered chart as CSV, XLS or HTML table
3+
*
4+
* Author: Torstein Honsi
5+
* Licence: MIT
6+
* Version: 1.4.7
7+
*/
8+
/*global Highcharts, window, document, Blob */
9+
(function (factory) {
10+
if (typeof module === 'object' && module.exports) {
11+
module.exports = factory;
12+
} else {
13+
factory(Highcharts);
14+
}
15+
})(function (Highcharts) {
16+
17+
'use strict';
18+
19+
var each = Highcharts.each,
20+
pick = Highcharts.pick,
21+
seriesTypes = Highcharts.seriesTypes,
22+
downloadAttrSupported = document.createElement('a').download !== undefined;
23+
24+
Highcharts.setOptions({
25+
lang: {
26+
downloadCSV: 'Download CSV',
27+
downloadXLS: 'Download XLS',
28+
viewData: 'View data table'
29+
}
30+
});
31+
32+
33+
/**
34+
* Get the data rows as a two dimensional array
35+
*/
36+
Highcharts.Chart.prototype.getDataRows = function () {
37+
var options = (this.options.exporting || {}).csv || {},
38+
xAxis,
39+
xAxes = this.xAxis,
40+
rows = {},
41+
rowArr = [],
42+
dataRows,
43+
names = [],
44+
i,
45+
x,
46+
xTitle,
47+
// Options
48+
dateFormat = options.dateFormat || '%Y-%m-%d %H:%M:%S',
49+
columnHeaderFormatter = options.columnHeaderFormatter || function (item, key, keyLength) {
50+
if (item instanceof Highcharts.Axis) {
51+
return (item.options.title && item.options.title.text) ||
52+
(item.isDatetimeAxis ? 'DateTime' : 'Category');
53+
}
54+
return item ?
55+
item.name + (keyLength > 1 ? ' ('+ key + ')' : '') :
56+
'Category';
57+
},
58+
xAxisIndices = [];
59+
60+
// Loop the series and index values
61+
i = 0;
62+
each(this.series, function (series) {
63+
var keys = series.options.keys,
64+
pointArrayMap = keys || series.pointArrayMap || ['y'],
65+
valueCount = pointArrayMap.length,
66+
requireSorting = series.requireSorting,
67+
categoryMap = {},
68+
xAxisIndex = Highcharts.inArray(series.xAxis, xAxes),
69+
j;
70+
71+
// Map the categories for value axes
72+
each(pointArrayMap, function (prop) {
73+
categoryMap[prop] = (series[prop + 'Axis'] && series[prop + 'Axis'].categories) || [];
74+
});
75+
76+
if (series.options.includeInCSVExport !== false && series.visible !== false) { // #55
77+
78+
// Build a lookup for X axis index and the position of the first
79+
// series that belongs to that X axis. Includes -1 for non-axis
80+
// series types like pies.
81+
if (!Highcharts.find(xAxisIndices, function (index) {
82+
return index[0] === xAxisIndex;
83+
})) {
84+
xAxisIndices.push([xAxisIndex, i]);
85+
}
86+
87+
// Add the column headers, usually the same as series names
88+
j = 0;
89+
while (j < valueCount) {
90+
names.push(columnHeaderFormatter(series, pointArrayMap[j], pointArrayMap.length));
91+
j = j + 1;
92+
}
93+
94+
each(series.points, function (point, pIdx) {
95+
var key = requireSorting ? point.x : pIdx,
96+
prop,
97+
val;
98+
99+
j = 0;
100+
101+
if (!rows[key]) {
102+
// Generate the row
103+
rows[key] = [];
104+
// Contain the X values from one or more X axes
105+
rows[key].xValues = [];
106+
}
107+
rows[key].x = point.x;
108+
rows[key].xValues[xAxisIndex] = point.x;
109+
110+
// Pies, funnels, geo maps etc. use point name in X row
111+
if (!series.xAxis || series.exportKey === 'name') {
112+
rows[key].name = point.name;
113+
}
114+
115+
while (j < valueCount) {
116+
prop = pointArrayMap[j]; // y, z etc
117+
val = point[prop];
118+
rows[key][i + j] = pick(categoryMap[prop][val], val); // Pick a Y axis category if present
119+
j = j + 1;
120+
}
121+
122+
});
123+
i = i + j;
124+
}
125+
});
126+
127+
// Make a sortable array
128+
for (x in rows) {
129+
if (rows.hasOwnProperty(x)) {
130+
rowArr.push(rows[x]);
131+
}
132+
}
133+
134+
var binding, xAxisIndex, column;
135+
dataRows = [names];
136+
137+
i = xAxisIndices.length;
138+
while (i--) { // Start from end to splice in
139+
xAxisIndex = xAxisIndices[i][0];
140+
column = xAxisIndices[i][1];
141+
xAxis = xAxes[xAxisIndex];
142+
143+
// Sort it by X values
144+
rowArr.sort(function (a, b) {
145+
return a.xValues[xAxisIndex] - b.xValues[xAxisIndex];
146+
});
147+
148+
// Add header row
149+
xTitle = columnHeaderFormatter(xAxis);
150+
//dataRows = [[xTitle].concat(names)];
151+
dataRows[0].splice(column, 0, xTitle);
152+
153+
// Add the category column
154+
each(rowArr, function (row) {
155+
156+
var category = row.name;
157+
if (!category) {
158+
if (xAxis.isDatetimeAxis) {
159+
if (row.x instanceof Date) {
160+
row.x = row.x.getTime();
161+
}
162+
category = Highcharts.dateFormat(dateFormat, row.x);
163+
} else if (xAxis.categories) {
164+
category = pick(
165+
xAxis.names[row.x],
166+
xAxis.categories[row.x],
167+
row.x
168+
)
169+
} else {
170+
category = row.x;
171+
}
172+
}
173+
174+
// Add the X/date/category
175+
row.splice(column, 0, category);
176+
});
177+
}
178+
dataRows = dataRows.concat(rowArr);
179+
180+
return dataRows;
181+
};
182+
183+
/**
184+
* Get a CSV string
185+
*/
186+
Highcharts.Chart.prototype.getCSV = function (useLocalDecimalPoint) {
187+
var csv = '',
188+
rows = this.getDataRows(),
189+
options = (this.options.exporting || {}).csv || {},
190+
itemDelimiter = options.itemDelimiter || ',', // use ';' for direct import to Excel
191+
lineDelimiter = options.lineDelimiter || '\n'; // '\n' isn't working with the js csv data extraction
192+
193+
// Transform the rows to CSV
194+
each(rows, function (row, i) {
195+
var val = '',
196+
j = row.length,
197+
n = useLocalDecimalPoint ? (1.1).toLocaleString()[1] : '.';
198+
while (j--) {
199+
val = row[j];
200+
if (typeof val === "string") {
201+
val = '"' + val + '"';
202+
}
203+
if (typeof val === 'number') {
204+
if (n === ',') {
205+
val = val.toString().replace(".", ",");
206+
}
207+
}
208+
row[j] = val;
209+
}
210+
// Add the values
211+
csv += row.join(itemDelimiter);
212+
213+
// Add the line delimiter
214+
if (i < rows.length - 1) {
215+
csv += lineDelimiter;
216+
}
217+
});
218+
return csv;
219+
};
220+
221+
/**
222+
* Build a HTML table with the data
223+
*/
224+
Highcharts.Chart.prototype.getTable = function (useLocalDecimalPoint) {
225+
var html = '<table><thead>',
226+
rows = this.getDataRows();
227+
228+
// Transform the rows to HTML
229+
each(rows, function (row, i) {
230+
var tag = i ? 'td' : 'th',
231+
val,
232+
j,
233+
n = useLocalDecimalPoint ? (1.1).toLocaleString()[1] : '.';
234+
235+
html += '<tr>';
236+
for (j = 0; j < row.length; j = j + 1) {
237+
val = row[j];
238+
// Add the cell
239+
if (typeof val === 'number') {
240+
val = val.toString();
241+
if (n === ',') {
242+
val = val.replace('.', n);
243+
}
244+
html += '<' + tag + ' class="number">' + val + '</' + tag + '>';
245+
246+
} else {
247+
html += '<' + tag + '>' + (val === undefined ? '' : val) + '</' + tag + '>';
248+
}
249+
}
250+
251+
html += '</tr>';
252+
253+
// After the first row, end head and start body
254+
if (!i) {
255+
html += '</thead><tbody>';
256+
}
257+
258+
});
259+
html += '</tbody></table>';
260+
261+
return html;
262+
};
263+
264+
function getContent(chart, href, extension, content, MIME) {
265+
var a,
266+
blobObject,
267+
name,
268+
options = (chart.options.exporting || {}).csv || {},
269+
url = options.url || 'http://www.highcharts.com/studies/csv-export/download.php';
270+
271+
if (chart.options.exporting.filename) {
272+
name = chart.options.exporting.filename;
273+
} else if (chart.title) {
274+
name = chart.title.textStr.replace(/ /g, '-').toLowerCase();
275+
} else {
276+
name = 'chart';
277+
}
278+
279+
// MS specific. Check this first because of bug with Edge (#76)
280+
if (window.Blob && window.navigator.msSaveOrOpenBlob) {
281+
// Falls to msSaveOrOpenBlob if download attribute is not supported
282+
blobObject = new Blob([content]);
283+
window.navigator.msSaveOrOpenBlob(blobObject, name + '.' + extension);
284+
285+
// Download attribute supported
286+
} else if (downloadAttrSupported) {
287+
a = document.createElement('a');
288+
a.href = href;
289+
a.target = '_blank';
290+
a.download = name + '.' + extension;
291+
chart.container.append(a); // #111
292+
a.click();
293+
a.remove();
294+
295+
} else {
296+
// Fall back to server side handling
297+
Highcharts.post(url, {
298+
data: content,
299+
type: MIME,
300+
extension: extension
301+
});
302+
}
303+
}
304+
305+
/**
306+
* Call this on click of 'Download CSV' button
307+
*/
308+
Highcharts.Chart.prototype.downloadCSV = function () {
309+
var csv = this.getCSV(true);
310+
getContent(
311+
this,
312+
'data:text/csv,\uFEFF' + encodeURIComponent(csv),
313+
'csv',
314+
csv,
315+
'text/csv'
316+
);
317+
};
318+
319+
/**
320+
* Call this on click of 'Download XLS' button
321+
*/
322+
Highcharts.Chart.prototype.downloadXLS = function () {
323+
var uri = 'data:application/vnd.ms-excel;base64,',
324+
template = '<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40">' +
325+
'<head><!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet>' +
326+
'<x:Name>Ark1</x:Name>' +
327+
'<x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet></x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]-->' +
328+
'<style>td{border:none;font-family: Calibri, sans-serif;} .number{mso-number-format:"0.00";}</style>' +
329+
'<meta name=ProgId content=Excel.Sheet>' +
330+
'<meta charset=UTF-8>' +
331+
'</head><body>' +
332+
this.getTable(true) +
333+
'</body></html>',
334+
base64 = function (s) {
335+
return window.btoa(unescape(encodeURIComponent(s))); // #50
336+
};
337+
getContent(
338+
this,
339+
uri + base64(template),
340+
'xls',
341+
template,
342+
'application/vnd.ms-excel'
343+
);
344+
};
345+
346+
/**
347+
* View the data in a table below the chart
348+
*/
349+
Highcharts.Chart.prototype.viewData = function () {
350+
if (!this.dataTableDiv) {
351+
this.dataTableDiv = document.createElement('div');
352+
this.dataTableDiv.className = 'highcharts-data-table';
353+
354+
// Insert after the chart container
355+
this.renderTo.parentNode.insertBefore(
356+
this.dataTableDiv,
357+
this.renderTo.nextSibling
358+
);
359+
}
360+
361+
this.dataTableDiv.innerHTML = this.getTable();
362+
};
363+
364+
365+
// Add "Download CSV" to the exporting menu. Use download attribute if supported, else
366+
// run a simple PHP script that returns a file. The source code for the PHP script can be viewed at
367+
// https://raw.github.com/highslide-software/highcharts.com/master/studies/csv-export/csv.php
368+
if (Highcharts.getOptions().exporting) {
369+
Highcharts.getOptions().exporting.buttons.contextButton.menuItems.push({
370+
textKey: 'downloadCSV',
371+
onclick: function () { this.downloadCSV(); }
372+
}, {
373+
textKey: 'downloadXLS',
374+
onclick: function () { this.downloadXLS(); }
375+
}, {
376+
textKey: 'viewData',
377+
onclick: function () { this.viewData(); }
378+
});
379+
}
380+
381+
// Series specific
382+
if (seriesTypes.map) {
383+
seriesTypes.map.prototype.exportKey = 'name';
384+
}
385+
if (seriesTypes.mapbubble) {
386+
seriesTypes.mapbubble.prototype.exportKey = 'name';
387+
}
388+
if (seriesTypes.treemap) {
389+
seriesTypes.treemap.prototype.exportKey = 'name';
390+
}
391+
392+
});

0 commit comments

Comments
 (0)