/* * Chartkick.js * Create beautiful charts with one line of JavaScript * https://github.com/ankane/chartkick.js * v3.0.1 * MIT License */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global.Chartkick = factory()); }(this, (function () { 'use strict'; function isArray(variable) { return Object.prototype.toString.call(variable) === "[object Array]"; } function isFunction(variable) { return variable instanceof Function; } function isPlainObject(variable) { return !isFunction(variable) && variable instanceof Object; } // https://github.com/madrobby/zepto/blob/master/src/zepto.js function extend(target, source) { var key; for (key in source) { if (isPlainObject(source[key]) || isArray(source[key])) { if (isPlainObject(source[key]) && !isPlainObject(target[key])) { target[key] = {}; } if (isArray(source[key]) && !isArray(target[key])) { target[key] = []; } extend(target[key], source[key]); } else if (source[key] !== undefined) { target[key] = source[key]; } } } function merge(obj1, obj2) { var target = {}; extend(target, obj1); extend(target, obj2); return target; } var DATE_PATTERN = /^(\d\d\d\d)(-)?(\d\d)(-)?(\d\d)$/i; // https://github.com/Do/iso8601.js var ISO8601_PATTERN = /(\d\d\d\d)(-)?(\d\d)(-)?(\d\d)(T)?(\d\d)(:)?(\d\d)?(:)?(\d\d)?([.,]\d+)?($|Z|([+-])(\d\d)(:)?(\d\d)?)/i; var DECIMAL_SEPARATOR = String(1.5).charAt(1); 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]") { return input; } if (type !== "[object String]") { return; } matches = input.match(ISO8601_PATTERN); if (matches) { year = parseInt(matches[1], 10); month = parseInt(matches[3], 10) - 1; day = parseInt(matches[5], 10); hour = parseInt(matches[7], 10); minutes = matches[9] ? parseInt(matches[9], 10) : 0; seconds = matches[11] ? parseInt(matches[11], 10) : 0; milliseconds = matches[12] ? parseFloat(DECIMAL_SEPARATOR + matches[12].slice(1)) * 1000 : 0; result = Date.UTC(year, month, day, hour, minutes, seconds, milliseconds); if (matches[13] && matches[14]) { offset = matches[15] * 60; if (matches[17]) { offset += parseInt(matches[17], 10); } offset *= matches[14] === "-" ? -1 : 1; result -= offset * 60 * 1000; } return new Date(result); } } // end iso8601.js function negativeValues(series) { var i, j, data; for (i = 0; i < series.length; i++) { data = series[i].data; for (j = 0; j < data.length; j++) { if (data[j][1] < 0) { return true; } } } return false; } function toStr(n) { return "" + n; } function toFloat(n) { return parseFloat(n); } function toDate(n) { var matches, year, month, day; if (typeof n !== "object") { if (typeof n === "number") { n = new Date(n * 1000); // ms } else { n = toStr(n); if ((matches = n.match(DATE_PATTERN))) { year = parseInt(matches[1], 10); month = parseInt(matches[3], 10) - 1; day = parseInt(matches[5], 10); return new Date(year, month, day); } else { // str // try our best to get the str into iso8601 // TODO be smarter about this var str = n.replace(/ /, "T").replace(" ", "").replace("UTC", "Z"); n = parseISO8601(str) || new Date(n); } } } return n; } function toArr(n) { if (!isArray(n)) { var arr = [], i; for (i in n) { if (n.hasOwnProperty(i)) { arr.push([i, n[i]]); } } n = arr; } return n; } 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 || {}); if (chart.hideLegend || "legend" in opts) { hideLegend(options, opts.legend, chart.hideLegend); } if (opts.title) { setTitle(options, opts.title); } // min if ("min" in opts) { setMin(options, opts.min); } else if (!negativeValues(series)) { setMin(options, 0); } // max if (opts.max) { setMax(options, opts.max); } if ("stacked" in opts) { setStacked(options, opts.stacked); } if (opts.colors) { options.colors = opts.colors; } if (opts.xtitle) { setXtitle(options, opts.xtitle); } if (opts.ytitle) { setYtitle(options, opts.ytitle); } // merge library last options = merge(options, opts.library || {}); return options; }; } function sortByTime(a, b) { return a[0].getTime() - b[0].getTime(); } function sortByNumberSeries(a, b) { return a[0] - b[0]; } function sortByNumber(a, b) { return a - b; } function isMinute(d) { return d.getMilliseconds() === 0 && d.getSeconds() === 0; } function isHour(d) { return isMinute(d) && d.getMinutes() === 0; } function isDay(d) { return isHour(d) && d.getHours() === 0; } function isWeek(d, dayOfWeek) { return isDay(d) && d.getDay() === dayOfWeek; } function isMonth(d) { return isDay(d) && d.getDate() === 1; } function isYear(d) { return isMonth(d) && d.getMonth() === 0; } function isDate(obj) { return !isNaN(toDate(obj)) && toStr(obj).length >= 6; } function isNumber(obj) { return typeof obj === "number"; } function formatValue(pre, value, options) { pre = pre || ""; if (options.prefix) { if (value < 0) { value = value * -1; pre += "-"; } pre += options.prefix; } if (options.thousands || options.decimal) { value = toStr(value); var parts = value.split("."); value = parts[0]; if (options.thousands) { value = value.replace(/\B(?=(\d{3})+(?!\d))/g, options.thousands); } if (parts.length > 1) { value += (options.decimal || ".") + parts[1]; } } return pre + value + (options.suffix || ""); } function allZeros(data) { var i, j, d; for (i = 0; i < data.length; i++) { d = data[i].data; for (j = 0; j < d.length; j++) { if (d[j][1] != 0) { return false; } } } return true; } var baseOptions = { maintainAspectRatio: false, animation: false, tooltips: { displayColors: false, callbacks: {} }, legend: {}, title: {fontSize: 20, fontColor: "#333"} }; var defaultOptions = { scales: { yAxes: [ { ticks: { maxTicksLimit: 4 }, scaleLabel: { fontSize: 16, // fontStyle: "bold", fontColor: "#333" } } ], xAxes: [ { gridLines: { drawOnChartArea: false }, scaleLabel: { fontSize: 16, // fontStyle: "bold", fontColor: "#333" }, time: {}, ticks: {} } ] } }; // http://there4.io/2012/05/02/google-chart-color-list/ var defaultColors = [ "#3366CC", "#DC3912", "#FF9900", "#109618", "#990099", "#3B3EAC", "#0099C6", "#DD4477", "#66AA00", "#B82E2E", "#316395", "#994499", "#22AA99", "#AAAA11", "#6633CC", "#E67300", "#8B0707", "#329262", "#5574A6", "#651067" ]; 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 = toFloat(min); } }; var setMax = function (options, max) { options.scales.yAxes[0].ticks.max = toFloat(max); }; var setBarMin = function (options, min) { if (min !== null) { options.scales.xAxes[0].ticks.min = toFloat(min); } }; var setBarMax = function (options, max) { options.scales.xAxes[0].ticks.max = toFloat(max); }; var setStacked = function (options, stacked) { options.scales.xAxes[0].stacked = !!stacked; options.scales.yAxes[0].stacked = !!stacked; }; var setXtitle = function (options, title) { options.scales.xAxes[0].scaleLabel.display = true; options.scales.xAxes[0].scaleLabel.labelString = title; }; var setYtitle = function (options, title) { options.scales.yAxes[0].scaleLabel.display = true; options.scales.yAxes[0].scaleLabel.labelString = title; }; // https://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb var addOpacity = function(hex, opacity) { var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return result ? "rgba(" + parseInt(result[1], 16) + ", " + parseInt(result[2], 16) + ", " + parseInt(result[3], 16) + ", " + opacity + ")" : hex; }; var setLabelSize = function (chart, data, options) { var maxLabelSize = Math.ceil(chart.element.offsetWidth / 4.0 / data.labels.length); if (maxLabelSize > 25) { maxLabelSize = 25; } else if (maxLabelSize < 10) { maxLabelSize = 10; } if (!options.scales.xAxes[0].ticks.callback) { options.scales.xAxes[0].ticks.callback = function (value) { value = toStr(value); if (value.length > maxLabelSize) { return value.substring(0, maxLabelSize - 2) + "..."; } else { return value; } }; } }; var setFormatOptions = function(chart, options, chartType) { var formatOptions = { prefix: chart.options.prefix, suffix: chart.options.suffix, thousands: chart.options.thousands, decimal: chart.options.decimal }; if (chartType !== "pie") { var myAxes = options.scales.yAxes; if (chartType === "bar") { myAxes = options.scales.xAxes; } if (!myAxes[0].ticks.callback) { myAxes[0].ticks.callback = function (value) { return formatValue("", value, formatOptions); }; } } if (!options.tooltips.callbacks.label) { if (chartType === "scatter") { options.tooltips.callbacks.label = function (item, data) { var label = data.datasets[item.datasetIndex].label || ''; if (label) { label += ': '; } return label + '(' + item.xLabel + ', ' + item.yLabel + ')'; }; } else if (chartType === "bubble") { options.tooltips.callbacks.label = function (item, data) { var label = data.datasets[item.datasetIndex].label || ''; if (label) { label += ': '; } var dataPoint = data.datasets[item.datasetIndex].data[item.index]; return label + '(' + item.xLabel + ', ' + item.yLabel + ', ' + dataPoint.v + ')'; }; } else if (chartType === "pie") { // need to use separate label for pie charts options.tooltips.callbacks.label = function (tooltipItem, data) { var dataLabel = data.labels[tooltipItem.index]; var value = ': '; if (isArray(dataLabel)) { // show value on first line of multiline label // need to clone because we are changing the value dataLabel = dataLabel.slice(); dataLabel[0] += value; } else { dataLabel += value; } return formatValue(dataLabel, data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index], formatOptions); }; } else { var valueLabel = chartType === "bar" ? "xLabel" : "yLabel"; options.tooltips.callbacks.label = function (tooltipItem, data) { var label = data.datasets[tooltipItem.datasetIndex].label || ''; if (label) { label += ': '; } return formatValue(label, tooltipItem[valueLabel], formatOptions); }; } } }; var jsOptions = jsOptionsFunc(merge(baseOptions, defaultOptions), hideLegend, setTitle, setMin, setMax, setStacked, setXtitle, setYtitle); var createDataTable = function (chart, options, chartType) { var datasets = []; var labels = []; var colors = chart.options.colors || defaultColors; var day = true; var week = true; var dayOfWeek; var month = true; var year = true; var hour = true; var minute = true; var series = chart.data; var max = 0; if (chartType === "bubble") { for (var i$1 = 0; i$1 < series.length; i$1++) { var s$1 = series[i$1]; for (var j$1 = 0; j$1 < s$1.data.length; j$1++) { if (s$1.data[j$1][2] > max) { max = s$1.data[j$1][2]; } } } } var i, j, s, d, key, rows = [], rows2 = []; if (chartType === "bar" || chartType === "column" || (chart.xtype !== "number" && chart.xtype !== "bubble")) { var sortedLabels = []; for (i = 0; i < series.length; i++) { s = series[i]; for (j = 0; j < s.data.length; j++) { d = s.data[j]; key = chart.xtype == "datetime" ? d[0].getTime() : d[0]; if (!rows[key]) { rows[key] = new Array(series.length); } rows[key][i] = toFloat(d[1]); if (sortedLabels.indexOf(key) === -1) { sortedLabels.push(key); } } } if (chart.xtype === "datetime" || chart.xtype === "number") { sortedLabels.sort(sortByNumber); } for (j = 0; j < series.length; j++) { rows2.push([]); } var value; var k; for (k = 0; k < sortedLabels.length; k++) { i = sortedLabels[k]; if (chart.xtype === "datetime") { value = new Date(toFloat(i)); // TODO make this efficient day = day && isDay(value); if (!dayOfWeek) { dayOfWeek = value.getDay(); } week = week && isWeek(value, dayOfWeek); month = month && isMonth(value); year = year && isYear(value); hour = hour && isHour(value); minute = minute && isMinute(value); } else { value = i; } labels.push(value); for (j = 0; j < series.length; j++) { // Chart.js doesn't like undefined rows2[j].push(rows[i][j] === undefined ? null : rows[i][j]); } } } else { for (var i$2 = 0; i$2 < series.length; i$2++) { var s$2 = series[i$2]; var d$1 = []; for (var j$2 = 0; j$2 < s$2.data.length; j$2++) { var point = { x: toFloat(s$2.data[j$2][0]), y: toFloat(s$2.data[j$2][1]) }; if (chartType === "bubble") { point.r = toFloat(s$2.data[j$2][2]) * 20 / max; // custom attribute, for tooltip point.v = s$2.data[j$2][2]; } d$1.push(point); } rows2.push(d$1); } } for (i = 0; i < series.length; i++) { s = series[i]; var color = s.color || colors[i]; var backgroundColor = chartType !== "line" ? addOpacity(color, 0.5) : color; var dataset = { label: s.name || "", data: rows2[i], fill: chartType === "area", borderColor: color, backgroundColor: backgroundColor, pointBackgroundColor: color, borderWidth: 2, pointHoverBackgroundColor: color }; if (s.stack) { dataset.stack = s.stack; } if (chart.options.curve === false) { dataset.lineTension = 0; } if (chart.options.points === false) { dataset.pointRadius = 0; dataset.pointHitRadius = 5; } dataset = merge(dataset, chart.options.dataset || {}); dataset = merge(dataset, s.library || {}); dataset = merge(dataset, s.dataset || {}); datasets.push(dataset); } if (chart.xtype === "datetime" && labels.length > 0) { var minTime = labels[0].getTime(); var maxTime = labels[0].getTime(); for (i = 1; i < labels.length; i++) { var value$1 = labels[i].getTime(); if (value$1 < minTime) { minTime = value$1; } if (value$1 > maxTime) { maxTime = value$1; } } var timeDiff = (maxTime - minTime) / (86400 * 1000.0); if (!options.scales.xAxes[0].time.unit) { var step; if (year || timeDiff > 365 * 10) { options.scales.xAxes[0].time.unit = "year"; step = 365; } else if (month || timeDiff > 30 * 10) { options.scales.xAxes[0].time.unit = "month"; step = 30; } else if (day || timeDiff > 10) { options.scales.xAxes[0].time.unit = "day"; step = 1; } 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) { options.scales.xAxes[0].time.displayFormats = {minute: "h:mm a"}; options.scales.xAxes[0].time.unit = "minute"; 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) { unitStepSize = Math.ceil(unitStepSize / 7.0) * 7; } options.scales.xAxes[0].time.unitStepSize = unitStepSize; } } if (!options.scales.xAxes[0].time.tooltipFormat) { if (day) { options.scales.xAxes[0].time.tooltipFormat = "ll"; } else if (hour) { options.scales.xAxes[0].time.tooltipFormat = "MMM D, h a"; } else if (minute) { options.scales.xAxes[0].time.tooltipFormat = "h:mm a"; } } } var data = { labels: labels, datasets: datasets }; return data; }; var defaultExport = function defaultExport(library) { this.name = "chartjs"; this.library = library; }; defaultExport.prototype.renderLineChart = function renderLineChart (chart, chartType) { var chartOptions = {}; // fix for https://github.com/chartjs/Chart.js/issues/2441 if (!chart.options.max && allZeros(chart.data)) { chartOptions.max = 1; } var options = jsOptions(chart, merge(chartOptions, chart.options)); setFormatOptions(chart, options, chartType); var data = createDataTable(chart, options, chartType || "line"); if (chart.xtype === "number") { options.scales.xAxes[0].type = "linear"; options.scales.xAxes[0].position = "bottom"; } else { options.scales.xAxes[0].type = chart.xtype === "string" ? "category" : "time"; } this.drawChart(chart, "line", data, options); }; defaultExport.prototype.renderPieChart = function renderPieChart (chart) { 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 || {}); setFormatOptions(chart, options, "pie"); var labels = []; var values = []; for (var i = 0; i < chart.data.length; i++) { var point = chart.data[i]; labels.push(point[0]); values.push(point[1]); } var dataset = { data: values, backgroundColor: chart.options.colors || defaultColors }; dataset = merge(dataset, chart.options.dataset || {}); var data = { labels: labels, datasets: [dataset] }; this.drawChart(chart, "pie", data, options); }; defaultExport.prototype.renderColumnChart = function renderColumnChart (chart, chartType) { var options; if (chartType === "bar") { options = jsOptionsFunc(merge(baseOptions, defaultOptions), hideLegend, setTitle, setBarMin, setBarMax, setStacked, setXtitle, setYtitle)(chart, chart.options); } else { options = jsOptions(chart, chart.options); } setFormatOptions(chart, options, chartType); var data = createDataTable(chart, options, "column"); if (chartType !== "bar") { setLabelSize(chart, data, options); } this.drawChart(chart, (chartType === "bar" ? "horizontalBar" : "bar"), data, options); }; defaultExport.prototype.renderAreaChart = function renderAreaChart (chart) { this.renderLineChart(chart, "area"); }; defaultExport.prototype.renderBarChart = function renderBarChart (chart) { this.renderColumnChart(chart, "bar"); }; defaultExport.prototype.renderScatterChart = function renderScatterChart (chart, chartType) { chartType = chartType || "scatter"; var options = jsOptions(chart, chart.options); setFormatOptions(chart, options, chartType); if (!("showLines" in options)) { options.showLines = false; } var data = createDataTable(chart, options, chartType); options.scales.xAxes[0].type = "linear"; options.scales.xAxes[0].position = "bottom"; this.drawChart(chart, chartType, data, options); }; defaultExport.prototype.renderBubbleChart = function renderBubbleChart (chart) { this.renderScatterChart(chart, "bubble"); }; defaultExport.prototype.destroy = function destroy (chart) { if (chart.chart) { chart.chart.destroy(); } }; defaultExport.prototype.drawChart = function drawChart (chart, type, data, options) { this.destroy(chart); var chartOptions = { type: type, data: data, options: options }; if (chart.options.code) { window.console.log("new Chart(ctx, " + JSON.stringify(chartOptions) + ");"); } chart.element.innerHTML = ""; var ctx = chart.element.getElementsByTagName("CANVAS")[0]; chart.chart = new this.library(ctx, chartOptions); }; var defaultOptions$1 = { chart: {}, xAxis: { title: { text: null }, labels: { style: { fontSize: "12px" } } }, yAxis: { title: { text: null }, labels: { style: { fontSize: "12px" } } }, title: { text: null }, credits: { enabled: false }, legend: { borderWidth: 0 }, tooltip: { style: { fontSize: "12px" } }, plotOptions: { areaspline: {}, series: { marker: {} } } }; var hideLegend$1 = 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$1 = function (options, title) { options.title.text = title; }; var setMin$1 = function (options, min) { options.yAxis.min = min; }; var setMax$1 = function (options, max) { options.yAxis.max = max; }; var setStacked$1 = function (options, stacked) { options.plotOptions.series.stacking = stacked ? (stacked === true ? "normal" : stacked) : null; }; var setXtitle$1 = function (options, title) { options.xAxis.title.text = title; }; var setYtitle$1 = function (options, title) { options.yAxis.title.text = title; }; var jsOptions$1 = jsOptionsFunc(defaultOptions$1, hideLegend$1, setTitle$1, setMin$1, setMax$1, setStacked$1, setXtitle$1, setYtitle$1); var setFormatOptions$1 = function(chart, options, chartType) { var formatOptions = { prefix: chart.options.prefix, suffix: chart.options.suffix, thousands: chart.options.thousands, decimal: chart.options.decimal }; if (chartType !== "pie" && !options.yAxis.labels.formatter) { options.yAxis.labels.formatter = function () { return formatValue("", this.value, formatOptions); }; } if (!options.tooltip.pointFormatter) { options.tooltip.pointFormatter = function () { return ' 0) { return false; } } return true; } } function renderChart(chartType, chart) { if (chart.options.messages && chart.options.messages.empty && dataEmpty(chart.data, chartType)) { setText(chart.element, chart.options.messages.empty); } else { 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 callAdapter(chartType, chart) { var i, adapter, fnName, adapterName; fnName = "render" + chartType; adapterName = chart.options.adapter; loadAdapters(); for (i = 0; i < adapters.length; i++) { adapter = adapters[i]; if ((!adapterName || adapterName === adapter.name) && isFunction(adapter[fnName])) { chart.adapter = adapter.name; chart.__adapterObject = adapter; return adapter[fnName](chart); } } if (adapters.length > 0) { throw new Error("No charting library found for " + chartType); } else { throw new Error("No charting libraries found - be sure to include one before your charts"); } } // process data var toFormattedKey = function (key, keyType) { if (keyType === "number") { key = toFloat(key); } else if (keyType === "datetime") { key = toDate(key); } else { key = toStr(key); } return key; }; var formatSeriesData = function (data, keyType) { var r = [], key, j; for (j = 0; j < data.length; j++) { if (keyType === "bubble") { r.push([toFloat(data[j][0]), toFloat(data[j][1]), toFloat(data[j][2])]); } else { key = toFormattedKey(data[j][0], keyType); r.push([key, toFloat(data[j][1])]); } } if (keyType === "datetime") { r.sort(sortByTime); } else if (keyType === "number") { r.sort(sortByNumberSeries); } return r; }; function detectXType(series, noDatetime) { if (detectXTypeWithFunction(series, isNumber)) { return "number"; } else if (!noDatetime && detectXTypeWithFunction(series, isDate)) { return "datetime"; } else { return "string"; } } function detectXTypeWithFunction(series, func) { var i, j, data; for (i = 0; i < series.length; i++) { data = toArr(series[i].data); for (j = 0; j < data.length; j++) { if (!func(data[j][0])) { return false; } } } return true; } // creates a shallow copy of each element of the array // elements are expected to be objects function copySeries(series) { var newSeries = [], i, j; for (i = 0; i < series.length; i++) { var copy = {}; for (j in series[i]) { if (series[i].hasOwnProperty(j)) { copy[j] = series[i][j]; } } newSeries.push(copy); } return newSeries; } function processSeries(chart, keyType, noDatetime) { 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, data: series}]; chart.hideLegend = true; } else { chart.hideLegend = false; } chart.xtype = keyType ? keyType : (opts.discrete ? "string" : detectXType(series, noDatetime)); // right format series = copySeries(series); for (i = 0; i < series.length; i++) { series[i].data = formatSeriesData(toArr(series[i].data), chart.xtype); } return series; } 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; } // define classes var Chart = function Chart(element, dataSource, options) { var elementId; if (typeof element === "string") { elementId = element; element = document.getElementById(element); if (!element) { throw new Error("No element with id " + elementId); } } this.element = element; this.options = merge(Chartkick.options, options || {}); this.dataSource = dataSource; Chartkick.charts[element.id] = this; fetchDataSource(this, dataSource); if (this.options.refresh) { this.startRefresh(); } }; Chart.prototype.getElement = function getElement () { return this.element; }; Chart.prototype.getDataSource = function getDataSource () { return this.dataSource; }; Chart.prototype.getData = function getData () { return this.data; }; Chart.prototype.getOptions = function getOptions () { return this.options; }; Chart.prototype.getChartObject = function getChartObject () { return this.chart; }; Chart.prototype.getAdapter = function getAdapter () { return this.adapter; }; Chart.prototype.updateData = function updateData (dataSource, options) { this.dataSource = dataSource; if (options) { this.__updateOptions(options); } fetchDataSource(this, dataSource); }; Chart.prototype.setOptions = function setOptions (options) { this.__updateOptions(options); this.redraw(); }; Chart.prototype.redraw = function redraw () { fetchDataSource(this, this.rawData); }; Chart.prototype.refreshData = function refreshData () { if (typeof this.dataSource === "string") { // prevent browser from caching var sep = this.dataSource.indexOf("?") === -1 ? "?" : "&"; var url = this.dataSource + sep + "_=" + (new Date()).getTime(); fetchDataSource(this, url); } }; Chart.prototype.startRefresh = function startRefresh () { var this$1 = this; var refresh = this.options.refresh; if (!this.intervalId) { if (refresh) { this.intervalId = setInterval( function () { this$1.refreshData(); }, refresh * 1000); } else { throw new Error("No refresh interval"); } } }; Chart.prototype.stopRefresh = function stopRefresh () { if (this.intervalId) { clearInterval(this.intervalId); this.intervalId = null; } }; Chart.prototype.toImage = function toImage () { if (this.adapter === "chartjs") { return this.chart.toBase64Image(); } else { return null; } }; Chart.prototype.destroy = function destroy () { if (this.__adapterObject) { this.__adapterObject.destroy(this); } if (this.__enterEvent) { removeEvent(this.element, "mouseover", this.__enterEvent); } if (this.__leaveEvent) { removeEvent(this.element, "mouseout", this.__leaveEvent); } }; Chart.prototype.__updateOptions = function __updateOptions (options) { var updateRefresh = options.refresh && options.refresh !== this.options.refresh; this.options = merge(Chartkick.options, options); if (updateRefresh) { this.stopRefresh(); this.startRefresh(); } }; Chart.prototype.__render = function __render () { this.data = this.__processData(); renderChart(this.__chartName(), this); }; Chart.prototype.__config = function __config () { return config; }; var LineChart = (function (Chart) { function LineChart () { Chart.apply(this, arguments); } if ( Chart ) LineChart.__proto__ = Chart; LineChart.prototype = Object.create( Chart && Chart.prototype ); LineChart.prototype.constructor = LineChart; LineChart.prototype.__processData = function __processData () { return processSeries(this); }; LineChart.prototype.__chartName = function __chartName () { return "LineChart"; }; return LineChart; }(Chart)); var PieChart = (function (Chart) { function PieChart () { Chart.apply(this, arguments); } if ( Chart ) PieChart.__proto__ = Chart; PieChart.prototype = Object.create( Chart && Chart.prototype ); PieChart.prototype.constructor = PieChart; PieChart.prototype.__processData = function __processData () { return processSimple(this); }; PieChart.prototype.__chartName = function __chartName () { return "PieChart"; }; return PieChart; }(Chart)); var ColumnChart = (function (Chart) { function ColumnChart () { Chart.apply(this, arguments); } if ( Chart ) ColumnChart.__proto__ = Chart; ColumnChart.prototype = Object.create( Chart && Chart.prototype ); ColumnChart.prototype.constructor = ColumnChart; ColumnChart.prototype.__processData = function __processData () { return processSeries(this, null, true); }; ColumnChart.prototype.__chartName = function __chartName () { return "ColumnChart"; }; return ColumnChart; }(Chart)); var BarChart = (function (Chart) { function BarChart () { Chart.apply(this, arguments); } if ( Chart ) BarChart.__proto__ = Chart; BarChart.prototype = Object.create( Chart && Chart.prototype ); BarChart.prototype.constructor = BarChart; BarChart.prototype.__processData = function __processData () { return processSeries(this, null, true); }; BarChart.prototype.__chartName = function __chartName () { return "BarChart"; }; return BarChart; }(Chart)); var AreaChart = (function (Chart) { function AreaChart () { Chart.apply(this, arguments); } if ( Chart ) AreaChart.__proto__ = Chart; AreaChart.prototype = Object.create( Chart && Chart.prototype ); AreaChart.prototype.constructor = AreaChart; AreaChart.prototype.__processData = function __processData () { return processSeries(this); }; AreaChart.prototype.__chartName = function __chartName () { return "AreaChart"; }; return AreaChart; }(Chart)); var GeoChart = (function (Chart) { function GeoChart () { Chart.apply(this, arguments); } if ( Chart ) GeoChart.__proto__ = Chart; GeoChart.prototype = Object.create( Chart && Chart.prototype ); GeoChart.prototype.constructor = GeoChart; GeoChart.prototype.__processData = function __processData () { return processSimple(this); }; GeoChart.prototype.__chartName = function __chartName () { return "GeoChart"; }; return GeoChart; }(Chart)); var ScatterChart = (function (Chart) { function ScatterChart () { Chart.apply(this, arguments); } if ( Chart ) ScatterChart.__proto__ = Chart; ScatterChart.prototype = Object.create( Chart && Chart.prototype ); ScatterChart.prototype.constructor = ScatterChart; ScatterChart.prototype.__processData = function __processData () { return processSeries(this, "number"); }; ScatterChart.prototype.__chartName = function __chartName () { return "ScatterChart"; }; return ScatterChart; }(Chart)); var BubbleChart = (function (Chart) { function BubbleChart () { Chart.apply(this, arguments); } if ( Chart ) BubbleChart.__proto__ = Chart; BubbleChart.prototype = Object.create( Chart && Chart.prototype ); BubbleChart.prototype.constructor = BubbleChart; BubbleChart.prototype.__processData = function __processData () { return processSeries(this, "bubble"); }; BubbleChart.prototype.__chartName = function __chartName () { return "BubbleChart"; }; return BubbleChart; }(Chart)); var Timeline = (function (Chart) { function Timeline () { Chart.apply(this, arguments); } if ( Chart ) Timeline.__proto__ = Chart; Timeline.prototype = Object.create( Chart && Chart.prototype ); Timeline.prototype.constructor = Timeline; Timeline.prototype.__processData = function __processData () { var i, data = this.rawData; for (i = 0; i < data.length; i++) { data[i][1] = toDate(data[i][1]); data[i][2] = toDate(data[i][2]); } return data; }; Timeline.prototype.__chartName = function __chartName () { return "Timeline"; }; return Timeline; }(Chart)); var Chartkick = { LineChart: LineChart, PieChart: PieChart, ColumnChart: ColumnChart, BarChart: BarChart, AreaChart: AreaChart, GeoChart: GeoChart, ScatterChart: ScatterChart, BubbleChart: BubbleChart, Timeline: Timeline, 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]); } } }, config: config, options: {}, adapters: adapters, addAdapter: addAdapter }; return Chartkick; })));