12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361 |
- /*
- * Chartkick.js
- * Create beautiful JavaScript charts with minimal code
- * https://github.com/ankane/chartkick.js
- * v2.0.0
- * MIT License
- */
- /*jslint browser: true, indent: 2, plusplus: true, vars: true */
- (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;
- // 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;
- }
- // https://github.com/madrobby/zepto/blob/master/src/zepto.js
- function extend(target, source) {
- var key;
- for (key in source) {
- if (isPlainObject(source[key]) || isArray(source[key])) {
- if (isPlainObject(source[key]) && !isPlainObject(target[key])) {
- target[key] = {};
- }
- if (isArray(source[key]) && !isArray(target[key])) {
- target[key] = [];
- }
- extend(target[key], source[key]);
- } else if (source[key] !== undefined) {
- target[key] = source[key];
- }
- }
- }
- function merge(obj1, obj2) {
- var target = {};
- extend(target, obj1);
- extend(target, obj2);
- return target;
- }
- // 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);
- function parseISO8601(input) {
- var day, hour, matches, milliseconds, minutes, month, offset, result, seconds, type, year;
- type = Object.prototype.toString.call(input);
- if (type === '[object Date]') {
- return input;
- }
- if (type !== '[object String]') {
- return;
- }
- matches = input.match(ISO8601_PATTERN);
- if (matches) {
- year = parseInt(matches[1], 10);
- month = parseInt(matches[3], 10) - 1;
- day = parseInt(matches[5], 10);
- hour = parseInt(matches[7], 10);
- minutes = matches[9] ? parseInt(matches[9], 10) : 0;
- seconds = matches[11] ? parseInt(matches[11], 10) : 0;
- milliseconds = matches[12] ? parseFloat(DECIMAL_SEPARATOR + matches[12].slice(1)) * 1000 : 0;
- result = Date.UTC(year, month, day, hour, minutes, seconds, milliseconds);
- if (matches[13] && matches[14]) {
- offset = matches[15] * 60;
- if (matches[17]) {
- offset += parseInt(matches[17], 10);
- }
- offset *= matches[14] === '-' ? -1 : 1;
- result -= offset * 60 * 1000;
- }
- return new Date(result);
- }
- }
- // end iso8601.js
- function negativeValues(series) {
- var i, j, data;
- for (i = 0; i < series.length; i++) {
- data = series[i].data;
- for (j = 0; j < data.length; j++) {
- if (data[j][1] < 0) {
- return true;
- }
- }
- }
- return false;
- }
- function jsOptionsFunc(defaultOptions, hideLegend, setMin, setMax, setStacked, setXtitle, setYtitle) {
- return function (series, opts, chartOptions) {
- var options = merge({}, defaultOptions);
- options = merge(options, chartOptions || {});
- // hide legend
- // this is *not* an external option!
- if (opts.hideLegend) {
- hideLegend(options);
- }
- // min
- if ("min" in opts) {
- setMin(options, opts.min);
- } else if (!negativeValues(series)) {
- setMin(options, 0);
- }
- // max
- if (opts.max) {
- setMax(options, opts.max);
- }
- if ("stacked" in opts) {
- setStacked(options, opts.stacked);
- }
- if (opts.colors) {
- options.colors = opts.colors;
- }
- if (opts.xtitle) {
- setXtitle(options, opts.xtitle);
- }
- if (opts.ytitle) {
- setYtitle(options, opts.ytitle);
- }
- // merge library last
- options = merge(options, opts.library || {});
- return options;
- };
- }
- function setText(element, text) {
- if (document.body.innerText) {
- element.innerText = text;
- } else {
- element.textContent = text;
- }
- }
- function chartError(element, message) {
- setText(element, "Error Loading Chart: " + message);
- element.style.color = "#ff0000";
- }
- function getJSON(element, url, success) {
- var $ = window.jQuery || window.Zepto || window.$;
- $.ajax({
- dataType: "json",
- url: url,
- success: success,
- error: function (jqXHR, textStatus, errorThrown) {
- var message = (typeof errorThrown === "string") ? errorThrown : errorThrown.message;
- chartError(element, message);
- }
- });
- }
- function errorCatcher(chart, callback) {
- try {
- callback(chart);
- } catch (err) {
- chartError(chart.element, err.message);
- throw err;
- }
- }
- function fetchDataSource(chart, callback) {
- if (typeof chart.dataSource === "string") {
- getJSON(chart.element, chart.dataSource, function (data, textStatus, jqXHR) {
- chart.data = data;
- errorCatcher(chart, callback);
- });
- } else {
- chart.data = chart.dataSource;
- errorCatcher(chart, callback);
- }
- }
- // type conversions
- 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 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 sortByTime(a, b) {
- return a[0].getTime() - b[0].getTime();
- }
- function sortByNumber(a, b) {
- return a - b;
- }
- function loadAdapters() {
- if (!HighchartsAdapter && "Highcharts" in window) {
- HighchartsAdapter = new function () {
- var Highcharts = window.Highcharts;
- this.name = "highcharts";
- 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 hideLegend = function (options) {
- options.legend.enabled = false;
- };
- var setMin = function (options, min) {
- options.yAxis.min = min;
- };
- var setMax = function (options, max) {
- options.yAxis.max = max;
- };
- var setStacked = function (options, stacked) {
- options.plotOptions.series.stacking = stacked ? "normal" : null;
- };
- var setXtitle = function (options, title) {
- options.xAxis.title.text = title;
- };
- var setYtitle = function (options, title) {
- options.yAxis.title.text = title;
- };
- var jsOptions = jsOptionsFunc(defaultOptions, hideLegend, setMin, setMax, setStacked, setXtitle, setYtitle);
- this.renderLineChart = function (chart, chartType) {
- chartType = chartType || "spline";
- var chartOptions = {};
- if (chartType === "areaspline") {
- chartOptions = {
- plotOptions: {
- areaspline: {
- stacking: "normal"
- },
- series: {
- marker: {
- enabled: false
- }
- }
- }
- };
- }
- var options = jsOptions(chart.data, chart.options, chartOptions), data, i, j;
- options.xAxis.type = chart.options.discrete ? "category" : "datetime";
- options.chart.type = chartType;
- options.chart.renderTo = chart.element.id;
- var series = chart.data;
- for (i = 0; i < series.length; i++) {
- data = series[i].data;
- if (!chart.options.discrete) {
- for (j = 0; j < data.length; j++) {
- data[j][0] = data[j][0].getTime();
- }
- }
- series[i].marker = {symbol: "circle"};
- }
- options.series = series;
- new Highcharts.Chart(options);
- };
- this.renderScatterChart = function (chart) {
- var chartOptions = {};
- var options = jsOptions(chart.data, chart.options, chartOptions);
- options.chart.type = 'scatter';
- options.chart.renderTo = chart.element.id;
- options.series = chart.data;
- new Highcharts.Chart(options);
- };
- this.renderPieChart = function (chart) {
- var chartOptions = {};
- if (chart.options.colors) {
- chartOptions.colors = chart.options.colors;
- }
- var options = merge(merge(defaultOptions, chartOptions), chart.options.library || {});
- options.chart.renderTo = chart.element.id;
- options.series = [{
- type: "pie",
- name: chart.options.label || "Value",
- data: chart.data
- }];
- new Highcharts.Chart(options);
- };
- this.renderColumnChart = function (chart, chartType) {
- chartType = chartType || "column";
- var series = chart.data;
- var options = jsOptions(series, chart.options), i, j, s, d, rows = [];
- options.chart.type = chartType;
- options.chart.renderTo = chart.element.id;
- for (i = 0; i < series.length; i++) {
- s = series[i];
- for (j = 0; j < s.data.length; j++) {
- d = s.data[j];
- if (!rows[d[0]]) {
- rows[d[0]] = new Array(series.length);
- }
- rows[d[0]][i] = d[1];
- }
- }
- var categories = [];
- for (i in rows) {
- if (rows.hasOwnProperty(i)) {
- categories.push(i);
- }
- }
- options.xAxis.categories = categories;
- var newSeries = [];
- for (i = 0; i < series.length; i++) {
- d = [];
- for (j = 0; j < categories.length; j++) {
- d.push(rows[categories[j]][i] || 0);
- }
- newSeries.push({
- name: series[i].name,
- data: d
- });
- }
- options.series = newSeries;
- new Highcharts.Chart(options);
- };
- var self = this;
- this.renderBarChart = function (chart) {
- self.renderColumnChart(chart, "bar");
- };
- this.renderAreaChart = function (chart) {
- self.renderLineChart(chart, "areaspline");
- };
- };
- adapters.push(HighchartsAdapter);
- }
- if (!GoogleChartsAdapter && window.google && window.google.setOnLoadCallback) {
- GoogleChartsAdapter = new function () {
- var google = window.google;
- this.name = "google";
- var loaded = {};
- var callbacks = [];
- 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--;
- }
- }
- };
- var waitForLoaded = function (pack, callback) {
- if (!callback) {
- callback = pack;
- pack = "corechart";
- }
- callbacks.push({pack: pack, callback: callback});
- if (loaded[pack]) {
- runCallbacks();
- } else {
- loaded[pack] = true;
- // https://groups.google.com/forum/#!topic/google-visualization-api/fMKJcyA2yyI
- var loadOptions = {
- packages: [pack],
- callback: runCallbacks
- };
- if (config.language) {
- loadOptions.language = config.language;
- }
- google.load("visualization", "1", loadOptions);
- }
- };
- // 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"
- },
- 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
- }
- }
- };
- var hideLegend = function (options) {
- options.legend.position = "none";
- };
- var setMin = function (options, min) {
- options.vAxis.viewWindow.min = min;
- };
- var setMax = function (options, max) {
- options.vAxis.viewWindow.max = max;
- };
- var setBarMin = function (options, min) {
- options.hAxis.viewWindow.min = min;
- };
- var setBarMax = function (options, max) {
- options.hAxis.viewWindow.max = max;
- };
- var setStacked = function (options, stacked) {
- options.isStacked = !!stacked;
- };
- var setXtitle = function (options, title) {
- options.hAxis.title = title;
- options.hAxis.titleTextStyle.italic = false;
- };
- var setYtitle = function (options, title) {
- options.vAxis.title = title;
- options.vAxis.titleTextStyle.italic = false;
- };
- var jsOptions = jsOptionsFunc(defaultOptions, hideLegend, setMin, setMax, setStacked, setXtitle, setYtitle);
- // cant use object as key
- var createDataTable = function (series, columnType) {
- var i, j, s, d, key, rows = [];
- for (i = 0; i < series.length; i++) {
- s = series[i];
- 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);
- }
- rows[key][i] = toFloat(d[1]);
- }
- }
- var rows2 = [];
- var day = true;
- var value;
- for (i in rows) {
- if (rows.hasOwnProperty(i)) {
- if (columnType === "datetime") {
- value = new Date(toFloat(i));
- day = day && isDay(value);
- } else if (columnType === "number") {
- value = toFloat(i);
- } else {
- value = i;
- }
- rows2.push([value].concat(rows[i]));
- }
- }
- if (columnType === "datetime") {
- rows2.sort(sortByTime);
- }
- // 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);
- return data;
- };
- var resize = function (callback) {
- if (window.attachEvent) {
- window.attachEvent("onresize", callback);
- } else if (window.addEventListener) {
- window.addEventListener("resize", callback, true);
- }
- callback();
- };
- this.renderLineChart = function (chart) {
- waitForLoaded(function () {
- var options = jsOptions(chart.data, chart.options);
- var data = createDataTable(chart.data, chart.options.discrete ? "string" : "datetime");
- chart.chart = new google.visualization.LineChart(chart.element);
- resize(function () {
- chart.chart.draw(data, options);
- });
- });
- };
- this.renderPieChart = function (chart) {
- waitForLoaded(function () {
- var chartOptions = {
- chartArea: {
- top: "10%",
- height: "80%"
- }
- };
- if (chart.options.colors) {
- chartOptions.colors = chart.options.colors;
- }
- var options = merge(merge(defaultOptions, chartOptions), chart.options.library || {});
- var data = new google.visualization.DataTable();
- data.addColumn("string", "");
- data.addColumn("number", "Value");
- data.addRows(chart.data);
- chart.chart = new google.visualization.PieChart(chart.element);
- resize(function () {
- chart.chart.draw(data, options);
- });
- });
- };
- this.renderColumnChart = function (chart) {
- waitForLoaded(function () {
- var options = jsOptions(chart.data, chart.options);
- var data = createDataTable(chart.data, "string");
- chart.chart = new google.visualization.ColumnChart(chart.element);
- resize(function () {
- chart.chart.draw(data, options);
- });
- });
- };
- this.renderBarChart = function (chart) {
- waitForLoaded(function () {
- var chartOptions = {
- hAxis: {
- gridlines: {
- color: "#ccc"
- }
- }
- };
- var options = jsOptionsFunc(defaultOptions, hideLegend, setBarMin, setBarMax, setStacked)(chart.data, chart.options, chartOptions);
- var data = createDataTable(chart.data, "string");
- chart.chart = new google.visualization.BarChart(chart.element);
- resize(function () {
- chart.chart.draw(data, options);
- });
- });
- };
- this.renderAreaChart = function (chart) {
- waitForLoaded(function () {
- var chartOptions = {
- isStacked: true,
- pointSize: 0,
- areaOpacity: 0.5
- };
- var options = jsOptions(chart.data, chart.options, chartOptions);
- var data = createDataTable(chart.data, chart.options.discrete ? "string" : "datetime");
- chart.chart = new google.visualization.AreaChart(chart.element);
- resize(function () {
- chart.chart.draw(data, options);
- });
- });
- };
- 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 data = new google.visualization.DataTable();
- data.addColumn("string", "");
- data.addColumn("number", chart.options.label || "Value");
- data.addRows(chart.data);
- chart.chart = new google.visualization.GeoChart(chart.element);
- resize(function () {
- chart.chart.draw(data, options);
- });
- });
- };
- this.renderScatterChart = function (chart) {
- waitForLoaded(function () {
- var chartOptions = {};
- var options = jsOptions(chart.data, chart.options, chartOptions);
- var data = createDataTable(chart.data, "number");
- chart.chart = new google.visualization.ScatterChart(chart.element);
- resize(function () {
- chart.chart.draw(data, options);
- });
- });
- };
- this.renderTimeline = function (chart) {
- waitForLoaded("timeline", function () {
- var chartOptions = {
- legend: "none"
- };
- if (chart.options.colors) {
- chartOptions.colors = chart.options.colors;
- }
- var options = merge(merge(defaultOptions, chartOptions), chart.options.library || {});
- 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);
- chart.element.style.lineHeight = "normal";
- chart.chart = new google.visualization.Timeline(chart.element);
- resize(function () {
- chart.chart.draw(data, options);
- });
- });
- };
- };
- adapters.push(GoogleChartsAdapter);
- }
- if (!ChartjsAdapter && "Chart" in window) {
- ChartjsAdapter = new function () {
- var Chart = window.Chart;
- this.name = "chartjs";
- var baseOptions = {
- maintainAspectRatio: false,
- animation: false
- };
- 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: {}
- }
- ]
- },
- legend: {}
- };
- // 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 hideLegend = function (options) {
- options.legend.display = false;
- };
- var setMin = function (options, min) {
- if (min !== null) {
- options.scales.yAxes[0].ticks.min = min;
- }
- };
- var setMax = function (options, max) {
- options.scales.yAxes[0].ticks.max = max;
- };
- var setBarMin = function (options, min) {
- if (min !== null) {
- options.scales.xAxes[0].ticks.min = min;
- }
- };
- var setBarMax = function (options, max) {
- options.scales.xAxes[0].ticks.max = max;
- };
- var setStacked = function (options, stacked) {
- options.scales.xAxes[0].stacked = !!stacked;
- options.scales.yAxes[0].stacked = !!stacked;
- };
- var setXtitle = function (options, title) {
- options.scales.xAxes[0].scaleLabel.display = true;
- options.scales.xAxes[0].scaleLabel.labelString = title;
- };
- var setYtitle = function (options, title) {
- options.scales.yAxes[0].scaleLabel.display = true;
- options.scales.yAxes[0].scaleLabel.labelString = title;
- };
- var drawChart = function(chart, type, data, options) {
- chart.element.innerHTML = "<canvas></canvas>";
- var ctx = chart.element.getElementsByTagName("CANVAS")[0];
- chart.chart = new Chart(ctx, {
- type: type,
- data: data,
- options: 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;
- };
- 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;
- }
- };
- };
- var jsOptions = jsOptionsFunc(merge(baseOptions, defaultOptions), hideLegend, setMin, setMax, setStacked, setXtitle, setYtitle);
- var createDataTable = function (chart, options, chartType) {
- var datasets = [];
- var labels = [];
- var colors = chart.options.colors || defaultColors;
- var day = true;
- var week = true;
- var dayOfWeek;
- var month = true;
- var year = true;
- var hour = true;
- var minute = true;
- var detectType = (chartType === "line" || chartType === "area") && !chart.options.discrete;
- var series = chart.data;
- var sortedLabels = [];
- var i, j, s, d, key, rows = [];
- for (i = 0; i < series.length; i++) {
- s = series[i];
- 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);
- }
- }
- }
- if (detectType) {
- sortedLabels.sort(sortByNumber);
- }
- var rows2 = [];
- for (j = 0; j < series.length; j++) {
- rows2.push([]);
- }
- 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++) {
- rows2[j].push(rows[i][j]);
- }
- }
- for (i = 0; i < series.length; i++) {
- s = series[i];
- var backgroundColor = chartType !== "line" ? addOpacity(colors[i], 0.5) : colors[i];
- var dataset = {
- label: s.name,
- data: rows2[i],
- fill: chartType === "area",
- borderColor: colors[i],
- backgroundColor: backgroundColor,
- pointBackgroundColor: colors[i],
- borderWidth: 2
- };
- datasets.push(merge(dataset, s.library || {}));
- }
- 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;
- }
- }
- 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) {
- options.scales.xAxes[0].time.unit = "hour";
- step = 1 / 24.0;
- } else if (minute) {
- options.scales.xAxes[0].time.displayFormats = {minute: "h:mm a"};
- options.scales.xAxes[0].time.unit = "minute";
- step = 1 / 24.0 / 60.0;
- }
- if (step && timeDiff > 0) {
- var unitStepSize = Math.ceil(timeDiff / step / (chart.element.offsetWidth / 100.0));
- if (week && step === 1) {
- unitStepSize = Math.ceil(unitStepSize / 7.0) * 7;
- }
- options.scales.xAxes[0].time.unitStepSize = unitStepSize;
- }
- }
- if (!options.scales.xAxes[0].time.tooltipFormat) {
- if (day) {
- options.scales.xAxes[0].time.tooltipFormat = "ll";
- } else if (hour) {
- options.scales.xAxes[0].time.tooltipFormat = "MMM D, h a";
- } else if (minute) {
- options.scales.xAxes[0].time.tooltipFormat = "h:mm a";
- }
- }
- }
- var data = {
- labels: labels,
- datasets: datasets
- };
- return data;
- };
- this.renderLineChart = function (chart, chartType) {
- var areaOptions = {};
- if (chartType === "area") {
- // TODO fix area stacked
- // areaOptions.stacked = true;
- }
- // fix for https://github.com/chartjs/Chart.js/issues/2441
- if (!chart.options.max && allZeros(chart.data)) {
- chart.options.max = 1;
- }
- var options = jsOptions(chart.data, merge(areaOptions, chart.options));
- var data = createDataTable(chart, options, chartType || "line");
- options.scales.xAxes[0].type = chart.options.discrete ? "category" : "time";
- drawChart(chart, "line", data, options);
- };
- this.renderPieChart = function (chart) {
- var options = merge(baseOptions, chart.options.library || {});
- 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 data = {
- labels: labels,
- datasets: [
- {
- data: values,
- backgroundColor: chart.options.colors || defaultColors
- }
- ]
- };
- drawChart(chart, "pie", data, options);
- };
- this.renderColumnChart = function (chart, chartType) {
- var options;
- if (chartType === "bar") {
- options = jsOptionsFunc(merge(baseOptions, defaultOptions), hideLegend, setBarMin, setBarMax, setStacked)(chart.data, chart.options);
- } else {
- options = jsOptions(chart.data, chart.options);
- }
- var data = createDataTable(chart, options, "column");
- setLabelSize(chart, data, options);
- drawChart(chart, (chartType === "bar" ? "horizontalBar" : "bar"), data, options);
- };
- var self = this;
- this.renderAreaChart = function (chart) {
- self.renderLineChart(chart, "area");
- };
- this.renderBarChart = function (chart) {
- self.renderColumnChart(chart, "bar");
- };
- };
- adapters.unshift(ChartjsAdapter);
- }
- }
- // TODO remove chartType if cross-browser way
- // to get the name of the chart class
- function renderChart(chartType, chart) {
- var i, adapter, fnName, adapterName;
- fnName = "render" + chartType;
- adapterName = chart.options.adapter;
- loadAdapters();
- for (i = 0; i < adapters.length; i++) {
- adapter = adapters[i];
- if ((!adapterName || adapterName === adapter.name) && isFunction(adapter[fnName])) {
- return adapter[fnName](chart);
- }
- }
- throw new Error("No adapter found");
- }
- // process data
- var toFormattedKey = function (key, keyType) {
- if (keyType === "number") {
- key = toFloat(key);
- } else if (keyType === "datetime") {
- key = toDate(key);
- } else {
- key = toStr(key);
- }
- return key;
- };
- var formatSeriesData = function (data, keyType) {
- var r = [], key, j;
- for (j = 0; j < data.length; j++) {
- key = toFormattedKey(data[j][0], keyType);
- r.push([key, toFloat(data[j][1])]);
- }
- if (keyType === "datetime") {
- r.sort(sortByTime);
- }
- return r;
- };
- function isMinute(d) {
- return d.getMilliseconds() === 0 && d.getSeconds() === 0;
- }
- function isHour(d) {
- return isMinute(d) && d.getMinutes() === 0;
- }
- function isDay(d) {
- return isHour(d) && d.getHours() === 0;
- }
- function isWeek(d, dayOfWeek) {
- return isDay(d) && d.getDay() === dayOfWeek;
- }
- function isMonth(d) {
- return isDay(d) && d.getDate() === 1;
- }
- function isYear(d) {
- return isMonth(d) && d.getMonth() === 0;
- }
- function isDate(obj) {
- return !isNaN(toDate(obj)) && toStr(obj).length >= 6;
- }
- function allZeros(data) {
- var i, j, d;
- for (i = 0; i < data.length; i++) {
- d = data[i].data;
- for (j = 0; j < d.length; j++) {
- if (d[j][1] != 0) {
- return false;
- }
- }
- }
- return true;
- }
- function detectDiscrete(series) {
- var i, j, data;
- 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;
- }
- }
- }
- return false;
- }
- function processSeries(series, opts, keyType) {
- var i;
- // see if one series or multiple
- if (!isArray(series) || typeof series[0] !== "object" || isArray(series[0])) {
- series = [{name: opts.label || "Value", data: series}];
- opts.hideLegend = true;
- } else {
- opts.hideLegend = false;
- }
- if ((opts.discrete === null || opts.discrete === undefined)) {
- opts.discrete = detectDiscrete(series);
- }
- if (opts.discrete) {
- keyType = "string";
- }
- // right format
- for (i = 0; i < series.length; i++) {
- series[i].data = formatSeriesData(toArr(series[i].data), keyType);
- }
- return series;
- }
- function processSimple(data) {
- var perfectData = toArr(data), i;
- for (i = 0; i < perfectData.length; i++) {
- perfectData[i] = [toStr(perfectData[i][0]), toFloat(perfectData[i][1])];
- }
- return perfectData;
- }
- function processTime(data)
- {
- var i;
- for (i = 0; i < data.length; i++) {
- data[i][1] = toDate(data[i][1]);
- data[i][2] = toDate(data[i][2]);
- }
- return data;
- }
- function processLineData(chart) {
- chart.data = processSeries(chart.data, chart.options, "datetime");
- renderChart("LineChart", chart);
- }
- function processColumnData(chart) {
- chart.data = processSeries(chart.data, chart.options, "string");
- renderChart("ColumnChart", chart);
- }
- function processPieData(chart) {
- chart.data = processSimple(chart.data);
- renderChart("PieChart", chart);
- }
- function processBarData(chart) {
- chart.data = processSeries(chart.data, chart.options, "string");
- renderChart("BarChart", chart);
- }
- function processAreaData(chart) {
- chart.data = processSeries(chart.data, chart.options, "datetime");
- renderChart("AreaChart", chart);
- }
- function processGeoData(chart) {
- chart.data = processSimple(chart.data);
- renderChart("GeoChart", chart);
- }
- function processScatterData(chart) {
- chart.data = processSeries(chart.data, chart.options, "number");
- renderChart("ScatterChart", chart);
- }
- function processTimelineData(chart) {
- chart.data = processTime(chart.data);
- renderChart("Timeline", chart);
- }
- function setElement(chart, element, dataSource, opts, callback) {
- var elementId;
- if (typeof element === "string") {
- elementId = element;
- element = document.getElementById(element);
- if (!element) {
- throw new Error("No element with id " + elementId);
- }
- }
- chart.element = element;
- chart.options = opts || {};
- chart.dataSource = dataSource;
- chart.getElement = function () {
- return element;
- };
- chart.getData = function () {
- return chart.data;
- };
- chart.getOptions = function () {
- return opts || {};
- };
- chart.getChartObject = function () {
- return chart.chart;
- };
- Chartkick.charts[element.id] = chart;
- fetchDataSource(chart, callback);
- }
- // define classes
- Chartkick = {
- LineChart: function (element, dataSource, opts) {
- setElement(this, element, dataSource, opts, processLineData);
- },
- PieChart: function (element, dataSource, opts) {
- setElement(this, element, dataSource, opts, processPieData);
- },
- ColumnChart: function (element, dataSource, opts) {
- setElement(this, element, dataSource, opts, processColumnData);
- },
- BarChart: function (element, dataSource, opts) {
- setElement(this, element, dataSource, opts, processBarData);
- },
- AreaChart: function (element, dataSource, opts) {
- setElement(this, element, dataSource, opts, processAreaData);
- },
- GeoChart: function (element, dataSource, opts) {
- setElement(this, element, dataSource, opts, processGeoData);
- },
- ScatterChart: function (element, dataSource, opts) {
- setElement(this, element, dataSource, opts, processScatterData);
- },
- Timeline: function (element, dataSource, opts) {
- setElement(this, element, dataSource, opts, processTimelineData);
- },
- charts: {}
- };
- if (typeof module === "object" && typeof module.exports === "object") {
- module.exports = Chartkick;
- } else {
- window.Chartkick = Chartkick;
- }
- }(window));
|