|
@@ -1,8 +1,8 @@
|
|
|
/*
|
|
|
* Chartkick.js
|
|
|
- * Create beautiful JavaScript charts with minimal code
|
|
|
+ * Create beautiful charts with one line of JavaScript
|
|
|
* https://github.com/ankane/chartkick.js
|
|
|
- * v2.0.0
|
|
|
+ * v2.2.1
|
|
|
* MIT License
|
|
|
*/
|
|
|
|
|
@@ -15,6 +15,7 @@
|
|
|
var Chartkick, ISO8601_PATTERN, DECIMAL_SEPARATOR, adapters = [];
|
|
|
var DATE_PATTERN = /^(\d\d\d\d)(\-)?(\d\d)(\-)?(\d\d)$/i;
|
|
|
var GoogleChartsAdapter, HighchartsAdapter, ChartjsAdapter;
|
|
|
+ var pendingRequests = [], runningRequests = 0, maxRequests = 4;
|
|
|
|
|
|
// helpers
|
|
|
|
|
@@ -62,10 +63,10 @@
|
|
|
function parseISO8601(input) {
|
|
|
var day, hour, matches, milliseconds, minutes, month, offset, result, seconds, type, year;
|
|
|
type = Object.prototype.toString.call(input);
|
|
|
- if (type === '[object Date]') {
|
|
|
+ if (type === "[object Date]") {
|
|
|
return input;
|
|
|
}
|
|
|
- if (type !== '[object String]') {
|
|
|
+ if (type !== "[object String]") {
|
|
|
return;
|
|
|
}
|
|
|
matches = input.match(ISO8601_PATTERN);
|
|
@@ -83,7 +84,7 @@
|
|
|
if (matches[17]) {
|
|
|
offset += parseInt(matches[17], 10);
|
|
|
}
|
|
|
- offset *= matches[14] === '-' ? -1 : 1;
|
|
|
+ offset *= matches[14] === "-" ? -1 : 1;
|
|
|
result -= offset * 60 * 1000;
|
|
|
}
|
|
|
return new Date(result);
|
|
@@ -104,15 +105,18 @@
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- function jsOptionsFunc(defaultOptions, hideLegend, setMin, setMax, setStacked, setXtitle, setYtitle) {
|
|
|
- return function (series, opts, chartOptions) {
|
|
|
+ function jsOptionsFunc(defaultOptions, hideLegend, setTitle, setMin, setMax, setStacked, setXtitle, setYtitle) {
|
|
|
+ return function (chart, opts, chartOptions) {
|
|
|
+ var series = chart.data;
|
|
|
var options = merge({}, defaultOptions);
|
|
|
options = merge(options, chartOptions || {});
|
|
|
|
|
|
- // hide legend
|
|
|
- // this is *not* an external option!
|
|
|
- if (opts.hideLegend) {
|
|
|
- hideLegend(options);
|
|
|
+ if (chart.hideLegend || "legend" in opts) {
|
|
|
+ hideLegend(options, opts.legend, chart.hideLegend);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (opts.title) {
|
|
|
+ setTitle(options, opts.title);
|
|
|
}
|
|
|
|
|
|
// min
|
|
@@ -163,19 +167,61 @@
|
|
|
element.style.color = "#ff0000";
|
|
|
}
|
|
|
|
|
|
- function getJSON(element, url, success) {
|
|
|
- var $ = window.jQuery || window.Zepto || window.$;
|
|
|
- $.ajax({
|
|
|
- dataType: "json",
|
|
|
- url: url,
|
|
|
- success: success,
|
|
|
- error: function (jqXHR, textStatus, errorThrown) {
|
|
|
- var message = (typeof errorThrown === "string") ? errorThrown : errorThrown.message;
|
|
|
- chartError(element, message);
|
|
|
+ function pushRequest(element, url, success) {
|
|
|
+ pendingRequests.push([element, url, success]);
|
|
|
+ runNext();
|
|
|
+ }
|
|
|
+
|
|
|
+ function runNext() {
|
|
|
+ if (runningRequests < maxRequests) {
|
|
|
+ var request = pendingRequests.shift()
|
|
|
+ if (request) {
|
|
|
+ runningRequests++;
|
|
|
+ getJSON(request[0], request[1], request[2]);
|
|
|
+ runNext();
|
|
|
}
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function requestComplete() {
|
|
|
+ runningRequests--;
|
|
|
+ runNext();
|
|
|
+ }
|
|
|
+
|
|
|
+ function getJSON(element, url, success) {
|
|
|
+ ajaxCall(url, success, function (jqXHR, textStatus, errorThrown) {
|
|
|
+ var message = (typeof errorThrown === "string") ? errorThrown : errorThrown.message;
|
|
|
+ chartError(element, message);
|
|
|
});
|
|
|
}
|
|
|
|
|
|
+ function ajaxCall(url, success, error) {
|
|
|
+ var $ = window.jQuery || window.Zepto || window.$;
|
|
|
+
|
|
|
+ if ($) {
|
|
|
+ $.ajax({
|
|
|
+ dataType: "json",
|
|
|
+ url: url,
|
|
|
+ success: success,
|
|
|
+ error: error,
|
|
|
+ complete: requestComplete
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ var xhr = new XMLHttpRequest();
|
|
|
+ xhr.open("GET", url, true);
|
|
|
+ xhr.setRequestHeader("Content-Type", "application/json");
|
|
|
+ xhr.onload = function () {
|
|
|
+ requestComplete();
|
|
|
+ if (xhr.status === 200) {
|
|
|
+ success(JSON.parse(xhr.responseText), xhr.statusText, xhr);
|
|
|
+ } else {
|
|
|
+ error(xhr, "error", xhr.statusText);
|
|
|
+ }
|
|
|
+ };
|
|
|
+ xhr.send();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
function errorCatcher(chart, callback) {
|
|
|
try {
|
|
|
callback(chart);
|
|
@@ -185,18 +231,79 @@
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- function fetchDataSource(chart, callback) {
|
|
|
- if (typeof chart.dataSource === "string") {
|
|
|
- getJSON(chart.element, chart.dataSource, function (data, textStatus, jqXHR) {
|
|
|
- chart.data = data;
|
|
|
+ function fetchDataSource(chart, callback, dataSource) {
|
|
|
+ if (typeof dataSource === "string") {
|
|
|
+ pushRequest(chart.element, dataSource, function (data, textStatus, jqXHR) {
|
|
|
+ chart.rawData = data;
|
|
|
errorCatcher(chart, callback);
|
|
|
});
|
|
|
} else {
|
|
|
- chart.data = chart.dataSource;
|
|
|
+ chart.rawData = dataSource;
|
|
|
errorCatcher(chart, callback);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ function addDownloadButton(chart) {
|
|
|
+ var element = chart.element;
|
|
|
+ var link = document.createElement("a");
|
|
|
+ link.download = chart.options.download === true ? "chart.png" : chart.options.download; // http://caniuse.com/download
|
|
|
+ link.style.position = "absolute";
|
|
|
+ link.style.top = "20px";
|
|
|
+ link.style.right = "20px";
|
|
|
+ link.style.zIndex = 1000;
|
|
|
+ link.style.lineHeight = "20px";
|
|
|
+ link.target = "_blank"; // for safari
|
|
|
+ var image = document.createElement("img");
|
|
|
+ image.alt = "Download";
|
|
|
+ image.style.border = "none";
|
|
|
+ // icon from font-awesome
|
|
|
+ // http://fa2png.io/
|
|
|
+ image.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAMAAAC6V+0/AAABCFBMVEUAAADMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMywEsqxAAAAV3RSTlMAAQIDBggJCgsMDQ4PERQaHB0eISIjJCouLzE0OTo/QUJHSUpLTU5PUllhYmltcHh5foWLjI+SlaCio6atr7S1t7m6vsHHyM7R2tze5Obo7fHz9ff5+/1hlxK2AAAA30lEQVQYGUXBhVYCQQBA0TdYWAt2d3d3YWAHyur7/z9xgD16Lw0DW+XKx+1GgX+FRzM3HWQWrHl5N/oapW5RPe0PkBu+UYeICvozTWZVK23Ao04B79oJrOsJDOoxkZoQPWgX29pHpCZEk7rEvQYiNSFq1UMqvlCjJkRBS1R8hb00Vb/TajtBL7nTHE1X1vyMQF732dQhyF2o6SAwrzP06iUQzvwsArlnzcOdrgBhJyHa1QOgO9U1GsKuvjUTjavliZYQ8nNPapG6sap/3nrIdJ6bOWzmX/fy0XVpfzZP3S8OJT3g9EEiJwAAAABJRU5ErkJggg==";
|
|
|
+ link.appendChild(image);
|
|
|
+ element.style.position = "relative";
|
|
|
+
|
|
|
+ chart.downloadAttached = true;
|
|
|
+
|
|
|
+ // mouseenter
|
|
|
+ addEvent(element, "mouseover", function(e) {
|
|
|
+ var related = e.relatedTarget;
|
|
|
+ // check download option again to ensure it wasn't changed
|
|
|
+ if (!related || (related !== this && !childOf(this, related)) && chart.options.download) {
|
|
|
+ link.href = chart.toImage();
|
|
|
+ element.appendChild(link);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // mouseleave
|
|
|
+ addEvent(element, "mouseout", function(e) {
|
|
|
+ var related = e.relatedTarget;
|
|
|
+ if (!related || (related !== this && !childOf(this, related))) {
|
|
|
+ if (link.parentNode) {
|
|
|
+ link.parentNode.removeChild(link);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // http://stackoverflow.com/questions/10149963/adding-event-listener-cross-browser
|
|
|
+ function addEvent(elem, event, fn) {
|
|
|
+ if (elem.addEventListener) {
|
|
|
+ elem.addEventListener(event, fn, false);
|
|
|
+ } else {
|
|
|
+ elem.attachEvent("on" + event, function() {
|
|
|
+ // set the this pointer same as addEventListener when fn is called
|
|
|
+ return(fn.call(elem, window.event));
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // https://gist.github.com/shawnbot/4166283
|
|
|
+ function childOf(p, c) {
|
|
|
+ if (p === c) return false;
|
|
|
+ while (c && c !== p) c = c.parentNode;
|
|
|
+ return c === p;
|
|
|
+ }
|
|
|
+
|
|
|
// type conversions
|
|
|
|
|
|
function toStr(n) {
|
|
@@ -299,8 +406,25 @@
|
|
|
}
|
|
|
};
|
|
|
|
|
|
- var hideLegend = function (options) {
|
|
|
- options.legend.enabled = false;
|
|
|
+ var hideLegend = function (options, legend, hideLegend) {
|
|
|
+ if (legend !== undefined) {
|
|
|
+ options.legend.enabled = !!legend;
|
|
|
+ if (legend && legend !== true) {
|
|
|
+ if (legend === "top" || legend === "bottom") {
|
|
|
+ options.legend.verticalAlign = legend;
|
|
|
+ } else {
|
|
|
+ options.legend.layout = "vertical";
|
|
|
+ options.legend.verticalAlign = "middle";
|
|
|
+ options.legend.align = legend;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else if (hideLegend) {
|
|
|
+ options.legend.enabled = false;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ var setTitle = function (options, title) {
|
|
|
+ options.title.text = title;
|
|
|
};
|
|
|
|
|
|
var setMin = function (options, min) {
|
|
@@ -323,7 +447,7 @@
|
|
|
options.yAxis.title.text = title;
|
|
|
};
|
|
|
|
|
|
- var jsOptions = jsOptionsFunc(defaultOptions, hideLegend, setMin, setMax, setStacked, setXtitle, setYtitle);
|
|
|
+ var jsOptions = jsOptionsFunc(defaultOptions, hideLegend, setTitle, setMin, setMax, setStacked, setXtitle, setYtitle);
|
|
|
|
|
|
this.renderLineChart = function (chart, chartType) {
|
|
|
chartType = chartType || "spline";
|
|
@@ -334,6 +458,9 @@
|
|
|
areaspline: {
|
|
|
stacking: "normal"
|
|
|
},
|
|
|
+ area: {
|
|
|
+ stacking: "normal"
|
|
|
+ },
|
|
|
series: {
|
|
|
marker: {
|
|
|
enabled: false
|
|
@@ -342,15 +469,26 @@
|
|
|
}
|
|
|
};
|
|
|
}
|
|
|
- var options = jsOptions(chart.data, chart.options, chartOptions), data, i, j;
|
|
|
- options.xAxis.type = chart.options.discrete ? "category" : "datetime";
|
|
|
- options.chart.type = chartType;
|
|
|
+
|
|
|
+ if (chart.options.curve === false) {
|
|
|
+ if (chartType === "areaspline") {
|
|
|
+ chartType = "area";
|
|
|
+ } else if (chartType === "spline") {
|
|
|
+ chartType = "line";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ var options = jsOptions(chart, chart.options, chartOptions), data, i, j;
|
|
|
+ options.xAxis.type = chart.discrete ? "category" : "datetime";
|
|
|
+ if (!options.chart.type) {
|
|
|
+ options.chart.type = chartType;
|
|
|
+ }
|
|
|
options.chart.renderTo = chart.element.id;
|
|
|
|
|
|
var series = chart.data;
|
|
|
for (i = 0; i < series.length; i++) {
|
|
|
data = series[i].data;
|
|
|
- if (!chart.options.discrete) {
|
|
|
+ if (!chart.discrete) {
|
|
|
for (j = 0; j < data.length; j++) {
|
|
|
data[j][0] = data[j][0].getTime();
|
|
|
}
|
|
@@ -358,37 +496,50 @@
|
|
|
series[i].marker = {symbol: "circle"};
|
|
|
}
|
|
|
options.series = series;
|
|
|
- new Highcharts.Chart(options);
|
|
|
+ chart.chart = new Highcharts.Chart(options);
|
|
|
};
|
|
|
|
|
|
this.renderScatterChart = function (chart) {
|
|
|
var chartOptions = {};
|
|
|
- var options = jsOptions(chart.data, chart.options, chartOptions);
|
|
|
- options.chart.type = 'scatter';
|
|
|
+ var options = jsOptions(chart, chart.options, chartOptions);
|
|
|
+ options.chart.type = "scatter";
|
|
|
options.chart.renderTo = chart.element.id;
|
|
|
options.series = chart.data;
|
|
|
- new Highcharts.Chart(options);
|
|
|
+ chart.chart = new Highcharts.Chart(options);
|
|
|
};
|
|
|
|
|
|
this.renderPieChart = function (chart) {
|
|
|
- var chartOptions = {};
|
|
|
+ var chartOptions = merge(defaultOptions, {});
|
|
|
+
|
|
|
if (chart.options.colors) {
|
|
|
chartOptions.colors = chart.options.colors;
|
|
|
}
|
|
|
- var options = merge(merge(defaultOptions, chartOptions), chart.options.library || {});
|
|
|
+ if (chart.options.donut) {
|
|
|
+ chartOptions.plotOptions = {pie: {innerSize: "50%"}};
|
|
|
+ }
|
|
|
+
|
|
|
+ if ("legend" in chart.options) {
|
|
|
+ hideLegend(chartOptions, chart.options.legend);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (chart.options.title) {
|
|
|
+ setTitle(chartOptions, chart.options.title);
|
|
|
+ }
|
|
|
+
|
|
|
+ var options = merge(chartOptions, chart.options.library || {});
|
|
|
options.chart.renderTo = chart.element.id;
|
|
|
options.series = [{
|
|
|
type: "pie",
|
|
|
name: chart.options.label || "Value",
|
|
|
data: chart.data
|
|
|
}];
|
|
|
- new Highcharts.Chart(options);
|
|
|
+ chart.chart = new Highcharts.Chart(options);
|
|
|
};
|
|
|
|
|
|
this.renderColumnChart = function (chart, chartType) {
|
|
|
chartType = chartType || "column";
|
|
|
var series = chart.data;
|
|
|
- var options = jsOptions(series, chart.options), i, j, s, d, rows = [];
|
|
|
+ var options = jsOptions(chart, chart.options), i, j, s, d, rows = [], categories = [];
|
|
|
options.chart.type = chartType;
|
|
|
options.chart.renderTo = chart.element.id;
|
|
|
|
|
@@ -399,17 +550,12 @@
|
|
|
d = s.data[j];
|
|
|
if (!rows[d[0]]) {
|
|
|
rows[d[0]] = new Array(series.length);
|
|
|
+ categories.push(d[0]);
|
|
|
}
|
|
|
rows[d[0]][i] = d[1];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- var categories = [];
|
|
|
- for (i in rows) {
|
|
|
- if (rows.hasOwnProperty(i)) {
|
|
|
- categories.push(i);
|
|
|
- }
|
|
|
- }
|
|
|
options.xAxis.categories = categories;
|
|
|
|
|
|
var newSeries = [];
|
|
@@ -426,7 +572,7 @@
|
|
|
}
|
|
|
options.series = newSeries;
|
|
|
|
|
|
- new Highcharts.Chart(options);
|
|
|
+ chart.chart = new Highcharts.Chart(options);
|
|
|
};
|
|
|
|
|
|
var self = this;
|
|
@@ -441,7 +587,7 @@
|
|
|
};
|
|
|
adapters.push(HighchartsAdapter);
|
|
|
}
|
|
|
- if (!GoogleChartsAdapter && window.google && window.google.setOnLoadCallback) {
|
|
|
+ if (!GoogleChartsAdapter && window.google && (window.google.setOnLoadCallback || window.google.charts)) {
|
|
|
GoogleChartsAdapter = new function () {
|
|
|
var google = window.google;
|
|
|
|
|
@@ -484,7 +630,12 @@
|
|
|
if (config.language) {
|
|
|
loadOptions.language = config.language;
|
|
|
}
|
|
|
- google.load("visualization", "1", loadOptions);
|
|
|
+
|
|
|
+ if (window.google.setOnLoadCallback) {
|
|
|
+ google.load("visualization", "1", loadOptions);
|
|
|
+ } else {
|
|
|
+ google.charts.load("current", loadOptions);
|
|
|
+ }
|
|
|
}
|
|
|
};
|
|
|
|
|
@@ -531,8 +682,25 @@
|
|
|
}
|
|
|
};
|
|
|
|
|
|
- var hideLegend = function (options) {
|
|
|
- options.legend.position = "none";
|
|
|
+ var hideLegend = function (options, legend, hideLegend) {
|
|
|
+ if (legend !== undefined) {
|
|
|
+ var position;
|
|
|
+ if (!legend) {
|
|
|
+ position = "none";
|
|
|
+ } else if (legend === true) {
|
|
|
+ position = "right";
|
|
|
+ } else {
|
|
|
+ position = legend;
|
|
|
+ }
|
|
|
+ options.legend.position = position;
|
|
|
+ } else if (hideLegend) {
|
|
|
+ options.legend.position = "none";
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ var setTitle = function (options, title) {
|
|
|
+ options.title = title;
|
|
|
+ options.titleTextStyle = {color: "#333", fontSize: "20px"};
|
|
|
};
|
|
|
|
|
|
var setMin = function (options, min) {
|
|
@@ -565,11 +733,11 @@
|
|
|
options.vAxis.titleTextStyle.italic = false;
|
|
|
};
|
|
|
|
|
|
- var jsOptions = jsOptionsFunc(defaultOptions, hideLegend, setMin, setMax, setStacked, setXtitle, setYtitle);
|
|
|
+ var jsOptions = jsOptionsFunc(defaultOptions, hideLegend, setTitle, setMin, setMax, setStacked, setXtitle, setYtitle);
|
|
|
|
|
|
// cant use object as key
|
|
|
var createDataTable = function (series, columnType) {
|
|
|
- var i, j, s, d, key, rows = [];
|
|
|
+ var i, j, s, d, key, rows = [], sortedLabels = [];
|
|
|
for (i = 0; i < series.length; i++) {
|
|
|
s = series[i];
|
|
|
|
|
@@ -578,6 +746,7 @@
|
|
|
key = (columnType === "datetime") ? d[0].getTime() : d[0];
|
|
|
if (!rows[key]) {
|
|
|
rows[key] = new Array(series.length);
|
|
|
+ sortedLabels.push(key);
|
|
|
}
|
|
|
rows[key][i] = toFloat(d[1]);
|
|
|
}
|
|
@@ -586,18 +755,17 @@
|
|
|
var rows2 = [];
|
|
|
var day = true;
|
|
|
var value;
|
|
|
- for (i in rows) {
|
|
|
- if (rows.hasOwnProperty(i)) {
|
|
|
- if (columnType === "datetime") {
|
|
|
- value = new Date(toFloat(i));
|
|
|
- day = day && isDay(value);
|
|
|
- } else if (columnType === "number") {
|
|
|
- value = toFloat(i);
|
|
|
- } else {
|
|
|
- value = i;
|
|
|
- }
|
|
|
- rows2.push([value].concat(rows[i]));
|
|
|
+ for (var j = 0; j < sortedLabels.length; j++) {
|
|
|
+ var i = sortedLabels[j];
|
|
|
+ if (columnType === "datetime") {
|
|
|
+ value = new Date(toFloat(i));
|
|
|
+ day = day && isDay(value);
|
|
|
+ } else if (columnType === "number") {
|
|
|
+ value = toFloat(i);
|
|
|
+ } else {
|
|
|
+ value = i;
|
|
|
}
|
|
|
+ rows2.push([value].concat(rows[i]));
|
|
|
}
|
|
|
if (columnType === "datetime") {
|
|
|
rows2.sort(sortByTime);
|
|
@@ -626,8 +794,14 @@
|
|
|
|
|
|
this.renderLineChart = function (chart) {
|
|
|
waitForLoaded(function () {
|
|
|
- var options = jsOptions(chart.data, chart.options);
|
|
|
- var data = createDataTable(chart.data, chart.options.discrete ? "string" : "datetime");
|
|
|
+ var chartOptions = {};
|
|
|
+
|
|
|
+ if (chart.options.curve === false) {
|
|
|
+ chartOptions.curveType = "none";
|
|
|
+ }
|
|
|
+
|
|
|
+ var options = jsOptions(chart, chart.options, chartOptions);
|
|
|
+ var data = createDataTable(chart.data, chart.discrete ? "string" : "datetime");
|
|
|
chart.chart = new google.visualization.LineChart(chart.element);
|
|
|
resize(function () {
|
|
|
chart.chart.draw(data, options);
|
|
@@ -641,11 +815,21 @@
|
|
|
chartArea: {
|
|
|
top: "10%",
|
|
|
height: "80%"
|
|
|
- }
|
|
|
+ },
|
|
|
+ legend: {}
|
|
|
};
|
|
|
if (chart.options.colors) {
|
|
|
chartOptions.colors = chart.options.colors;
|
|
|
}
|
|
|
+ if (chart.options.donut) {
|
|
|
+ chartOptions.pieHole = 0.5;
|
|
|
+ }
|
|
|
+ if ("legend" in chart.options) {
|
|
|
+ hideLegend(chartOptions, chart.options.legend);
|
|
|
+ }
|
|
|
+ if (chart.options.title) {
|
|
|
+ setTitle(chartOptions, chart.options.title);
|
|
|
+ }
|
|
|
var options = merge(merge(defaultOptions, chartOptions), chart.options.library || {});
|
|
|
|
|
|
var data = new google.visualization.DataTable();
|
|
@@ -662,7 +846,7 @@
|
|
|
|
|
|
this.renderColumnChart = function (chart) {
|
|
|
waitForLoaded(function () {
|
|
|
- var options = jsOptions(chart.data, chart.options);
|
|
|
+ var options = jsOptions(chart, chart.options);
|
|
|
var data = createDataTable(chart.data, "string");
|
|
|
chart.chart = new google.visualization.ColumnChart(chart.element);
|
|
|
resize(function () {
|
|
@@ -680,7 +864,7 @@
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
- var options = jsOptionsFunc(defaultOptions, hideLegend, setBarMin, setBarMax, setStacked)(chart.data, chart.options, chartOptions);
|
|
|
+ var options = jsOptionsFunc(defaultOptions, hideLegend, setTitle, setBarMin, setBarMax, setStacked, setXtitle, setYtitle)(chart, chart.options, chartOptions);
|
|
|
var data = createDataTable(chart.data, "string");
|
|
|
chart.chart = new google.visualization.BarChart(chart.element);
|
|
|
resize(function () {
|
|
@@ -696,8 +880,8 @@
|
|
|
pointSize: 0,
|
|
|
areaOpacity: 0.5
|
|
|
};
|
|
|
- var options = jsOptions(chart.data, chart.options, chartOptions);
|
|
|
- var data = createDataTable(chart.data, chart.options.discrete ? "string" : "datetime");
|
|
|
+ var options = jsOptions(chart, chart.options, chartOptions);
|
|
|
+ var data = createDataTable(chart.data, chart.discrete ? "string" : "datetime");
|
|
|
chart.chart = new google.visualization.AreaChart(chart.element);
|
|
|
resize(function () {
|
|
|
chart.chart.draw(data, options);
|
|
@@ -730,7 +914,7 @@
|
|
|
this.renderScatterChart = function (chart) {
|
|
|
waitForLoaded(function () {
|
|
|
var chartOptions = {};
|
|
|
- var options = jsOptions(chart.data, chart.options, chartOptions);
|
|
|
+ var options = jsOptions(chart, chart.options, chartOptions);
|
|
|
var data = createDataTable(chart.data, "number");
|
|
|
|
|
|
chart.chart = new google.visualization.ScatterChart(chart.element);
|
|
@@ -777,7 +961,12 @@
|
|
|
|
|
|
var baseOptions = {
|
|
|
maintainAspectRatio: false,
|
|
|
- animation: false
|
|
|
+ animation: false,
|
|
|
+ tooltips: {
|
|
|
+ displayColors: false
|
|
|
+ },
|
|
|
+ legend: {},
|
|
|
+ title: {fontSize: 20, fontColor: "#333"}
|
|
|
};
|
|
|
|
|
|
var defaultOptions = {
|
|
@@ -808,8 +997,7 @@
|
|
|
ticks: {}
|
|
|
}
|
|
|
]
|
|
|
- },
|
|
|
- legend: {}
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
// http://there4.io/2012/05/02/google-chart-color-list/
|
|
@@ -819,28 +1007,40 @@
|
|
|
"#6633CC", "#E67300", "#8B0707", "#329262", "#5574A6", "#3B3EAC"
|
|
|
];
|
|
|
|
|
|
- var hideLegend = function (options) {
|
|
|
- options.legend.display = false;
|
|
|
+ var hideLegend = function (options, legend, hideLegend) {
|
|
|
+ if (legend !== undefined) {
|
|
|
+ options.legend.display = !!legend;
|
|
|
+ if (legend && legend !== true) {
|
|
|
+ options.legend.position = legend;
|
|
|
+ }
|
|
|
+ } else if (hideLegend) {
|
|
|
+ options.legend.display = false;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ var setTitle = function (options, title) {
|
|
|
+ options.title.display = true;
|
|
|
+ options.title.text = title;
|
|
|
};
|
|
|
|
|
|
var setMin = function (options, min) {
|
|
|
if (min !== null) {
|
|
|
- options.scales.yAxes[0].ticks.min = min;
|
|
|
+ options.scales.yAxes[0].ticks.min = toFloat(min);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
var setMax = function (options, max) {
|
|
|
- options.scales.yAxes[0].ticks.max = max;
|
|
|
+ options.scales.yAxes[0].ticks.max = toFloat(max);
|
|
|
};
|
|
|
|
|
|
var setBarMin = function (options, min) {
|
|
|
if (min !== null) {
|
|
|
- options.scales.xAxes[0].ticks.min = min;
|
|
|
+ options.scales.xAxes[0].ticks.min = toFloat(min);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
var setBarMax = function (options, max) {
|
|
|
- options.scales.xAxes[0].ticks.max = max;
|
|
|
+ options.scales.xAxes[0].ticks.max = toFloat(max);
|
|
|
};
|
|
|
|
|
|
var setStacked = function (options, stacked) {
|
|
@@ -859,9 +1059,13 @@
|
|
|
};
|
|
|
|
|
|
var drawChart = function(chart, type, data, options) {
|
|
|
- chart.element.innerHTML = "<canvas></canvas>";
|
|
|
- var ctx = chart.element.getElementsByTagName("CANVAS")[0];
|
|
|
+ if (chart.chart) {
|
|
|
+ chart.chart.destroy();
|
|
|
+ } else {
|
|
|
+ chart.element.innerHTML = "<canvas></canvas>";
|
|
|
+ }
|
|
|
|
|
|
+ var ctx = chart.element.getElementsByTagName("CANVAS")[0];
|
|
|
chart.chart = new Chart(ctx, {
|
|
|
type: type,
|
|
|
data: data,
|
|
@@ -890,7 +1094,7 @@
|
|
|
};
|
|
|
};
|
|
|
|
|
|
- var jsOptions = jsOptionsFunc(merge(baseOptions, defaultOptions), hideLegend, setMin, setMax, setStacked, setXtitle, setYtitle);
|
|
|
+ var jsOptions = jsOptionsFunc(merge(baseOptions, defaultOptions), hideLegend, setTitle, setMin, setMax, setStacked, setXtitle, setYtitle);
|
|
|
|
|
|
var createDataTable = function (chart, options, chartType) {
|
|
|
var datasets = [];
|
|
@@ -905,7 +1109,7 @@
|
|
|
var year = true;
|
|
|
var hour = true;
|
|
|
var minute = true;
|
|
|
- var detectType = (chartType === "line" || chartType === "area") && !chart.options.discrete;
|
|
|
+ var detectType = (chartType === "line" || chartType === "area") && !chart.discrete;
|
|
|
|
|
|
var series = chart.data;
|
|
|
|
|
@@ -958,7 +1162,8 @@
|
|
|
}
|
|
|
labels.push(value);
|
|
|
for (j = 0; j < series.length; j++) {
|
|
|
- rows2[j].push(rows[i][j]);
|
|
|
+ // Chart.js doesn't like undefined
|
|
|
+ rows2[j].push(rows[i][j] === undefined ? null : rows[i][j]);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -977,6 +1182,10 @@
|
|
|
borderWidth: 2
|
|
|
};
|
|
|
|
|
|
+ if (chart.options.curve === false) {
|
|
|
+ dataset.lineTension = 0;
|
|
|
+ }
|
|
|
+
|
|
|
datasets.push(merge(dataset, s.library || {}));
|
|
|
}
|
|
|
|
|
@@ -1006,7 +1215,8 @@
|
|
|
} else if (day || timeDiff > 10) {
|
|
|
options.scales.xAxes[0].time.unit = "day";
|
|
|
step = 1;
|
|
|
- } else if (hour) {
|
|
|
+ } else if (hour || timeDiff > 0.5) {
|
|
|
+ options.scales.xAxes[0].time.displayFormats = {hour: "MMM D, h a"};
|
|
|
options.scales.xAxes[0].time.unit = "hour";
|
|
|
step = 1 / 24.0;
|
|
|
} else if (minute) {
|
|
@@ -1015,7 +1225,6 @@
|
|
|
step = 1 / 24.0 / 60.0;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
if (step && timeDiff > 0) {
|
|
|
var unitStepSize = Math.ceil(timeDiff / step / (chart.element.offsetWidth / 100.0));
|
|
|
if (week && step === 1) {
|
|
@@ -1045,27 +1254,40 @@
|
|
|
};
|
|
|
|
|
|
this.renderLineChart = function (chart, chartType) {
|
|
|
- var areaOptions = {};
|
|
|
+ var chartOptions = {};
|
|
|
if (chartType === "area") {
|
|
|
// TODO fix area stacked
|
|
|
- // areaOptions.stacked = true;
|
|
|
+ // chartOptions.stacked = true;
|
|
|
}
|
|
|
// fix for https://github.com/chartjs/Chart.js/issues/2441
|
|
|
if (!chart.options.max && allZeros(chart.data)) {
|
|
|
- chart.options.max = 1;
|
|
|
+ chartOptions.max = 1;
|
|
|
}
|
|
|
|
|
|
- var options = jsOptions(chart.data, merge(areaOptions, chart.options));
|
|
|
+ var options = jsOptions(chart, merge(chartOptions, chart.options));
|
|
|
|
|
|
var data = createDataTable(chart, options, chartType || "line");
|
|
|
|
|
|
- options.scales.xAxes[0].type = chart.options.discrete ? "category" : "time";
|
|
|
+ options.scales.xAxes[0].type = chart.discrete ? "category" : "time";
|
|
|
|
|
|
drawChart(chart, "line", data, options);
|
|
|
};
|
|
|
|
|
|
this.renderPieChart = function (chart) {
|
|
|
- var options = merge(baseOptions, chart.options.library || {});
|
|
|
+ var options = merge({}, baseOptions);
|
|
|
+ if (chart.options.donut) {
|
|
|
+ options.cutoutPercentage = 50;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ("legend" in chart.options) {
|
|
|
+ hideLegend(options, chart.options.legend);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (chart.options.title) {
|
|
|
+ setTitle(options, chart.options.title);
|
|
|
+ }
|
|
|
+
|
|
|
+ options = merge(options, chart.options.library || {});
|
|
|
|
|
|
var labels = [];
|
|
|
var values = [];
|
|
@@ -1091,9 +1313,9 @@
|
|
|
this.renderColumnChart = function (chart, chartType) {
|
|
|
var options;
|
|
|
if (chartType === "bar") {
|
|
|
- options = jsOptionsFunc(merge(baseOptions, defaultOptions), hideLegend, setBarMin, setBarMax, setStacked)(chart.data, chart.options);
|
|
|
+ options = jsOptionsFunc(merge(baseOptions, defaultOptions), hideLegend, setTitle, setBarMin, setBarMax, setStacked, setXtitle, setYtitle)(chart, chart.options);
|
|
|
} else {
|
|
|
- options = jsOptions(chart.data, chart.options);
|
|
|
+ options = jsOptions(chart, chart.options);
|
|
|
}
|
|
|
var data = createDataTable(chart, options, "column");
|
|
|
setLabelSize(chart, data, options);
|
|
@@ -1109,15 +1331,57 @@
|
|
|
this.renderBarChart = function (chart) {
|
|
|
self.renderColumnChart(chart, "bar");
|
|
|
};
|
|
|
+
|
|
|
+ this.renderScatterChart = function (chart) {
|
|
|
+ var options = jsOptions(chart, chart.options);
|
|
|
+
|
|
|
+ var colors = chart.options.colors || defaultColors;
|
|
|
+
|
|
|
+ var datasets = [];
|
|
|
+ var series = chart.data;
|
|
|
+ for (var i = 0; i < series.length; i++) {
|
|
|
+ var s = series[i];
|
|
|
+ var d = [];
|
|
|
+ for (var j = 0; j < s.data.length; j++) {
|
|
|
+ d.push({
|
|
|
+ x: toFloat(s.data[j][0]),
|
|
|
+ y: toFloat(s.data[j][1])
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ datasets.push({
|
|
|
+ label: s.name,
|
|
|
+ showLine: false,
|
|
|
+ data: d,
|
|
|
+ borderColor: colors[i],
|
|
|
+ backgroundColor: colors[i],
|
|
|
+ pointBackgroundColor: colors[i]
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ var data = {datasets: datasets};
|
|
|
+
|
|
|
+ options.scales.xAxes[0].type = "linear";
|
|
|
+ options.scales.xAxes[0].position = "bottom";
|
|
|
+
|
|
|
+ drawChart(chart, "line", data, options);
|
|
|
+ };
|
|
|
};
|
|
|
|
|
|
adapters.unshift(ChartjsAdapter);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ function renderChart(chartType, chart) {
|
|
|
+ callAdapter(chartType, chart);
|
|
|
+ if (chart.options.download && !chart.downloadAttached && chart.adapter === "chartjs") {
|
|
|
+ addDownloadButton(chart);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// TODO remove chartType if cross-browser way
|
|
|
// to get the name of the chart class
|
|
|
- function renderChart(chartType, chart) {
|
|
|
+ function callAdapter(chartType, chart) {
|
|
|
var i, adapter, fnName, adapterName;
|
|
|
fnName = "render" + chartType;
|
|
|
adapterName = chart.options.adapter;
|
|
@@ -1127,6 +1391,7 @@
|
|
|
for (i = 0; i < adapters.length; i++) {
|
|
|
adapter = adapters[i];
|
|
|
if ((!adapterName || adapterName === adapter.name) && isFunction(adapter[fnName])) {
|
|
|
+ chart.adapter = adapter.name;
|
|
|
return adapter[fnName](chart);
|
|
|
}
|
|
|
}
|
|
@@ -1212,20 +1477,25 @@
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- function processSeries(series, opts, keyType) {
|
|
|
+ function processSeries(chart, keyType) {
|
|
|
var i;
|
|
|
|
|
|
+ var opts = chart.options;
|
|
|
+ var series = chart.rawData;
|
|
|
+
|
|
|
// see if one series or multiple
|
|
|
if (!isArray(series) || typeof series[0] !== "object" || isArray(series[0])) {
|
|
|
series = [{name: opts.label || "Value", data: series}];
|
|
|
- opts.hideLegend = true;
|
|
|
+ chart.hideLegend = true;
|
|
|
} else {
|
|
|
- opts.hideLegend = false;
|
|
|
+ chart.hideLegend = false;
|
|
|
}
|
|
|
if ((opts.discrete === null || opts.discrete === undefined)) {
|
|
|
- opts.discrete = detectDiscrete(series);
|
|
|
+ chart.discrete = detectDiscrete(series);
|
|
|
+ } else {
|
|
|
+ chart.discrete = opts.discrete;
|
|
|
}
|
|
|
- if (opts.discrete) {
|
|
|
+ if (chart.discrete) {
|
|
|
keyType = "string";
|
|
|
}
|
|
|
|
|
@@ -1237,17 +1507,17 @@
|
|
|
return series;
|
|
|
}
|
|
|
|
|
|
- function processSimple(data) {
|
|
|
- var perfectData = toArr(data), i;
|
|
|
+ function processSimple(chart) {
|
|
|
+ var perfectData = toArr(chart.rawData), i;
|
|
|
for (i = 0; i < perfectData.length; i++) {
|
|
|
perfectData[i] = [toStr(perfectData[i][0]), toFloat(perfectData[i][1])];
|
|
|
}
|
|
|
return perfectData;
|
|
|
}
|
|
|
|
|
|
- function processTime(data)
|
|
|
+ function processTime(chart)
|
|
|
{
|
|
|
- var i;
|
|
|
+ var i, data = chart.rawData;
|
|
|
for (i = 0; i < data.length; i++) {
|
|
|
data[i][1] = toDate(data[i][1]);
|
|
|
data[i][2] = toDate(data[i][2]);
|
|
@@ -1256,46 +1526,26 @@
|
|
|
}
|
|
|
|
|
|
function processLineData(chart) {
|
|
|
- chart.data = processSeries(chart.data, chart.options, "datetime");
|
|
|
- renderChart("LineChart", chart);
|
|
|
+ return processSeries(chart, "datetime");
|
|
|
}
|
|
|
|
|
|
function processColumnData(chart) {
|
|
|
- chart.data = processSeries(chart.data, chart.options, "string");
|
|
|
- renderChart("ColumnChart", chart);
|
|
|
- }
|
|
|
-
|
|
|
- function processPieData(chart) {
|
|
|
- chart.data = processSimple(chart.data);
|
|
|
- renderChart("PieChart", chart);
|
|
|
+ return processSeries(chart, "string");
|
|
|
}
|
|
|
|
|
|
function processBarData(chart) {
|
|
|
- chart.data = processSeries(chart.data, chart.options, "string");
|
|
|
- renderChart("BarChart", chart);
|
|
|
+ return processSeries(chart, "string");
|
|
|
}
|
|
|
|
|
|
function processAreaData(chart) {
|
|
|
- chart.data = processSeries(chart.data, chart.options, "datetime");
|
|
|
- renderChart("AreaChart", chart);
|
|
|
- }
|
|
|
-
|
|
|
- function processGeoData(chart) {
|
|
|
- chart.data = processSimple(chart.data);
|
|
|
- renderChart("GeoChart", chart);
|
|
|
+ return processSeries(chart, "datetime");
|
|
|
}
|
|
|
|
|
|
function processScatterData(chart) {
|
|
|
- chart.data = processSeries(chart.data, chart.options, "number");
|
|
|
- renderChart("ScatterChart", chart);
|
|
|
- }
|
|
|
-
|
|
|
- function processTimelineData(chart) {
|
|
|
- chart.data = processTime(chart.data);
|
|
|
- renderChart("Timeline", chart);
|
|
|
+ return processSeries(chart, "number");
|
|
|
}
|
|
|
|
|
|
- function setElement(chart, element, dataSource, opts, callback) {
|
|
|
+ function createChart(chartType, chart, element, dataSource, opts, processData) {
|
|
|
var elementId;
|
|
|
if (typeof element === "string") {
|
|
|
elementId = element;
|
|
@@ -1304,53 +1554,135 @@
|
|
|
throw new Error("No element with id " + elementId);
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
chart.element = element;
|
|
|
- chart.options = opts || {};
|
|
|
+ opts = merge(Chartkick.options, opts || {});
|
|
|
+ chart.options = opts;
|
|
|
chart.dataSource = dataSource;
|
|
|
+
|
|
|
+ if (!processData) {
|
|
|
+ processData = function (chart) {
|
|
|
+ return chart.rawData;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // getters
|
|
|
chart.getElement = function () {
|
|
|
return element;
|
|
|
};
|
|
|
+ chart.getDataSource = function () {
|
|
|
+ return chart.dataSource;
|
|
|
+ };
|
|
|
chart.getData = function () {
|
|
|
return chart.data;
|
|
|
};
|
|
|
chart.getOptions = function () {
|
|
|
- return opts || {};
|
|
|
+ return chart.options;
|
|
|
};
|
|
|
chart.getChartObject = function () {
|
|
|
return chart.chart;
|
|
|
};
|
|
|
+ chart.getAdapter = function () {
|
|
|
+ return chart.adapter;
|
|
|
+ };
|
|
|
+
|
|
|
+ var callback = function () {
|
|
|
+ chart.data = processData(chart);
|
|
|
+ renderChart(chartType, chart);
|
|
|
+ };
|
|
|
+
|
|
|
+ // functions
|
|
|
+ chart.updateData = function (dataSource, options) {
|
|
|
+ chart.dataSource = dataSource;
|
|
|
+ if (options) {
|
|
|
+ chart.options = merge(Chartkick.options, options);
|
|
|
+ }
|
|
|
+ fetchDataSource(chart, callback, dataSource);
|
|
|
+ };
|
|
|
+ chart.setOptions = function (options) {
|
|
|
+ chart.options = merge(Chartkick.options, options);
|
|
|
+ chart.redraw();
|
|
|
+ };
|
|
|
+ chart.redraw = function() {
|
|
|
+ fetchDataSource(chart, callback, chart.rawData);
|
|
|
+ };
|
|
|
+ chart.refreshData = function () {
|
|
|
+ if (typeof dataSource === "string") {
|
|
|
+ // prevent browser from caching
|
|
|
+ var sep = dataSource.indexOf("?") === -1 ? "?" : "&";
|
|
|
+ var url = dataSource + sep + "_=" + (new Date()).getTime();
|
|
|
+ fetchDataSource(chart, callback, url);
|
|
|
+ }
|
|
|
+ };
|
|
|
+ chart.stopRefresh = function () {
|
|
|
+ if (chart.intervalId) {
|
|
|
+ clearInterval(chart.intervalId);
|
|
|
+ }
|
|
|
+ };
|
|
|
+ chart.toImage = function () {
|
|
|
+ if (chart.adapter === "chartjs") {
|
|
|
+ return chart.chart.toBase64Image();
|
|
|
+ } else {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
Chartkick.charts[element.id] = chart;
|
|
|
- fetchDataSource(chart, callback);
|
|
|
+
|
|
|
+ fetchDataSource(chart, callback, dataSource);
|
|
|
+
|
|
|
+ if (opts.refresh) {
|
|
|
+ chart.intervalId = setInterval( function () {
|
|
|
+ chart.refreshData();
|
|
|
+ }, opts.refresh * 1000);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// define classes
|
|
|
|
|
|
Chartkick = {
|
|
|
- LineChart: function (element, dataSource, opts) {
|
|
|
- setElement(this, element, dataSource, opts, processLineData);
|
|
|
+ LineChart: function (element, dataSource, options) {
|
|
|
+ createChart("LineChart", this, element, dataSource, options, processLineData);
|
|
|
+ },
|
|
|
+ PieChart: function (element, dataSource, options) {
|
|
|
+ createChart("PieChart", this, element, dataSource, options, processSimple);
|
|
|
},
|
|
|
- PieChart: function (element, dataSource, opts) {
|
|
|
- setElement(this, element, dataSource, opts, processPieData);
|
|
|
+ ColumnChart: function (element, dataSource, options) {
|
|
|
+ createChart("ColumnChart", this, element, dataSource, options, processColumnData);
|
|
|
},
|
|
|
- ColumnChart: function (element, dataSource, opts) {
|
|
|
- setElement(this, element, dataSource, opts, processColumnData);
|
|
|
+ BarChart: function (element, dataSource, options) {
|
|
|
+ createChart("BarChart", this, element, dataSource, options, processBarData);
|
|
|
},
|
|
|
- BarChart: function (element, dataSource, opts) {
|
|
|
- setElement(this, element, dataSource, opts, processBarData);
|
|
|
+ AreaChart: function (element, dataSource, options) {
|
|
|
+ createChart("AreaChart", this, element, dataSource, options, processAreaData);
|
|
|
},
|
|
|
- AreaChart: function (element, dataSource, opts) {
|
|
|
- setElement(this, element, dataSource, opts, processAreaData);
|
|
|
+ GeoChart: function (element, dataSource, options) {
|
|
|
+ createChart("GeoChart", this, element, dataSource, options, processSimple);
|
|
|
},
|
|
|
- GeoChart: function (element, dataSource, opts) {
|
|
|
- setElement(this, element, dataSource, opts, processGeoData);
|
|
|
+ ScatterChart: function (element, dataSource, options) {
|
|
|
+ createChart("ScatterChart", this, element, dataSource, options, processScatterData);
|
|
|
},
|
|
|
- ScatterChart: function (element, dataSource, opts) {
|
|
|
- setElement(this, element, dataSource, opts, processScatterData);
|
|
|
+ Timeline: function (element, dataSource, options) {
|
|
|
+ createChart("Timeline", this, element, dataSource, options, processTime);
|
|
|
},
|
|
|
- Timeline: function (element, dataSource, opts) {
|
|
|
- setElement(this, element, dataSource, opts, processTimelineData);
|
|
|
+ charts: {},
|
|
|
+ configure: function (options) {
|
|
|
+ for (var key in options) {
|
|
|
+ if (options.hasOwnProperty(key)) {
|
|
|
+ config[key] = options[key];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ eachChart: function (callback) {
|
|
|
+ for (var chartId in Chartkick.charts) {
|
|
|
+ if (Chartkick.charts.hasOwnProperty(chartId)) {
|
|
|
+ callback(Chartkick.charts[chartId]);
|
|
|
+ }
|
|
|
+ }
|
|
|
},
|
|
|
- charts: {}
|
|
|
+ options: {},
|
|
|
+ adapters: adapters,
|
|
|
+ createChart: createChart
|
|
|
};
|
|
|
|
|
|
if (typeof module === "object" && typeof module.exports === "object") {
|