app/assets/javascripts/pghero/chartkick.js in pghero-2.3.0 vs app/assets/javascripts/pghero/chartkick.js in pghero-2.4.0

- old
+ new

@@ -1,42 +1,39 @@ /* * Chartkick.js * Create beautiful charts with one line of JavaScript * https://github.com/ankane/chartkick.js - * v2.2.2 + * v3.2.0 * MIT License */ -/*jslint browser: true, indent: 2, plusplus: true, vars: true */ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, global.Chartkick = factory()); +}(this, (function () { 'use strict'; -(function (window) { - 'use strict'; - - var config = window.Chartkick || {}; - 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 - 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; + // protect against prototype pollution, defense 2 + return Object.prototype.toString.call(variable) === "[object Object]" && !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) { + // protect against prototype pollution, defense 1 + if (key === "__proto__") { continue; } + if (isPlainObject(source[key]) || isArray(source[key])) { if (isPlainObject(source[key]) && !isPlainObject(target[key])) { target[key] = {}; } if (isArray(source[key]) && !isArray(target[key])) { @@ -54,13 +51,15 @@ 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 - ISO8601_PATTERN = /(\d\d\d\d)(\-)?(\d\d)(\-)?(\d\d)(T)?(\d\d)(:)?(\d\d)?(:)?(\d\d)?([\.,]\d+)?($|Z|([\+\-])(\d\d)(:)?(\d\d)?)/i; - DECIMAL_SEPARATOR = String(1.5).charAt(1); + 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]") { @@ -103,10 +102,54 @@ } } 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 || {}); @@ -152,1311 +195,1723 @@ return options; }; } - function setText(element, text) { - if (document.body.innerText) { - element.innerText = text; - } else { - element.textContent = text; - } + function sortByTime(a, b) { + return a[0].getTime() - b[0].getTime(); } - function chartError(element, message) { - setText(element, "Error Loading Chart: " + message); - element.style.color = "#ff0000"; + function sortByNumberSeries(a, b) { + return a[0] - b[0]; } - function pushRequest(element, url, success) { - pendingRequests.push([element, url, success]); - runNext(); + function sortByNumber(a, b) { + return a - b; } - function runNext() { - if (runningRequests < maxRequests) { - var request = pendingRequests.shift() - if (request) { - runningRequests++; - getJSON(request[0], request[1], request[2]); - runNext(); - } - } + function isMinute(d) { + return d.getMilliseconds() === 0 && d.getSeconds() === 0; } - function requestComplete() { - runningRequests--; - runNext(); + function isHour(d) { + return isMinute(d) && d.getMinutes() === 0; } - function getJSON(element, url, success) { - ajaxCall(url, success, function (jqXHR, textStatus, errorThrown) { - var message = (typeof errorThrown === "string") ? errorThrown : errorThrown.message; - chartError(element, message); - }); + function isDay(d) { + return isHour(d) && d.getHours() === 0; } - function ajaxCall(url, success, error) { - var $ = window.jQuery || window.Zepto || window.$; + function isWeek(d, dayOfWeek) { + return isDay(d) && d.getDay() === dayOfWeek; + } - 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 isMonth(d) { + return isDay(d) && d.getDate() === 1; } - function errorCatcher(chart, callback) { - try { - callback(chart); - } catch (err) { - chartError(chart.element, err.message); - throw err; - } + function isYear(d) { + return isMonth(d) && d.getMonth() === 0; } - 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.rawData = dataSource; - errorCatcher(chart, callback); - } + function isDate(obj) { + return !isNaN(toDate(obj)) && toStr(obj).length >= 6; } - 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"; + function isNumber(obj) { + return typeof obj === "number"; + } - chart.downloadAttached = true; + var byteSuffixes = ["bytes", "KB", "MB", "GB", "TB", "PB", "EB"]; - // 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); + function formatValue(pre, value, options, axis) { + pre = pre || ""; + if (options.prefix) { + if (value < 0) { + value = value * -1; + pre += "-"; } - }); + pre += options.prefix; + } - // mouseleave - addEvent(element, "mouseout", function(e) { - var related = e.relatedTarget; - if (!related || (related !== this && !childOf(this, related))) { - if (link.parentNode) { - link.parentNode.removeChild(link); + var suffix = options.suffix || ""; + var precision = options.precision; + var round = options.round; + + if (options.byteScale) { + var suffixIdx; + var baseValue = axis ? options.byteScale : value; + + if (baseValue >= 1152921504606846976) { + value /= 1152921504606846976; + suffixIdx = 6; + } else if (baseValue >= 1125899906842624) { + value /= 1125899906842624; + suffixIdx = 5; + } else if (baseValue >= 1099511627776) { + value /= 1099511627776; + suffixIdx = 4; + } else if (baseValue >= 1073741824) { + value /= 1073741824; + suffixIdx = 3; + } else if (baseValue >= 1048576) { + value /= 1048576; + suffixIdx = 2; + } else if (baseValue >= 1024) { + value /= 1024; + suffixIdx = 1; + } else { + suffixIdx = 0; + } + + // TODO handle manual precision case + if (precision === undefined && round === undefined) { + if (value >= 1023.5) { + if (suffixIdx < byteSuffixes.length - 1) { + value = 1.0; + suffixIdx += 1; + } } + precision = value >= 1000 ? 4 : 3; } - }); - } + suffix = " " + byteSuffixes[suffixIdx]; + } - // 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)); - }); + if (precision !== undefined && round !== undefined) { + throw Error("Use either round or precision, not both"); } - } - // 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; - } + if (!axis) { + if (precision !== undefined) { + value = value.toPrecision(precision); + if (!options.zeros) { + value = parseFloat(value); + } + } - // type conversions + if (round !== undefined) { + if (round < 0) { + var num = Math.pow(10, -1 * round); + value = parseInt((1.0 * value / num).toFixed(0)) * num; + } else { + value = value.toFixed(round); + if (!options.zeros) { + value = parseFloat(value); + } + } + } + } - function toStr(n) { - return "" + n; + 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 + suffix; } - function toFloat(n) { - return parseFloat(n); + function seriesOption(chart, series, option) { + if (option in series) { + return series[option]; + } else if (option in chart.options) { + return chart.options[option]; + } + return null; } - function toDate(n) { - var matches, year, month, day; - if (typeof n !== "object") { - if (typeof n === "number") { - n = new Date(n * 1000); // ms - } else 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); + 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 n; + return true; } - function toArr(n) { - if (!isArray(n)) { - var arr = [], i; - for (i in n) { - if (n.hasOwnProperty(i)) { - arr.push([i, n[i]]); + 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; } - n = arr; + } else if (hideLegend) { + options.legend.display = false; } - return n; - } + }; - function sortByTime(a, b) { - return a[0].getTime() - b[0].getTime(); - } + var setTitle = function (options, title) { + options.title.display = true; + options.title.text = title; + }; - function sortByNumberSeries(a, b) { - return a[0] - b[0]; - } + var setMin = function (options, min) { + if (min !== null) { + options.scales.yAxes[0].ticks.min = toFloat(min); + } + }; - function sortByNumber(a, b) { - return a - b; - } + var setMax = function (options, max) { + options.scales.yAxes[0].ticks.max = toFloat(max); + }; - function loadAdapters() { - if (!HighchartsAdapter && "Highcharts" in window) { - HighchartsAdapter = new function () { - var Highcharts = window.Highcharts; + var setBarMin = function (options, min) { + if (min !== null) { + options.scales.xAxes[0].ticks.min = toFloat(min); + } + }; - this.name = "highcharts"; + var setBarMax = function (options, max) { + options.scales.xAxes[0].ticks.max = toFloat(max); + }; - var defaultOptions = { - 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 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; + }; + + // check if not null or undefined + // https://stackoverflow.com/a/27757708/1177228 + var notnull = function(x) { + return x != null; + }; + + 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, + precision: chart.options.precision, + round: chart.options.round, + zeros: chart.options.zeros + }; + + if (chart.options.bytes) { + var series = chart.data; + if (chartType === "pie") { + series = [{data: series}]; + } + + // calculate max + var max = 0; + for (var i = 0; i < series.length; i++) { + var s = series[i]; + for (var j = 0; j < s.data.length; j++) { + if (s.data[j][1] > max) { + max = s.data[j][1]; } + } + } + + // calculate scale + var scale = 1; + while (max >= 1024) { + scale *= 1024; + max /= 1024; + } + + // set step size + formatOptions.byteScale = scale; + } + + if (chartType !== "pie") { + var myAxes = options.scales.yAxes; + if (chartType === "bar") { + myAxes = options.scales.xAxes; + } + + if (formatOptions.byteScale) { + if (!myAxes[0].ticks.stepSize) { + myAxes[0].ticks.stepSize = formatOptions.byteScale / 2; + } + if (!myAxes[0].ticks.maxTicksLimit) { + myAxes[0].ticks.maxTicksLimit = 4; + } + } + + if (!myAxes[0].ticks.callback) { + myAxes[0].ticks.callback = function (value) { + return formatValue("", value, formatOptions, true); }; + } + } - 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; + 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 + ')'; }; - - var setTitle = function (options, title) { - options.title.text = title; + } 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 = ': '; - var setMin = function (options, min) { - options.yAxis.min = min; - }; + 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; + } - var setMax = function (options, max) { - options.yAxis.max = max; + return formatValue(dataLabel, data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index], formatOptions); }; - - var setStacked = function (options, stacked) { - options.plotOptions.series.stacking = stacked ? "normal" : null; + } 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 setXtitle = function (options, title) { - options.xAxis.title.text = title; - }; + var jsOptions = jsOptionsFunc(merge(baseOptions, defaultOptions), hideLegend, setTitle, setMin, setMax, setStacked, setXtitle, setYtitle); - var setYtitle = function (options, title) { - options.yAxis.title.text = title; - }; + var createDataTable = function (chart, options, chartType, library) { + var datasets = []; + var labels = []; - var jsOptions = jsOptionsFunc(defaultOptions, hideLegend, setTitle, setMin, setMax, setStacked, setXtitle, setYtitle); + var colors = chart.options.colors || defaultColors; - this.renderLineChart = function (chart, chartType) { - chartType = chartType || "spline"; - var chartOptions = {}; - if (chartType === "areaspline") { - chartOptions = { - plotOptions: { - areaspline: { - stacking: "normal" - }, - area: { - stacking: "normal" - }, - series: { - marker: { - enabled: false - } - } - } - }; - } + var day = true; + var week = true; + var dayOfWeek; + var month = true; + var year = true; + var hour = true; + var minute = true; - if (chart.options.curve === false) { - if (chartType === "areaspline") { - chartType = "area"; - } else if (chartType === "spline") { - chartType = "line"; - } - } + var series = chart.data; - 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; + 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]; } - options.chart.renderTo = chart.element.id; + } + } + } - var series = chart.data; - for (i = 0; i < series.length; i++) { - data = series[i].data; - if (!chart.discrete) { - for (j = 0; j < data.length; j++) { - data[j][0] = data[j][0].getTime(); - } - } - series[i].marker = {symbol: "circle"}; - if (chart.options.points === false) { - series[i].marker.enabled = false; - } - } - options.series = series; - chart.chart = new Highcharts.Chart(options); - }; + var i, j, s, d, key, rows = [], rows2 = []; - this.renderScatterChart = function (chart) { - var chartOptions = {}; - var options = jsOptions(chart, chart.options, chartOptions); - options.chart.type = "scatter"; - options.chart.renderTo = chart.element.id; - options.series = chart.data; - chart.chart = new Highcharts.Chart(options); - }; + if (chartType === "bar" || chartType === "column" || (chart.xtype !== "number" && chart.xtype !== "bubble")) { + var sortedLabels = []; - this.renderPieChart = function (chart) { - var chartOptions = merge(defaultOptions, {}); + for (i = 0; i < series.length; i++) { + s = series[i]; - if (chart.options.colors) { - chartOptions.colors = chart.options.colors; + 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); } - if (chart.options.donut) { - chartOptions.plotOptions = {pie: {innerSize: "50%"}}; + rows[key][i] = toFloat(d[1]); + if (sortedLabels.indexOf(key) === -1) { + sortedLabels.push(key); } + } + } - if ("legend" in chart.options) { - hideLegend(chartOptions, chart.options.legend); - } + if (chart.xtype === "datetime" || chart.xtype === "number") { + sortedLabels.sort(sortByNumber); + } - if (chart.options.title) { - setTitle(chartOptions, chart.options.title); + 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); + } + } - 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 - }]; - chart.chart = new Highcharts.Chart(options); - }; + for (i = 0; i < series.length; i++) { + s = series[i]; - this.renderColumnChart = function (chart, chartType) { - chartType = chartType || "column"; - var series = chart.data; - var options = jsOptions(chart, chart.options), i, j, s, d, rows = [], categories = []; - options.chart.type = chartType; - options.chart.renderTo = chart.element.id; + var color = s.color || colors[i]; + var backgroundColor = chartType !== "line" ? addOpacity(color, 0.5) : color; - for (i = 0; i < series.length; i++) { - s = series[i]; + var dataset = { + label: s.name || "", + data: rows2[i], + fill: chartType === "area", + borderColor: color, + backgroundColor: backgroundColor, + pointBackgroundColor: color, + borderWidth: 2, + pointHoverBackgroundColor: color + }; - for (j = 0; j < s.data.length; j++) { - 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]; - } - } + if (s.stack) { + dataset.stack = s.stack; + } - if (chart.options.xtype === "number") { - categories.sort(sortByNumber); - } + var curve = seriesOption(chart, s, "curve"); + if (curve === false) { + dataset.lineTension = 0; + } - options.xAxis.categories = categories; + var points = seriesOption(chart, s, "points"); + if (points === false) { + dataset.pointRadius = 0; + dataset.pointHitRadius = 5; + } - var newSeries = []; - for (i = 0; i < series.length; i++) { - d = []; - for (j = 0; j < categories.length; j++) { - d.push(rows[categories[j]][i] || 0); - } + dataset = merge(dataset, chart.options.dataset || {}); + dataset = merge(dataset, s.library || {}); + dataset = merge(dataset, s.dataset || {}); - newSeries.push({ - name: series[i].name, - data: d - }); + datasets.push(dataset); + } + + var xmin = chart.options.xmin; + var xmax = chart.options.xmax; + + if (chart.xtype === "datetime") { + // hacky check for Chart.js >= 2.9.0 + // https://github.com/chartjs/Chart.js/compare/v2.8.0...v2.9.0 + var gte29 = "math" in library.helpers; + var ticksKey = gte29 ? "ticks" : "time"; + if (notnull(xmin)) { + options.scales.xAxes[0][ticksKey].min = toDate(xmin).getTime(); + } + if (notnull(xmax)) { + options.scales.xAxes[0][ticksKey].max = toDate(xmax).getTime(); + } + } else if (chart.xtype === "number") { + if (notnull(xmin)) { + options.scales.xAxes[0].ticks.min = xmin; + } + if (notnull(xmax)) { + options.scales.xAxes[0].ticks.max = xmax; + } + } + + if (chart.xtype === "datetime" && labels.length > 0) { + var minTime = (notnull(xmin) ? toDate(xmin) : labels[0]).getTime(); + var maxTime = (notnull(xmax) ? toDate(xmax) : 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.series = newSeries; + options.scales.xAxes[0].time.unitStepSize = unitStepSize; + } + } - chart.chart = new Highcharts.Chart(options); - }; + 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 self = this; + var data = { + labels: labels, + datasets: datasets + }; - this.renderBarChart = function (chart) { - self.renderColumnChart(chart, "bar"); - }; + return data; + }; - this.renderAreaChart = function (chart) { - self.renderLineChart(chart, "areaspline"); - }; - }; - adapters.push(HighchartsAdapter); + 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; } - if (!GoogleChartsAdapter && window.google && (window.google.setOnLoadCallback || window.google.charts)) { - GoogleChartsAdapter = new function () { - var google = window.google; - this.name = "google"; + var options = jsOptions(chart, merge(chartOptions, chart.options)); + setFormatOptions(chart, options, chartType); - var loaded = {}; - var callbacks = []; + var data = createDataTable(chart, options, chartType || "line", this.library); - var runCallbacks = function () { - var cb, call; - for (var i = 0; i < callbacks.length; i++) { - cb = callbacks[i]; - call = google.visualization && ((cb.pack === "corechart" && google.visualization.LineChart) || (cb.pack === "timeline" && google.visualization.Timeline)); - if (call) { - cb.callback(); - callbacks.splice(i, 1); - i--; - } - } - }; + 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"; + } - var waitForLoaded = function (pack, callback) { - if (!callback) { - callback = pack; - pack = "corechart"; - } + this.drawChart(chart, "line", data, options); + }; - callbacks.push({pack: pack, callback: callback}); + defaultExport.prototype.renderPieChart = function renderPieChart (chart) { + var options = merge({}, baseOptions); + if (chart.options.donut) { + options.cutoutPercentage = 50; + } - if (loaded[pack]) { - runCallbacks(); - } else { - loaded[pack] = true; + if ("legend" in chart.options) { + hideLegend(options, chart.options.legend); + } - // https://groups.google.com/forum/#!topic/google-visualization-api/fMKJcyA2yyI - var loadOptions = { - packages: [pack], - callback: runCallbacks - }; - if (config.language) { - loadOptions.language = config.language; - } + if (chart.options.title) { + setTitle(options, chart.options.title); + } - if (window.google.setOnLoadCallback) { - google.load("visualization", "1", loadOptions); - } else { - google.charts.load("current", loadOptions); - } - } - }; + options = merge(options, chart.options.library || {}); + setFormatOptions(chart, options, "pie"); - // Set chart options - var defaultOptions = { - chartArea: {}, - fontName: "'Lucida Grande', 'Lucida Sans Unicode', Verdana, Arial, Helvetica, sans-serif", - pointSize: 6, - legend: { - textStyle: { - fontSize: 12, - color: "#444" - }, - alignment: "center", - position: "right" + 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") { + var barOptions = merge(baseOptions, defaultOptions); + delete barOptions.scales.yAxes[0].ticks.maxTicksLimit; + options = jsOptionsFunc(barOptions, 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", this.library); + 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, this.library); + + 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 = "<canvas></canvas>"; + 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: {}, + area: {}, + 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) { + var stackedValue = stacked ? (stacked === true ? "normal" : stacked) : null; + options.plotOptions.series.stacking = stackedValue; + options.plotOptions.area.stacking = stackedValue; + options.plotOptions.areaspline.stacking = stackedValue; + }; + + 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, + precision: chart.options.precision, + round: chart.options.round, + zeros: chart.options.zeros + }; + + 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 '<span style="color:' + this.color + '">\u25CF</span> ' + formatValue(this.series.name + ': <b>', this.y, formatOptions) + '</b><br/>'; + }; + } + }; + + var defaultExport$1 = function defaultExport(library) { + this.name = "highcharts"; + this.library = library; + }; + + defaultExport$1.prototype.renderLineChart = function renderLineChart (chart, chartType) { + chartType = chartType || "spline"; + var chartOptions = {}; + if (chartType === "areaspline") { + chartOptions = { + plotOptions: { + areaspline: { + stacking: "normal" }, - curveType: "function", - hAxis: { - textStyle: { - color: "#666", - fontSize: 12 - }, - titleTextStyle: {}, - gridlines: { - color: "transparent" - }, - baselineColor: "#ccc", - viewWindow: {} + area: { + stacking: "normal" }, - vAxis: { - textStyle: { - color: "#666", - fontSize: 12 - }, - titleTextStyle: {}, - baselineColor: "#ccc", - viewWindow: {} - }, - tooltip: { - textStyle: { - color: "#666", - fontSize: 12 + series: { + marker: { + enabled: false } } - }; + } + }; + } - 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"; - } - }; + if (chart.options.curve === false) { + if (chartType === "areaspline") { + chartType = "area"; + } else if (chartType === "spline") { + chartType = "line"; + } + } - var setTitle = function (options, title) { - options.title = title; - options.titleTextStyle = {color: "#333", fontSize: "20px"}; - }; + var options = jsOptions$1(chart, chart.options, chartOptions), data, i, j; + options.xAxis.type = chart.xtype === "string" ? "category" : (chart.xtype === "number" ? "linear" : "datetime"); + if (!options.chart.type) { + options.chart.type = chartType; + } + setFormatOptions$1(chart, options, chartType); - var setMin = function (options, min) { - options.vAxis.viewWindow.min = min; - }; + var series = chart.data; + for (i = 0; i < series.length; i++) { + series[i].name = series[i].name || "Value"; + data = series[i].data; + if (chart.xtype === "datetime") { + for (j = 0; j < data.length; j++) { + data[j][0] = data[j][0].getTime(); + } + } + series[i].marker = {symbol: "circle"}; + if (chart.options.points === false) { + series[i].marker.enabled = false; + } + } - var setMax = function (options, max) { - options.vAxis.viewWindow.max = max; - }; + this.drawChart(chart, series, options); + }; - var setBarMin = function (options, min) { - options.hAxis.viewWindow.min = min; - }; + defaultExport$1.prototype.renderScatterChart = function renderScatterChart (chart) { + var options = jsOptions$1(chart, chart.options, {}); + options.chart.type = "scatter"; + this.drawChart(chart, chart.data, options); + }; - var setBarMax = function (options, max) { - options.hAxis.viewWindow.max = max; - }; + defaultExport$1.prototype.renderPieChart = function renderPieChart (chart) { + var chartOptions = merge(defaultOptions$1, {}); - var setStacked = function (options, stacked) { - options.isStacked = !!stacked; - }; + if (chart.options.colors) { + chartOptions.colors = chart.options.colors; + } + if (chart.options.donut) { + chartOptions.plotOptions = {pie: {innerSize: "50%"}}; + } - var setXtitle = function (options, title) { - options.hAxis.title = title; - options.hAxis.titleTextStyle.italic = false; - }; + if ("legend" in chart.options) { + hideLegend$1(chartOptions, chart.options.legend); + } - var setYtitle = function (options, title) { - options.vAxis.title = title; - options.vAxis.titleTextStyle.italic = false; - }; + if (chart.options.title) { + setTitle$1(chartOptions, chart.options.title); + } - var jsOptions = jsOptionsFunc(defaultOptions, hideLegend, setTitle, setMin, setMax, setStacked, setXtitle, setYtitle); + var options = merge(chartOptions, chart.options.library || {}); + setFormatOptions$1(chart, options, "pie"); + var series = [{ + type: "pie", + name: chart.options.label || "Value", + data: chart.data + }]; - // cant use object as key - var createDataTable = function (series, columnType, xtype) { - var i, j, s, d, key, rows = [], sortedLabels = []; - for (i = 0; i < series.length; i++) { - s = series[i]; + this.drawChart(chart, series, options); + }; - for (j = 0; j < s.data.length; j++) { - d = s.data[j]; - 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]); - } - } + defaultExport$1.prototype.renderColumnChart = function renderColumnChart (chart, chartType) { + chartType = chartType || "column"; + var series = chart.data; + var options = jsOptions$1(chart, chart.options), i, j, s, d, rows = [], categories = []; + options.chart.type = chartType; + setFormatOptions$1(chart, options, chartType); - var rows2 = []; - var day = true; - var value; - 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); - } else if (columnType === "number") { - rows2.sort(sortByNumberSeries); - } + for (i = 0; i < series.length; i++) { + s = series[i]; - if (xtype === "number") { - rows2.sort(sortByNumberSeries); + for (j = 0; j < s.data.length; j++) { + 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]; + } + } - for (var i = 0; i < rows2.length; i++) { - rows2[i][0] = toStr(rows2[i][0]); - } - } + if (chart.xtype === "number") { + categories.sort(sortByNumber); + } - // create datatable - var data = new google.visualization.DataTable(); - columnType = columnType === "datetime" && day ? "date" : columnType; - data.addColumn(columnType, ""); - for (i = 0; i < series.length; i++) { - data.addColumn("number", series[i].name); - } - data.addRows(rows2); + options.xAxis.categories = categories; - return data; - }; + var newSeries = [], d2; + for (i = 0; i < series.length; i++) { + d = []; + for (j = 0; j < categories.length; j++) { + d.push(rows[categories[j]][i] || 0); + } - var resize = function (callback) { - if (window.attachEvent) { - window.attachEvent("onresize", callback); - } else if (window.addEventListener) { - window.addEventListener("resize", callback, true); - } - callback(); - }; + d2 = { + name: series[i].name || "Value", + data: d + }; + if (series[i].stack) { + d2.stack = series[i].stack; + } - this.renderLineChart = function (chart) { - waitForLoaded(function () { - var chartOptions = {}; + newSeries.push(d2); + } - if (chart.options.curve === false) { - chartOptions.curveType = "none"; - } + this.drawChart(chart, newSeries, options); + }; - if (chart.options.points === false) { - chartOptions.pointSize = 0; - } + defaultExport$1.prototype.renderBarChart = function renderBarChart (chart) { + this.renderColumnChart(chart, "bar"); + }; - var options = jsOptions(chart, chart.options, chartOptions); - var columnType = chart.discrete ? "string" : "datetime"; - if (chart.options.xtype === "number") { - columnType = "number"; - } - var data = createDataTable(chart.data, columnType); - chart.chart = new google.visualization.LineChart(chart.element); - resize(function () { - chart.chart.draw(data, options); - }); - }); - }; + defaultExport$1.prototype.renderAreaChart = function renderAreaChart (chart) { + this.renderLineChart(chart, "areaspline"); + }; - this.renderPieChart = function (chart) { - waitForLoaded(function () { - var chartOptions = { - 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 || {}); + defaultExport$1.prototype.destroy = function destroy (chart) { + if (chart.chart) { + chart.chart.destroy(); + } + }; - var data = new google.visualization.DataTable(); - data.addColumn("string", ""); - data.addColumn("number", "Value"); - data.addRows(chart.data); + defaultExport$1.prototype.drawChart = function drawChart (chart, data, options) { + this.destroy(chart); - chart.chart = new google.visualization.PieChart(chart.element); - resize(function () { - chart.chart.draw(data, options); - }); - }); - }; + options.chart.renderTo = chart.element.id; + options.series = data; - this.renderColumnChart = function (chart) { - waitForLoaded(function () { - var options = jsOptions(chart, chart.options); - var data = createDataTable(chart.data, "string", chart.options.xtype); - chart.chart = new google.visualization.ColumnChart(chart.element); - resize(function () { - chart.chart.draw(data, options); - }); - }); - }; + if (chart.options.code) { + window.console.log("new Highcharts.Chart(" + JSON.stringify(options) + ");"); + } - this.renderBarChart = function (chart) { - waitForLoaded(function () { - var chartOptions = { - hAxis: { - gridlines: { - color: "#ccc" - } - } - }; - var options = jsOptionsFunc(defaultOptions, hideLegend, setTitle, setBarMin, setBarMax, setStacked, setXtitle, setYtitle)(chart, chart.options, chartOptions); - var data = createDataTable(chart.data, "string", chart.options.xtype); - chart.chart = new google.visualization.BarChart(chart.element); - resize(function () { - chart.chart.draw(data, options); - }); - }); - }; + chart.chart = new this.library.Chart(options); + }; - this.renderAreaChart = function (chart) { - waitForLoaded(function () { - var chartOptions = { - isStacked: true, - pointSize: 0, - areaOpacity: 0.5 - }; + var loaded = {}; + var callbacks = []; - var options = jsOptions(chart, chart.options, chartOptions); - var columnType = chart.discrete ? "string" : "datetime"; - if (chart.options.xtype === "number") { - columnType = "number"; - } - var data = createDataTable(chart.data, columnType); - chart.chart = new google.visualization.AreaChart(chart.element); - resize(function () { - chart.chart.draw(data, options); - }); - }); - }; + // Set chart options + var defaultOptions$2 = { + chartArea: {}, + fontName: "'Lucida Grande', 'Lucida Sans Unicode', Verdana, Arial, Helvetica, sans-serif", + pointSize: 6, + legend: { + textStyle: { + fontSize: 12, + color: "#444" + }, + alignment: "center", + position: "right" + }, + curveType: "function", + hAxis: { + textStyle: { + color: "#666", + fontSize: 12 + }, + titleTextStyle: {}, + gridlines: { + color: "transparent" + }, + baselineColor: "#ccc", + viewWindow: {} + }, + vAxis: { + textStyle: { + color: "#666", + fontSize: 12 + }, + titleTextStyle: {}, + baselineColor: "#ccc", + viewWindow: {} + }, + tooltip: { + textStyle: { + color: "#666", + fontSize: 12 + } + } + }; - this.renderGeoChart = function (chart) { - waitForLoaded(function () { - var chartOptions = { - legend: "none", - colorAxis: { - colors: chart.options.colors || ["#f6c7b6", "#ce502d"] - } - }; - var options = merge(merge(defaultOptions, chartOptions), chart.options.library || {}); + var hideLegend$2 = 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 data = new google.visualization.DataTable(); - data.addColumn("string", ""); - data.addColumn("number", chart.options.label || "Value"); - data.addRows(chart.data); + var setTitle$2 = function (options, title) { + options.title = title; + options.titleTextStyle = {color: "#333", fontSize: "20px"}; + }; - chart.chart = new google.visualization.GeoChart(chart.element); - resize(function () { - chart.chart.draw(data, options); - }); - }); - }; + var setMin$2 = function (options, min) { + options.vAxis.viewWindow.min = min; + }; - this.renderScatterChart = function (chart) { - waitForLoaded(function () { - var chartOptions = {}; - var options = jsOptions(chart, chart.options, chartOptions); + var setMax$2 = function (options, max) { + options.vAxis.viewWindow.max = max; + }; - var series = chart.data, rows2 = [], i, j, data, d; - for (i = 0; i < series.length; i++) { - d = series[i].data; - for (j = 0; j < d.length; j++) { - var row = new Array(series.length + 1); - row[0] = d[j][0]; - row[i + 1] = d[j][1]; - rows2.push(row); - } - } + var setBarMin$1 = function (options, min) { + options.hAxis.viewWindow.min = min; + }; - var data = new google.visualization.DataTable(); - data.addColumn("number", ""); - for (i = 0; i < series.length; i++) { - data.addColumn("number", series[i].name); - } - data.addRows(rows2); + var setBarMax$1 = function (options, max) { + options.hAxis.viewWindow.max = max; + }; - chart.chart = new google.visualization.ScatterChart(chart.element); - resize(function () { - chart.chart.draw(data, options); - }); - }); - }; + var setStacked$2 = function (options, stacked) { + options.isStacked = stacked ? stacked : false; + }; - this.renderTimeline = function (chart) { - waitForLoaded("timeline", function () { - var chartOptions = { - legend: "none" - }; + var setXtitle$2 = function (options, title) { + options.hAxis.title = title; + options.hAxis.titleTextStyle.italic = false; + }; - if (chart.options.colors) { - chartOptions.colors = chart.options.colors; - } - var options = merge(merge(defaultOptions, chartOptions), chart.options.library || {}); + var setYtitle$2 = function (options, title) { + options.vAxis.title = title; + options.vAxis.titleTextStyle.italic = false; + }; - var data = new google.visualization.DataTable(); - data.addColumn({type: "string", id: "Name"}); - data.addColumn({type: "date", id: "Start"}); - data.addColumn({type: "date", id: "End"}); - data.addRows(chart.data); + var jsOptions$2 = jsOptionsFunc(defaultOptions$2, hideLegend$2, setTitle$2, setMin$2, setMax$2, setStacked$2, setXtitle$2, setYtitle$2); - chart.element.style.lineHeight = "normal"; - chart.chart = new google.visualization.Timeline(chart.element); + var resize = function (callback) { + if (window.attachEvent) { + window.attachEvent("onresize", callback); + } else if (window.addEventListener) { + window.addEventListener("resize", callback, true); + } + callback(); + }; - resize(function () { - chart.chart.draw(data, options); - }); - }); - }; - }; + var defaultExport$2 = function defaultExport(library) { + this.name = "google"; + this.library = library; + }; - adapters.push(GoogleChartsAdapter); - } - if (!ChartjsAdapter && "Chart" in window) { - ChartjsAdapter = new function () { - var Chart = window.Chart; + defaultExport$2.prototype.renderLineChart = function renderLineChart (chart) { + var this$1 = this; - this.name = "chartjs"; + this.waitForLoaded(chart, function () { + var chartOptions = {}; - var baseOptions = { - maintainAspectRatio: false, - animation: false, - tooltips: { - displayColors: false - }, - legend: {}, - title: {fontSize: 20, fontColor: "#333"} - }; + if (chart.options.curve === false) { + chartOptions.curveType = "none"; + } - 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: {} - } - ] - } - }; + if (chart.options.points === false) { + chartOptions.pointSize = 0; + } - // 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", "#3B3EAC" - ]; + var options = jsOptions$2(chart, chart.options, chartOptions); + var data = this$1.createDataTable(chart.data, chart.xtype); - 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; - } - }; + this$1.drawChart(chart, "LineChart", data, options); + }); + }; - var setTitle = function (options, title) { - options.title.display = true; - options.title.text = title; - }; + defaultExport$2.prototype.renderPieChart = function renderPieChart (chart) { + var this$1 = this; - var setMin = function (options, min) { - if (min !== null) { - options.scales.yAxes[0].ticks.min = toFloat(min); - } - }; + this.waitForLoaded(chart, function () { + var chartOptions = { + 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$2(chartOptions, chart.options.legend); + } + if (chart.options.title) { + setTitle$2(chartOptions, chart.options.title); + } + var options = merge(merge(defaultOptions$2, chartOptions), chart.options.library || {}); - var setMax = function (options, max) { - options.scales.yAxes[0].ticks.max = toFloat(max); - }; + var data = new this$1.library.visualization.DataTable(); + data.addColumn("string", ""); + data.addColumn("number", "Value"); + data.addRows(chart.data); - var setBarMin = function (options, min) { - if (min !== null) { - options.scales.xAxes[0].ticks.min = toFloat(min); - } - }; + this$1.drawChart(chart, "PieChart", data, options); + }); + }; - var setBarMax = function (options, max) { - options.scales.xAxes[0].ticks.max = toFloat(max); - }; + defaultExport$2.prototype.renderColumnChart = function renderColumnChart (chart) { + var this$1 = this; - var setStacked = function (options, stacked) { - options.scales.xAxes[0].stacked = !!stacked; - options.scales.yAxes[0].stacked = !!stacked; - }; + this.waitForLoaded(chart, function () { + var options = jsOptions$2(chart, chart.options); + var data = this$1.createDataTable(chart.data, chart.xtype); - var setXtitle = function (options, title) { - options.scales.xAxes[0].scaleLabel.display = true; - options.scales.xAxes[0].scaleLabel.labelString = title; - }; + this$1.drawChart(chart, "ColumnChart", data, options); + }); + }; - var setYtitle = function (options, title) { - options.scales.yAxes[0].scaleLabel.display = true; - options.scales.yAxes[0].scaleLabel.labelString = title; - }; + defaultExport$2.prototype.renderBarChart = function renderBarChart (chart) { + var this$1 = this; - var drawChart = function(chart, type, data, options) { - if (chart.chart) { - chart.chart.destroy(); - } else { - chart.element.innerHTML = "<canvas></canvas>"; + this.waitForLoaded(chart, function () { + var chartOptions = { + hAxis: { + gridlines: { + color: "#ccc" } + } + }; + var options = jsOptionsFunc(defaultOptions$2, hideLegend$2, setTitle$2, setBarMin$1, setBarMax$1, setStacked$2, setXtitle$2, setYtitle$2)(chart, chart.options, chartOptions); + var data = this$1.createDataTable(chart.data, chart.xtype); - var ctx = chart.element.getElementsByTagName("CANVAS")[0]; - chart.chart = new Chart(ctx, { - type: type, - data: data, - options: options - }); - }; + this$1.drawChart(chart, "BarChart", data, options); + }); + }; - // http://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; - }; + defaultExport$2.prototype.renderAreaChart = function renderAreaChart (chart) { + var this$1 = this; - var setLabelSize = function (chart, data, options) { - var maxLabelSize = Math.ceil(chart.element.offsetWidth / 4.0 / data.labels.length); - if (maxLabelSize > 25) { - maxLabelSize = 25; - } - options.scales.xAxes[0].ticks.callback = function (value) { - value = toStr(value); - if (value.length > maxLabelSize) { - return value.substring(0, maxLabelSize - 2) + "..."; - } else { - return value; - } - }; - }; + this.waitForLoaded(chart, function () { + var chartOptions = { + isStacked: true, + pointSize: 0, + areaOpacity: 0.5 + }; - var jsOptions = jsOptionsFunc(merge(baseOptions, defaultOptions), hideLegend, setTitle, setMin, setMax, setStacked, setXtitle, setYtitle); + var options = jsOptions$2(chart, chart.options, chartOptions); + var data = this$1.createDataTable(chart.data, chart.xtype); - var createDataTable = function (chart, options, chartType) { - var datasets = []; - var labels = []; + this$1.drawChart(chart, "AreaChart", data, options); + }); + }; - var colors = chart.options.colors || defaultColors; + defaultExport$2.prototype.renderGeoChart = function renderGeoChart (chart) { + var this$1 = this; - var day = true; - var week = true; - var dayOfWeek; - var month = true; - var year = true; - var hour = true; - var minute = true; - var detectType = (chartType === "line" || chartType === "area") && !chart.discrete; + this.waitForLoaded(chart, function () { + var chartOptions = { + legend: "none", + colorAxis: { + colors: chart.options.colors || ["#f6c7b6", "#ce502d"] + } + }; + var options = merge(merge(defaultOptions$2, chartOptions), chart.options.library || {}); - var series = chart.data; + var data = new this$1.library.visualization.DataTable(); + data.addColumn("string", ""); + data.addColumn("number", chart.options.label || "Value"); + data.addRows(chart.data); - var sortedLabels = []; + this$1.drawChart(chart, "GeoChart", data, options); + }); + }; - var i, j, s, d, key, rows = []; - for (i = 0; i < series.length; i++) { - s = series[i]; + defaultExport$2.prototype.renderScatterChart = function renderScatterChart (chart) { + var this$1 = this; - for (j = 0; j < s.data.length; j++) { - d = s.data[j]; - key = detectType ? 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); - } - } - } + this.waitForLoaded(chart, function () { + var chartOptions = {}; + var options = jsOptions$2(chart, chart.options, chartOptions); - if (detectType || chart.options.xtype === "number") { - sortedLabels.sort(sortByNumber); - } + var series = chart.data, rows2 = [], i, j, data, d; + for (i = 0; i < series.length; i++) { + series[i].name = series[i].name || "Value"; + d = series[i].data; + for (j = 0; j < d.length; j++) { + var row = new Array(series.length + 1); + row[0] = d[j][0]; + row[i + 1] = d[j][1]; + rows2.push(row); + } + } - var rows2 = []; - for (j = 0; j < series.length; j++) { - rows2.push([]); - } + data = new this$1.library.visualization.DataTable(); + data.addColumn("number", ""); + for (i = 0; i < series.length; i++) { + data.addColumn("number", series[i].name); + } + data.addRows(rows2); - var value; - var k; - for (k = 0; k < sortedLabels.length; k++) { - i = sortedLabels[k]; - if (detectType) { - 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]); - } - } + this$1.drawChart(chart, "ScatterChart", data, options); + }); + }; - for (i = 0; i < series.length; i++) { - s = series[i]; + defaultExport$2.prototype.renderTimeline = function renderTimeline (chart) { + var this$1 = this; - var color = s.color || colors[i]; - var backgroundColor = chartType !== "line" ? addOpacity(color, 0.5) : color; + this.waitForLoaded(chart, "timeline", function () { + var chartOptions = { + legend: "none" + }; - var dataset = { - label: s.name, - data: rows2[i], - fill: chartType === "area", - borderColor: color, - backgroundColor: backgroundColor, - pointBackgroundColor: color, - borderWidth: 2 - }; + if (chart.options.colors) { + chartOptions.colors = chart.options.colors; + } + var options = merge(merge(defaultOptions$2, chartOptions), chart.options.library || {}); - if (chart.options.curve === false) { - dataset.lineTension = 0; - } + var data = new this$1.library.visualization.DataTable(); + data.addColumn({type: "string", id: "Name"}); + data.addColumn({type: "date", id: "Start"}); + data.addColumn({type: "date", id: "End"}); + data.addRows(chart.data); - if (chart.options.points === false) { - dataset.pointRadius = 0; - dataset.pointHitRadius = 5; - } + chart.element.style.lineHeight = "normal"; - datasets.push(merge(dataset, s.library || {})); - } + this$1.drawChart(chart, "Timeline", data, options); + }); + }; - if (detectType && labels.length > 0) { - var minTime = labels[0].getTime(); - var maxTime = labels[0].getTime(); - for (i = 1; i < labels.length; i++) { - value = labels[i].getTime(); - if (value < minTime) { - minTime = value; - } - if (value > maxTime) { - maxTime = value; - } - } + defaultExport$2.prototype.destroy = function destroy (chart) { + if (chart.chart) { + chart.chart.clearChart(); + } + }; - var timeDiff = (maxTime - minTime) / (86400 * 1000.0); + defaultExport$2.prototype.drawChart = function drawChart (chart, type, data, options) { + this.destroy(chart); - 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 (chart.options.code) { + window.console.log("var data = new google.visualization.DataTable(" + data.toJSON() + ");\nvar chart = new google.visualization." + type + "(element);\nchart.draw(data, " + JSON.stringify(options) + ");"); + } - 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; - } - } + chart.chart = new this.library.visualization[type](chart.element); + resize(function () { + chart.chart.draw(data, options); + }); + }; - 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"; - } - } - } + defaultExport$2.prototype.waitForLoaded = function waitForLoaded (chart, pack, callback) { + var this$1 = this; - var data = { - labels: labels, - datasets: datasets - }; + if (!callback) { + callback = pack; + pack = "corechart"; + } - return data; - }; + callbacks.push({pack: pack, callback: callback}); - this.renderLineChart = function (chart, chartType) { - if (chart.options.xtype === "number") { - return self.renderScatterChart(chart, chartType, true); - } + if (loaded[pack]) { + this.runCallbacks(); + } else { + loaded[pack] = true; - var chartOptions = {}; - if (chartType === "area") { - // TODO fix area stacked - // chartOptions.stacked = true; - } - // fix for https://github.com/chartjs/Chart.js/issues/2441 - if (!chart.options.max && allZeros(chart.data)) { - chartOptions.max = 1; - } + // https://groups.google.com/forum/#!topic/google-visualization-api/fMKJcyA2yyI + var loadOptions = { + packages: [pack], + callback: function () { this$1.runCallbacks(); } + }; + var config = chart.__config(); + if (config.language) { + loadOptions.language = config.language; + } + if (pack === "corechart" && config.mapsApiKey) { + loadOptions.mapsApiKey = config.mapsApiKey; + } - var options = jsOptions(chart, merge(chartOptions, chart.options)); + this.library.charts.load("current", loadOptions); + } + }; - var data = createDataTable(chart, options, chartType || "line"); + defaultExport$2.prototype.runCallbacks = function runCallbacks () { + var cb, call; + for (var i = 0; i < callbacks.length; i++) { + cb = callbacks[i]; + call = this.library.visualization && ((cb.pack === "corechart" && this.library.visualization.LineChart) || (cb.pack === "timeline" && this.library.visualization.Timeline)); + if (call) { + cb.callback(); + callbacks.splice(i, 1); + i--; + } + } + }; - options.scales.xAxes[0].type = chart.discrete ? "category" : "time"; + // cant use object as key + defaultExport$2.prototype.createDataTable = function createDataTable (series, columnType) { + var i, j, s, d, key, rows = [], sortedLabels = []; + for (i = 0; i < series.length; i++) { + s = series[i]; + series[i].name = series[i].name || "Value"; - drawChart(chart, "line", data, options); - }; + for (j = 0; j < s.data.length; j++) { + d = s.data[j]; + 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]); + } + } - this.renderPieChart = function (chart) { - var options = merge({}, baseOptions); - if (chart.options.donut) { - options.cutoutPercentage = 50; - } + var rows2 = []; + var day = true; + var value; + for (j = 0; j < sortedLabels.length; j++) { + 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); + } else if (columnType === "number") { + rows2.sort(sortByNumberSeries); - if ("legend" in chart.options) { - hideLegend(options, chart.options.legend); - } + for (i = 0; i < rows2.length; i++) { + rows2[i][0] = toStr(rows2[i][0]); + } - if (chart.options.title) { - setTitle(options, chart.options.title); - } + columnType = "string"; + } - options = merge(options, chart.options.library || {}); + // create datatable + var data = new this.library.visualization.DataTable(); + columnType = columnType === "datetime" && day ? "date" : columnType; + data.addColumn(columnType, ""); + for (i = 0; i < series.length; i++) { + data.addColumn("number", series[i].name); + } + data.addRows(rows2); - 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]); - } + return data; + }; - var data = { - labels: labels, - datasets: [ - { - data: values, - backgroundColor: chart.options.colors || defaultColors - } - ] - }; + var pendingRequests = [], runningRequests = 0, maxRequests = 4; - drawChart(chart, "pie", data, options); - }; + function pushRequest(url, success, error) { + pendingRequests.push([url, success, error]); + runNext(); + } - this.renderColumnChart = function (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); - } - var data = createDataTable(chart, options, "column"); - setLabelSize(chart, data, options); - drawChart(chart, (chartType === "bar" ? "horizontalBar" : "bar"), data, options); - }; + function runNext() { + if (runningRequests < maxRequests) { + var request = pendingRequests.shift(); + if (request) { + runningRequests++; + getJSON(request[0], request[1], request[2]); + runNext(); + } + } + } - var self = this; + function requestComplete() { + runningRequests--; + runNext(); + } - this.renderAreaChart = function (chart) { - self.renderLineChart(chart, "area"); - }; + function getJSON(url, success, error) { + ajaxCall(url, success, function (jqXHR, textStatus, errorThrown) { + var message = (typeof errorThrown === "string") ? errorThrown : errorThrown.message; + error(message); + }); + } - this.renderBarChart = function (chart) { - self.renderColumnChart(chart, "bar"); - }; + function ajaxCall(url, success, error) { + var $ = window.jQuery || window.Zepto || window.$; - this.renderScatterChart = function (chart, chartType, lineChart) { - chartType = chartType || "line"; + if ($ && $.ajax) { + $.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(); + } + } - var options = jsOptions(chart, chart.options); + var config = {}; + var adapters = []; - var colors = chart.options.colors || defaultColors; + // helpers - 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++) { - var point = { - x: toFloat(s.data[j][0]), - y: toFloat(s.data[j][1]) - }; - if (chartType === "bubble") { - point.r = toFloat(s.data[j][2]); - } - d.push(point); - } + function setText(element, text) { + if (document.body.innerText) { + element.innerText = text; + } else { + element.textContent = text; + } + } - var color = s.color || colors[i]; - var backgroundColor = chartType === "area" ? addOpacity(color, 0.5) : color; + // TODO remove prefix for all messages + function chartError(element, message, noPrefix) { + if (!noPrefix) { + message = "Error Loading Chart: " + message; + } + setText(element, message); + element.style.color = "#ff0000"; + } - datasets.push({ - label: s.name, - showLine: lineChart || false, - data: d, - borderColor: color, - backgroundColor: backgroundColor, - pointBackgroundColor: color, - fill: chartType === "area" - }) - } + function errorCatcher(chart) { + try { + chart.__render(); + } catch (err) { + chartError(chart.element, err.message); + throw err; + } + } - if (chartType === "area") { - chartType = "line"; - } + function fetchDataSource(chart, dataSource) { + if (typeof dataSource === "string") { + pushRequest(dataSource, function (data) { + chart.rawData = data; + errorCatcher(chart); + }, function (message) { + chartError(chart.element, message); + }); + } else if (typeof dataSource === "function") { + try { + dataSource(function (data) { + chart.rawData = data; + errorCatcher(chart); + }, function (message) { + chartError(chart.element, message, true); + }); + } catch (err) { + chartError(chart.element, err, true); + } + } else { + chart.rawData = dataSource; + errorCatcher(chart); + } + } - var data = {datasets: datasets}; + function addDownloadButton(chart) { + var element = chart.element; + var link = document.createElement("a"); - options.scales.xAxes[0].type = "linear"; - options.scales.xAxes[0].position = "bottom"; + var download = chart.options.download; + if (download === true) { + download = {}; + } else if (typeof download === "string") { + download = {filename: download}; + } + link.download = download.filename || "chart.png"; // https://caniuse.com/download - drawChart(chart, chartType, data, options); - }; + 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"; - this.renderBubbleChart = function (chart) { - this.renderScatterChart(chart, "bubble"); - }; + chart.__downloadAttached = true; + + // mouseenter + chart.__enterEvent = 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(download); + element.appendChild(link); + } + }); + + // mouseleave + chart.__leaveEvent = addEvent(element, "mouseout", function(e) { + var related = e.relatedTarget; + if (!related || (related !== this && !childOf(this, related))) { + if (link.parentNode) { + link.parentNode.removeChild(link); + } + } + }); + } + + // https://stackoverflow.com/questions/10149963/adding-event-listener-cross-browser + function addEvent(elem, event, fn) { + if (elem.addEventListener) { + elem.addEventListener(event, fn, false); + return fn; + } else { + var fn2 = function() { + // set the this pointer same as addEventListener when fn is called + return(fn.call(elem, window.event)); }; + elem.attachEvent("on" + event, fn2); + return fn2; + } + } - adapters.unshift(ChartjsAdapter); + function removeEvent(elem, event, fn) { + if (elem.removeEventListener) { + elem.removeEventListener(event, fn, false); + } else { + elem.detachEvent("on" + event, fn); } } + // 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; + } + + function getAdapterType(library) { + if (library) { + if (library.product === "Highcharts") { + return defaultExport$1; + } else if (library.charts) { + return defaultExport$2; + } else if (isFunction(library)) { + return defaultExport; + } + } + throw new Error("Unknown adapter"); + } + + function addAdapter(library) { + var adapterType = getAdapterType(library); + var adapter = new adapterType(library); + + if (adapters.indexOf(adapter) === -1) { + adapters.push(adapter); + } + } + + function loadAdapters() { + if ("Chart" in window) { + addAdapter(window.Chart); + } + + if ("Highcharts" in window) { + addAdapter(window.Highcharts); + } + + if (window.google && window.google.charts) { + addAdapter(window.google); + } + } + + function dataEmpty(data, chartType) { + if (chartType === "PieChart" || chartType === "GeoChart" || chartType === "Timeline") { + return data.length === 0; + } else { + for (var i = 0; i < data.length; i++) { + if (data[i].data.length > 0) { + return false; + } + } + return true; + } + } + function renderChart(chartType, chart) { - callAdapter(chartType, chart); - if (chart.options.download && !chart.downloadAttached && chart.adapter === "chartjs") { - addDownloadButton(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 @@ -1469,14 +1924,20 @@ 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); } } - throw new Error("No adapter found"); + + 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) { @@ -1506,92 +1967,69 @@ r.sort(sortByNumberSeries); } return r; }; - function isMinute(d) { - return d.getMilliseconds() === 0 && d.getSeconds() === 0; + function detectXType(series, noDatetime) { + if (detectXTypeWithFunction(series, isNumber)) { + return "number"; + } else if (!noDatetime && detectXTypeWithFunction(series, isDate)) { + return "datetime"; + } else { + return "string"; + } } - 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 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) { + 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; } - function detectDiscrete(series) { - var i, j, data; + // 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++) { - data = toArr(series[i].data); - for (j = 0; j < data.length; j++) { - if (!isDate(data[j][0])) { - return true; + var copy = {}; + for (j in series[i]) { + if (series[i].hasOwnProperty(j)) { + copy[j] = series[i][j]; } } + newSeries.push(copy); } - return false; + return newSeries; } - function processSeries(chart, keyType) { + 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 || "Value", data: series}]; + series = [{name: opts.label, data: series}]; chart.hideLegend = true; } else { chart.hideLegend = false; } - if ((opts.discrete === null || opts.discrete === undefined) && keyType !== "bubble" && keyType !== "number") { - chart.discrete = detectDiscrete(series); - } else { - chart.discrete = opts.discrete; - } - if (chart.discrete) { - keyType = "string"; - } - if (chart.options.xtype) { - keyType = chart.options.xtype; - } + 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), keyType); + series[i].data = formatSeriesData(toArr(series[i].data), chart.xtype); } return series; } @@ -1601,188 +2039,398 @@ perfectData[i] = [toStr(perfectData[i][0]), toFloat(perfectData[i][1])]; } return perfectData; } - function processTime(chart) - { - 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]); + // 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); + } } - return data; - } + this.element = element; + this.options = merge(Chartkick.options, options || {}); + this.dataSource = dataSource; - function processLineData(chart) { - return processSeries(chart, "datetime"); - } + Chartkick.charts[element.id] = this; - function processColumnData(chart) { - return processSeries(chart, "string"); - } + fetchDataSource(this, dataSource); - function processBarData(chart) { - return processSeries(chart, "string"); - } + if (this.options.refresh) { + this.startRefresh(); + } + }; - function processAreaData(chart) { - return processSeries(chart, "datetime"); - } + Chart.prototype.getElement = function getElement () { + return this.element; + }; - function processScatterData(chart) { - return processSeries(chart, "number"); - } + Chart.prototype.getDataSource = function getDataSource () { + return this.dataSource; + }; - function processBubbleData(chart) { - return processSeries(chart, "bubble"); - } + Chart.prototype.getData = function getData () { + return this.data; + }; - function createChart(chartType, chart, element, dataSource, opts, processData) { - var elementId; - if (typeof element === "string") { - elementId = element; - element = document.getElementById(element); - if (!element) { - throw new Error("No element with id " + elementId); + 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); + } else if (typeof this.dataSource === "function") { + fetchDataSource(this, this.dataSource); + } + }; + + Chart.prototype.startRefresh = function startRefresh () { + var this$1 = this; + + var refresh = this.options.refresh; + + if (refresh && typeof this.dataSource !== "string" && typeof this.dataSource !== "function") { + throw new Error("Data source must be a URL or callback for refresh"); + } + + if (!this.intervalId) { + if (refresh) { + this.intervalId = setInterval( function () { + this$1.refreshData(); + }, refresh * 1000); + } else { + throw new Error("No refresh interval"); } } + }; - chart.element = element; - opts = merge(Chartkick.options, opts || {}); - chart.options = opts; - chart.dataSource = dataSource; + Chart.prototype.stopRefresh = function stopRefresh () { + if (this.intervalId) { + clearInterval(this.intervalId); + this.intervalId = null; + } + }; - if (!processData) { - processData = function (chart) { - return chart.rawData; + Chart.prototype.toImage = function toImage (download) { + if (this.adapter === "chartjs") { + if (download && download.background && download.background !== "transparent") { + // https://stackoverflow.com/questions/30464750/chartjs-line-chart-set-background-color + var canvas = this.chart.chart.canvas; + var ctx = this.chart.chart.ctx; + var tmpCanvas = document.createElement("canvas"); + var tmpCtx = tmpCanvas.getContext("2d"); + tmpCanvas.width = ctx.canvas.width; + tmpCanvas.height = ctx.canvas.height; + tmpCtx.fillStyle = download.background; + tmpCtx.fillRect(0, 0, tmpCanvas.width, tmpCanvas.height); + tmpCtx.drawImage(canvas, 0, 0); + return tmpCanvas.toDataURL("image/png"); + } else { + return this.chart.toBase64Image(); } + } else { + // TODO throw error in next major version + // throw new Error("Feature only available for Chart.js"); + return null; } + }; - // getters - chart.getElement = function () { - return element; + 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 = /*@__PURE__*/(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); }; - chart.getDataSource = function () { - return chart.dataSource; + + LineChart.prototype.__chartName = function __chartName () { + return "LineChart"; }; - chart.getData = function () { - return chart.data; + + return LineChart; + }(Chart)); + + var PieChart = /*@__PURE__*/(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); }; - chart.getOptions = function () { - return chart.options; + + PieChart.prototype.__chartName = function __chartName () { + return "PieChart"; }; - chart.getChartObject = function () { - return chart.chart; + + return PieChart; + }(Chart)); + + var ColumnChart = /*@__PURE__*/(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); }; - chart.getAdapter = function () { - return chart.adapter; + + ColumnChart.prototype.__chartName = function __chartName () { + return "ColumnChart"; }; - var callback = function () { - chart.data = processData(chart); - renderChart(chartType, chart); + return ColumnChart; + }(Chart)); + + var BarChart = /*@__PURE__*/(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); }; - // functions - chart.updateData = function (dataSource, options) { - chart.dataSource = dataSource; - if (options) { - chart.options = merge(Chartkick.options, options); - } - fetchDataSource(chart, callback, dataSource); + BarChart.prototype.__chartName = function __chartName () { + return "BarChart"; }; - chart.setOptions = function (options) { - chart.options = merge(Chartkick.options, options); - chart.redraw(); + + return BarChart; + }(Chart)); + + var AreaChart = /*@__PURE__*/(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); }; - chart.redraw = function() { - fetchDataSource(chart, callback, chart.rawData); + + AreaChart.prototype.__chartName = function __chartName () { + return "AreaChart"; }; - 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); - } + + return AreaChart; + }(Chart)); + + var GeoChart = /*@__PURE__*/(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); }; - chart.stopRefresh = function () { - if (chart.intervalId) { - clearInterval(chart.intervalId); - } + + GeoChart.prototype.__chartName = function __chartName () { + return "GeoChart"; }; - chart.toImage = function () { - if (chart.adapter === "chartjs") { - return chart.chart.toBase64Image(); - } else { - return null; - } + + return GeoChart; + }(Chart)); + + var ScatterChart = /*@__PURE__*/(function (Chart) { + function ScatterChart () { + Chart.apply(this, arguments); } - Chartkick.charts[element.id] = chart; + if ( Chart ) ScatterChart.__proto__ = Chart; + ScatterChart.prototype = Object.create( Chart && Chart.prototype ); + ScatterChart.prototype.constructor = ScatterChart; - fetchDataSource(chart, callback, dataSource); + ScatterChart.prototype.__processData = function __processData () { + return processSeries(this, "number"); + }; - if (opts.refresh) { - chart.intervalId = setInterval( function () { - chart.refreshData(); - }, opts.refresh * 1000); + ScatterChart.prototype.__chartName = function __chartName () { + return "ScatterChart"; + }; + + return ScatterChart; + }(Chart)); + + var BubbleChart = /*@__PURE__*/(function (Chart) { + function BubbleChart () { + Chart.apply(this, arguments); } - } - // define classes + if ( Chart ) BubbleChart.__proto__ = Chart; + BubbleChart.prototype = Object.create( Chart && Chart.prototype ); + BubbleChart.prototype.constructor = BubbleChart; - Chartkick = { - LineChart: function (element, dataSource, options) { - createChart("LineChart", this, element, dataSource, options, processLineData); - }, - PieChart: function (element, dataSource, options) { - createChart("PieChart", this, element, dataSource, options, processSimple); - }, - ColumnChart: function (element, dataSource, options) { - createChart("ColumnChart", this, element, dataSource, options, processColumnData); - }, - BarChart: function (element, dataSource, options) { - createChart("BarChart", this, element, dataSource, options, processBarData); - }, - AreaChart: function (element, dataSource, options) { - createChart("AreaChart", this, element, dataSource, options, processAreaData); - }, - GeoChart: function (element, dataSource, options) { - createChart("GeoChart", this, element, dataSource, options, processSimple); - }, - ScatterChart: function (element, dataSource, options) { - createChart("ScatterChart", this, element, dataSource, options, processScatterData); - }, - BubbleChart: function (element, dataSource, options) { - createChart("BubbleChart", this, element, dataSource, options, processBubbleData); - }, - Timeline: function (element, dataSource, options) { - createChart("Timeline", this, element, dataSource, options, processTime); - }, + BubbleChart.prototype.__processData = function __processData () { + return processSeries(this, "bubble"); + }; + + BubbleChart.prototype.__chartName = function __chartName () { + return "BubbleChart"; + }; + + return BubbleChart; + }(Chart)); + + var Timeline = /*@__PURE__*/(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]; } } }, + setDefaultOptions: function (opts) { + Chartkick.options = opts; + }, eachChart: function (callback) { for (var chartId in Chartkick.charts) { if (Chartkick.charts.hasOwnProperty(chartId)) { callback(Chartkick.charts[chartId]); } } }, + config: config, options: {}, adapters: adapters, - createChart: createChart + addAdapter: addAdapter, + use: function(adapter) { + addAdapter(adapter); + return Chartkick; + } }; - if (typeof module === "object" && typeof module.exports === "object") { - module.exports = Chartkick; - } else { + // not ideal, but allows for simpler integration + if (typeof window !== "undefined" && !window.Chartkick) { window.Chartkick = Chartkick; } -}(window)); + + // backwards compatibility for esm require + Chartkick.default = Chartkick; + + return Chartkick; + +})));