chartkick.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935
  1. /*
  2. * Chartkick.js
  3. * Create beautiful Javascript charts with minimal code
  4. * https://github.com/ankane/chartkick.js
  5. * v1.4.1
  6. * MIT License
  7. */
  8. /*jslint browser: true, indent: 2, plusplus: true, vars: true */
  9. (function (window) {
  10. 'use strict';
  11. var config = window.Chartkick || {};
  12. var Chartkick, DATE_PATTERN, ISO8601_PATTERN, DECIMAL_SEPARATOR, adapters = [];
  13. DATE_PATTERN = /^(\d\d\d\d)(\-)?(\d\d)(\-)?(\d\d)$/i;
  14. // helpers
  15. function isArray(variable) {
  16. return Object.prototype.toString.call(variable) === "[object Array]";
  17. }
  18. function isFunction(variable) {
  19. return variable instanceof Function;
  20. }
  21. function isPlainObject(variable) {
  22. return !isFunction(variable) && variable instanceof Object;
  23. }
  24. // https://github.com/madrobby/zepto/blob/master/src/zepto.js
  25. function extend(target, source) {
  26. var key;
  27. for (key in source) {
  28. if (isPlainObject(source[key]) || isArray(source[key])) {
  29. if (isPlainObject(source[key]) && !isPlainObject(target[key])) {
  30. target[key] = {};
  31. }
  32. if (isArray(source[key]) && !isArray(target[key])) {
  33. target[key] = [];
  34. }
  35. extend(target[key], source[key]);
  36. } else if (source[key] !== undefined) {
  37. target[key] = source[key];
  38. }
  39. }
  40. }
  41. function merge(obj1, obj2) {
  42. var target = {};
  43. extend(target, obj1);
  44. extend(target, obj2);
  45. return target;
  46. }
  47. // https://github.com/Do/iso8601.js
  48. ISO8601_PATTERN = /(\d\d\d\d)(\-)?(\d\d)(\-)?(\d\d)(T)?(\d\d)(:)?(\d\d)?(:)?(\d\d)?([\.,]\d+)?($|Z|([\+\-])(\d\d)(:)?(\d\d)?)/i;
  49. DECIMAL_SEPARATOR = String(1.5).charAt(1);
  50. function parseISO8601(input) {
  51. var day, hour, matches, milliseconds, minutes, month, offset, result, seconds, type, year;
  52. type = Object.prototype.toString.call(input);
  53. if (type === '[object Date]') {
  54. return input;
  55. }
  56. if (type !== '[object String]') {
  57. return;
  58. }
  59. if (matches = input.match(ISO8601_PATTERN)) {
  60. year = parseInt(matches[1], 10);
  61. month = parseInt(matches[3], 10) - 1;
  62. day = parseInt(matches[5], 10);
  63. hour = parseInt(matches[7], 10);
  64. minutes = matches[9] ? parseInt(matches[9], 10) : 0;
  65. seconds = matches[11] ? parseInt(matches[11], 10) : 0;
  66. milliseconds = matches[12] ? parseFloat(DECIMAL_SEPARATOR + matches[12].slice(1)) * 1000 : 0;
  67. result = Date.UTC(year, month, day, hour, minutes, seconds, milliseconds);
  68. if (matches[13] && matches[14]) {
  69. offset = matches[15] * 60;
  70. if (matches[17]) {
  71. offset += parseInt(matches[17], 10);
  72. }
  73. offset *= matches[14] === '-' ? -1 : 1;
  74. result -= offset * 60 * 1000;
  75. }
  76. return new Date(result);
  77. }
  78. }
  79. // end iso8601.js
  80. function negativeValues(series) {
  81. var i, j, data;
  82. for (i = 0; i < series.length; i++) {
  83. data = series[i].data;
  84. for (j = 0; j < data.length; j++) {
  85. if (data[j][1] < 0) {
  86. return true;
  87. }
  88. }
  89. }
  90. return false;
  91. }
  92. function jsOptionsFunc(defaultOptions, hideLegend, setMin, setMax, setStacked, setXtitle, setYtitle) {
  93. return function (series, opts, chartOptions) {
  94. var options = merge({}, defaultOptions);
  95. options = merge(options, chartOptions || {});
  96. // hide legend
  97. // this is *not* an external option!
  98. if (opts.hideLegend) {
  99. hideLegend(options);
  100. }
  101. // min
  102. if ("min" in opts) {
  103. setMin(options, opts.min);
  104. } else if (!negativeValues(series)) {
  105. setMin(options, 0);
  106. }
  107. // max
  108. if (opts.max) {
  109. setMax(options, opts.max);
  110. }
  111. if (opts.stacked) {
  112. setStacked(options);
  113. }
  114. if (opts.colors) {
  115. options.colors = opts.colors;
  116. }
  117. if (opts.xtitle) {
  118. setXtitle(options, opts.xtitle);
  119. }
  120. if (opts.ytitle) {
  121. setYtitle(options, opts.ytitle);
  122. }
  123. // merge library last
  124. options = merge(options, opts.library || {});
  125. return options;
  126. };
  127. }
  128. function setText(element, text) {
  129. if (document.body.innerText) {
  130. element.innerText = text;
  131. } else {
  132. element.textContent = text;
  133. }
  134. }
  135. function chartError(element, message) {
  136. setText(element, "Error Loading Chart: " + message);
  137. element.style.color = "#ff0000";
  138. }
  139. function getJSON(element, url, success) {
  140. var $ = window.jQuery || window.Zepto || window.$;
  141. $.ajax({
  142. dataType: "json",
  143. url: url,
  144. success: success,
  145. error: function (jqXHR, textStatus, errorThrown) {
  146. var message = (typeof errorThrown === "string") ? errorThrown : errorThrown.message;
  147. chartError(element, message);
  148. }
  149. });
  150. }
  151. function errorCatcher(chart, callback) {
  152. try {
  153. callback(chart);
  154. } catch (err) {
  155. chartError(chart.element, err.message);
  156. throw err;
  157. }
  158. }
  159. function fetchDataSource(chart, callback) {
  160. if (typeof chart.dataSource === "string") {
  161. getJSON(chart.element, chart.dataSource, function (data, textStatus, jqXHR) {
  162. chart.data = data;
  163. errorCatcher(chart, callback);
  164. });
  165. } else {
  166. chart.data = chart.dataSource;
  167. errorCatcher(chart, callback);
  168. }
  169. }
  170. // type conversions
  171. function toStr(n) {
  172. return "" + n;
  173. }
  174. function toFloat(n) {
  175. return parseFloat(n);
  176. }
  177. function toDate(n) {
  178. var matches, year, month, day;
  179. if (typeof n !== "object") {
  180. if (typeof n === "number") {
  181. n = new Date(n * 1000); // ms
  182. } else if (matches = n.match(DATE_PATTERN)) {
  183. year = parseInt(matches[1], 10);
  184. month = parseInt(matches[3], 10) - 1;
  185. day = parseInt(matches[5], 10);
  186. return new Date(year, month, day);
  187. } else { // str
  188. // try our best to get the str into iso8601
  189. // TODO be smarter about this
  190. var str = n.replace(/ /, "T").replace(" ", "").replace("UTC", "Z");
  191. n = parseISO8601(str) || new Date(n);
  192. }
  193. }
  194. return n;
  195. }
  196. function toArr(n) {
  197. if (!isArray(n)) {
  198. var arr = [], i;
  199. for (i in n) {
  200. if (n.hasOwnProperty(i)) {
  201. arr.push([i, n[i]]);
  202. }
  203. }
  204. n = arr;
  205. }
  206. return n;
  207. }
  208. function sortByTime(a, b) {
  209. return a[0].getTime() - b[0].getTime();
  210. }
  211. if ("Highcharts" in window) {
  212. var HighchartsAdapter = new function () {
  213. var Highcharts = window.Highcharts;
  214. this.name = "highcharts";
  215. var defaultOptions = {
  216. chart: {},
  217. xAxis: {
  218. title: {
  219. text: null
  220. },
  221. labels: {
  222. style: {
  223. fontSize: "12px"
  224. }
  225. }
  226. },
  227. yAxis: {
  228. title: {
  229. text: null
  230. },
  231. labels: {
  232. style: {
  233. fontSize: "12px"
  234. }
  235. }
  236. },
  237. title: {
  238. text: null
  239. },
  240. credits: {
  241. enabled: false
  242. },
  243. legend: {
  244. borderWidth: 0
  245. },
  246. tooltip: {
  247. style: {
  248. fontSize: "12px"
  249. }
  250. },
  251. plotOptions: {
  252. areaspline: {},
  253. series: {
  254. marker: {}
  255. }
  256. }
  257. };
  258. var hideLegend = function (options) {
  259. options.legend.enabled = false;
  260. };
  261. var setMin = function (options, min) {
  262. options.yAxis.min = min;
  263. };
  264. var setMax = function (options, max) {
  265. options.yAxis.max = max;
  266. };
  267. var setStacked = function (options) {
  268. options.plotOptions.series.stacking = "normal";
  269. };
  270. var setXtitle = function (options, title) {
  271. options.xAxis.title.text = title;
  272. };
  273. var setYtitle = function (options, title) {
  274. options.yAxis.title.text = title;
  275. };
  276. var jsOptions = jsOptionsFunc(defaultOptions, hideLegend, setMin, setMax, setStacked, setXtitle, setYtitle);
  277. this.renderLineChart = function (chart, chartType) {
  278. chartType = chartType || "spline";
  279. var chartOptions = {};
  280. if (chartType === "areaspline") {
  281. chartOptions = {
  282. plotOptions: {
  283. areaspline: {
  284. stacking: "normal"
  285. },
  286. series: {
  287. marker: {
  288. enabled: false
  289. }
  290. }
  291. }
  292. };
  293. }
  294. var options = jsOptions(chart.data, chart.options, chartOptions), data, i, j;
  295. options.xAxis.type = chart.options.discrete ? "category" : "datetime";
  296. options.chart.type = chartType;
  297. options.chart.renderTo = chart.element.id;
  298. var series = chart.data;
  299. for (i = 0; i < series.length; i++) {
  300. data = series[i].data;
  301. if (!chart.options.discrete) {
  302. for (j = 0; j < data.length; j++) {
  303. data[j][0] = data[j][0].getTime();
  304. }
  305. }
  306. series[i].marker = {symbol: "circle"};
  307. }
  308. options.series = series;
  309. new Highcharts.Chart(options);
  310. };
  311. this.renderScatterChart = function (chart) {
  312. var chartOptions = {};
  313. var options = jsOptions(chart.data, chart.options, chartOptions);
  314. options.chart.type = 'scatter';
  315. options.chart.renderTo = chart.element.id;
  316. options.series = chart.data;
  317. new Highcharts.Chart(options);
  318. };
  319. this.renderPieChart = function (chart) {
  320. var chartOptions = {};
  321. if (chart.options.colors) {
  322. chartOptions.colors = chart.options.colors;
  323. }
  324. var options = merge(merge(defaultOptions, chartOptions), chart.options.library || {});
  325. options.chart.renderTo = chart.element.id;
  326. options.series = [{
  327. type: "pie",
  328. name: "Value",
  329. data: chart.data
  330. }];
  331. new Highcharts.Chart(options);
  332. };
  333. this.renderColumnChart = function (chart, chartType) {
  334. var chartType = chartType || "column";
  335. var series = chart.data;
  336. var options = jsOptions(series, chart.options), i, j, s, d, rows = [];
  337. options.chart.type = chartType;
  338. options.chart.renderTo = chart.element.id;
  339. for (i = 0; i < series.length; i++) {
  340. s = series[i];
  341. for (j = 0; j < s.data.length; j++) {
  342. d = s.data[j];
  343. if (!rows[d[0]]) {
  344. rows[d[0]] = new Array(series.length);
  345. }
  346. rows[d[0]][i] = d[1];
  347. }
  348. }
  349. var categories = [];
  350. for (i in rows) {
  351. if (rows.hasOwnProperty(i)) {
  352. categories.push(i);
  353. }
  354. }
  355. options.xAxis.categories = categories;
  356. var newSeries = [];
  357. for (i = 0; i < series.length; i++) {
  358. d = [];
  359. for (j = 0; j < categories.length; j++) {
  360. d.push(rows[categories[j]][i] || 0);
  361. }
  362. newSeries.push({
  363. name: series[i].name,
  364. data: d
  365. });
  366. }
  367. options.series = newSeries;
  368. new Highcharts.Chart(options);
  369. };
  370. var self = this;
  371. this.renderBarChart = function (chart) {
  372. self.renderColumnChart(chart, "bar");
  373. };
  374. this.renderAreaChart = function (chart) {
  375. self.renderLineChart(chart, "areaspline");
  376. };
  377. };
  378. adapters.push(HighchartsAdapter);
  379. }
  380. if (window.google && window.google.setOnLoadCallback) {
  381. var GoogleChartsAdapter = new function () {
  382. var google = window.google;
  383. this.name = "google";
  384. var loaded = {};
  385. var callbacks = [];
  386. var runCallbacks = function () {
  387. var cb, call;
  388. for (var i = 0; i < callbacks.length; i++) {
  389. cb = callbacks[i];
  390. call = google.visualization && ((cb.pack === "corechart" && google.visualization.LineChart) || (cb.pack === "timeline" && google.visualization.Timeline))
  391. if (call) {
  392. cb.callback();
  393. callbacks.splice(i, 1);
  394. i--;
  395. }
  396. }
  397. };
  398. var waitForLoaded = function (pack, callback) {
  399. if (!callback) {
  400. callback = pack;
  401. pack = "corechart";
  402. }
  403. callbacks.push({pack: pack, callback: callback});
  404. if (loaded[pack]) {
  405. runCallbacks();
  406. } else {
  407. loaded[pack] = true;
  408. // https://groups.google.com/forum/#!topic/google-visualization-api/fMKJcyA2yyI
  409. var loadOptions = {
  410. packages: [pack],
  411. callback: runCallbacks
  412. };
  413. if (config.language) {
  414. loadOptions.language = config.language;
  415. }
  416. google.load("visualization", "1", loadOptions);
  417. }
  418. };
  419. // Set chart options
  420. var defaultOptions = {
  421. chartArea: {},
  422. fontName: "'Lucida Grande', 'Lucida Sans Unicode', Verdana, Arial, Helvetica, sans-serif",
  423. pointSize: 6,
  424. legend: {
  425. textStyle: {
  426. fontSize: 12,
  427. color: "#444"
  428. },
  429. alignment: "center",
  430. position: "right"
  431. },
  432. curveType: "function",
  433. hAxis: {
  434. textStyle: {
  435. color: "#666",
  436. fontSize: 12
  437. },
  438. titleTextStyle: {},
  439. gridlines: {
  440. color: "transparent"
  441. },
  442. baselineColor: "#ccc",
  443. viewWindow: {}
  444. },
  445. vAxis: {
  446. textStyle: {
  447. color: "#666",
  448. fontSize: 12
  449. },
  450. titleTextStyle: {},
  451. baselineColor: "#ccc",
  452. viewWindow: {}
  453. },
  454. tooltip: {
  455. textStyle: {
  456. color: "#666",
  457. fontSize: 12
  458. }
  459. }
  460. };
  461. var hideLegend = function (options) {
  462. options.legend.position = "none";
  463. };
  464. var setMin = function (options, min) {
  465. options.vAxis.viewWindow.min = min;
  466. };
  467. var setMax = function (options, max) {
  468. options.vAxis.viewWindow.max = max;
  469. };
  470. var setBarMin = function (options, min) {
  471. options.hAxis.viewWindow.min = min;
  472. };
  473. var setBarMax = function (options, max) {
  474. options.hAxis.viewWindow.max = max;
  475. };
  476. var setStacked = function (options) {
  477. options.isStacked = true;
  478. };
  479. var setXtitle = function (options, title) {
  480. options.hAxis.title = title;
  481. options.hAxis.titleTextStyle.italic = false;
  482. }
  483. var setYtitle = function (options, title) {
  484. options.vAxis.title = title;
  485. options.vAxis.titleTextStyle.italic = false;
  486. };
  487. var jsOptions = jsOptionsFunc(defaultOptions, hideLegend, setMin, setMax, setStacked, setXtitle, setYtitle);
  488. // cant use object as key
  489. var createDataTable = function (series, columnType) {
  490. var data = new google.visualization.DataTable();
  491. data.addColumn(columnType, "");
  492. var i, j, s, d, key, rows = [];
  493. for (i = 0; i < series.length; i++) {
  494. s = series[i];
  495. data.addColumn("number", s.name);
  496. for (j = 0; j < s.data.length; j++) {
  497. d = s.data[j];
  498. key = (columnType === "datetime") ? d[0].getTime() : d[0];
  499. if (!rows[key]) {
  500. rows[key] = new Array(series.length);
  501. }
  502. rows[key][i] = toFloat(d[1]);
  503. }
  504. }
  505. var rows2 = [];
  506. var day = true;
  507. var value;
  508. for (i in rows) {
  509. if (rows.hasOwnProperty(i)) {
  510. if (columnType === "datetime") {
  511. value = new Date(toFloat(i));
  512. day = day && isDay(value);
  513. } else if (columnType === "number") {
  514. value = toFloat(i);
  515. } else {
  516. value = i;
  517. }
  518. rows2.push([value].concat(rows[i]));
  519. }
  520. }
  521. if (columnType === "datetime") {
  522. rows2.sort(sortByTime);
  523. }
  524. data.addRows(rows2);
  525. if (columnType === "datetime" && day) {
  526. var formatter = new google.visualization.DateFormat({
  527. pattern: "MMM d, yyyy"
  528. });
  529. formatter.format(data, 0);
  530. }
  531. return data;
  532. };
  533. var resize = function (callback) {
  534. if (window.attachEvent) {
  535. window.attachEvent("onresize", callback);
  536. } else if (window.addEventListener) {
  537. window.addEventListener("resize", callback, true);
  538. }
  539. callback();
  540. };
  541. this.renderLineChart = function (chart) {
  542. waitForLoaded(function () {
  543. var options = jsOptions(chart.data, chart.options);
  544. var data = createDataTable(chart.data, chart.options.discrete ? "string" : "datetime");
  545. chart.chart = new google.visualization.LineChart(chart.element);
  546. resize(function () {
  547. chart.chart.draw(data, options);
  548. });
  549. });
  550. };
  551. this.renderPieChart = function (chart) {
  552. waitForLoaded(function () {
  553. var chartOptions = {
  554. chartArea: {
  555. top: "10%",
  556. height: "80%"
  557. }
  558. };
  559. if (chart.options.colors) {
  560. chartOptions.colors = chart.options.colors;
  561. }
  562. var options = merge(merge(defaultOptions, chartOptions), chart.options.library || {});
  563. var data = new google.visualization.DataTable();
  564. data.addColumn("string", "");
  565. data.addColumn("number", "Value");
  566. data.addRows(chart.data);
  567. chart.chart = new google.visualization.PieChart(chart.element);
  568. resize(function () {
  569. chart.chart.draw(data, options);
  570. });
  571. });
  572. };
  573. this.renderColumnChart = function (chart) {
  574. waitForLoaded(function () {
  575. var options = jsOptions(chart.data, chart.options);
  576. var data = createDataTable(chart.data, "string");
  577. chart.chart = new google.visualization.ColumnChart(chart.element);
  578. resize(function () {
  579. chart.chart.draw(data, options);
  580. });
  581. });
  582. };
  583. this.renderBarChart = function (chart) {
  584. waitForLoaded(function () {
  585. var chartOptions = {
  586. hAxis: {
  587. gridlines: {
  588. color: "#ccc"
  589. }
  590. }
  591. };
  592. var options = jsOptionsFunc(defaultOptions, hideLegend, setBarMin, setBarMax, setStacked)(chart.data, chart.options, chartOptions);
  593. var data = createDataTable(chart.data, "string");
  594. chart.chart = new google.visualization.BarChart(chart.element);
  595. resize(function () {
  596. chart.chart.draw(data, options);
  597. });
  598. });
  599. };
  600. this.renderAreaChart = function (chart) {
  601. waitForLoaded(function () {
  602. var chartOptions = {
  603. isStacked: true,
  604. pointSize: 0,
  605. areaOpacity: 0.5
  606. };
  607. var options = jsOptions(chart.data, chart.options, chartOptions);
  608. var data = createDataTable(chart.data, chart.options.discrete ? "string" : "datetime");
  609. chart.chart = new google.visualization.AreaChart(chart.element);
  610. resize(function () {
  611. chart.chart.draw(data, options);
  612. });
  613. });
  614. };
  615. this.renderGeoChart = function (chart) {
  616. waitForLoaded(function () {
  617. var chartOptions = {
  618. legend: "none",
  619. colorAxis: {
  620. colors: chart.options.colors || ["#f6c7b6", "#ce502d"]
  621. }
  622. };
  623. var options = merge(merge(defaultOptions, chartOptions), chart.options.library || {});
  624. var data = new google.visualization.DataTable();
  625. data.addColumn("string", "");
  626. data.addColumn("number", "Value");
  627. data.addRows(chart.data);
  628. chart.chart = new google.visualization.GeoChart(chart.element);
  629. resize(function () {
  630. chart.chart.draw(data, options);
  631. });
  632. });
  633. };
  634. this.renderScatterChart = function (chart) {
  635. waitForLoaded(function () {
  636. var chartOptions = {};
  637. var options = jsOptions(chart.data, chart.options, chartOptions);
  638. var data = createDataTable(chart.data, "number");
  639. chart.chart = new google.visualization.ScatterChart(chart.element);
  640. resize(function () {
  641. chart.chart.draw(data, options);
  642. });
  643. });
  644. };
  645. this.renderTimeline = function (chart) {
  646. waitForLoaded("timeline", function () {
  647. var chartOptions = {
  648. legend: "none"
  649. };
  650. if (chart.options.colors) {
  651. chartOptions.colors = chart.options.colors;
  652. }
  653. var options = merge(merge(defaultOptions, chartOptions), chart.options.library || {});
  654. var data = new google.visualization.DataTable();
  655. data.addColumn({type: "string", id: "Name"});
  656. data.addColumn({type: "date", id: "Start"});
  657. data.addColumn({type: "date", id: "End"});
  658. data.addRows(chart.data);
  659. chart.chart = new google.visualization.Timeline(chart.element);
  660. resize(function () {
  661. chart.chart.draw(data, options);
  662. });
  663. });
  664. };
  665. };
  666. adapters.push(GoogleChartsAdapter);
  667. }
  668. // TODO remove chartType if cross-browser way
  669. // to get the name of the chart class
  670. function renderChart(chartType, chart) {
  671. var i, adapter, fnName, adapterName;
  672. fnName = "render" + chartType;
  673. adapterName = chart.options.adapter;
  674. for (i = 0; i < adapters.length; i++) {
  675. adapter = adapters[i];
  676. if ((!adapterName || adapterName === adapter.name) && isFunction(adapter[fnName])) {
  677. return adapter[fnName](chart);
  678. }
  679. }
  680. throw new Error("No adapter found");
  681. }
  682. // process data
  683. var toFormattedKey = function (key, keyType) {
  684. if (keyType === "number") {
  685. key = toFloat(key);
  686. } else if (keyType === "datetime") {
  687. key = toDate(key);
  688. } else {
  689. key = toStr(key);
  690. }
  691. return key;
  692. };
  693. var formatSeriesData = function (data, keyType) {
  694. var r = [], key, j;
  695. for (j = 0; j < data.length; j++) {
  696. key = toFormattedKey(data[j][0], keyType);
  697. r.push([key, toFloat(data[j][1])]);
  698. }
  699. if (keyType === "datetime") {
  700. r.sort(sortByTime);
  701. }
  702. return r;
  703. };
  704. function isDay(d) {
  705. return d.getMilliseconds() + d.getSeconds() + d.getMinutes() + d.getHours() === 0;
  706. }
  707. function processSeries(series, opts, keyType) {
  708. var i;
  709. // see if one series or multiple
  710. if (!isArray(series) || typeof series[0] !== "object" || isArray(series[0])) {
  711. series = [{name: "Value", data: series}];
  712. opts.hideLegend = true;
  713. } else {
  714. opts.hideLegend = false;
  715. }
  716. if (opts.discrete) {
  717. keyType = "string";
  718. }
  719. // right format
  720. for (i = 0; i < series.length; i++) {
  721. series[i].data = formatSeriesData(toArr(series[i].data), keyType);
  722. }
  723. return series;
  724. }
  725. function processSimple(data) {
  726. var perfectData = toArr(data), i;
  727. for (i = 0; i < perfectData.length; i++) {
  728. perfectData[i] = [toStr(perfectData[i][0]), toFloat(perfectData[i][1])];
  729. }
  730. return perfectData;
  731. }
  732. function processTime(data)
  733. {
  734. var i;
  735. for (i = 0; i < data.length; i++) {
  736. data[i][1] = toDate(data[i][1]);
  737. data[i][2] = toDate(data[i][2]);
  738. }
  739. return data;
  740. }
  741. function processLineData(chart) {
  742. chart.data = processSeries(chart.data, chart.options, "datetime");
  743. renderChart("LineChart", chart);
  744. }
  745. function processColumnData(chart) {
  746. chart.data = processSeries(chart.data, chart.options, "string");
  747. renderChart("ColumnChart", chart);
  748. }
  749. function processPieData(chart) {
  750. chart.data = processSimple(chart.data);
  751. renderChart("PieChart", chart);
  752. }
  753. function processBarData(chart) {
  754. chart.data = processSeries(chart.data, chart.options, "string");
  755. renderChart("BarChart", chart);
  756. }
  757. function processAreaData(chart) {
  758. chart.data = processSeries(chart.data, chart.options, "datetime");
  759. renderChart("AreaChart", chart);
  760. }
  761. function processGeoData(chart) {
  762. chart.data = processSimple(chart.data);
  763. renderChart("GeoChart", chart);
  764. }
  765. function processScatterData(chart) {
  766. chart.data = processSeries(chart.data, chart.options, "number");
  767. renderChart("ScatterChart", chart);
  768. }
  769. function processTimelineData(chart) {
  770. chart.data = processTime(chart.data);
  771. renderChart("Timeline", chart);
  772. }
  773. function setElement(chart, element, dataSource, opts, callback) {
  774. if (typeof element === "string") {
  775. element = document.getElementById(element);
  776. }
  777. chart.element = element;
  778. chart.options = opts || {};
  779. chart.dataSource = dataSource;
  780. Chartkick.charts[element.id] = chart;
  781. fetchDataSource(chart, callback);
  782. }
  783. // define classes
  784. Chartkick = {
  785. LineChart: function (element, dataSource, opts) {
  786. setElement(this, element, dataSource, opts, processLineData);
  787. },
  788. PieChart: function (element, dataSource, opts) {
  789. setElement(this, element, dataSource, opts, processPieData);
  790. },
  791. ColumnChart: function (element, dataSource, opts) {
  792. setElement(this, element, dataSource, opts, processColumnData);
  793. },
  794. BarChart: function (element, dataSource, opts) {
  795. setElement(this, element, dataSource, opts, processBarData);
  796. },
  797. AreaChart: function (element, dataSource, opts) {
  798. setElement(this, element, dataSource, opts, processAreaData);
  799. },
  800. GeoChart: function (element, dataSource, opts) {
  801. setElement(this, element, dataSource, opts, processGeoData);
  802. },
  803. ScatterChart: function (element, dataSource, opts) {
  804. setElement(this, element, dataSource, opts, processScatterData);
  805. },
  806. Timeline: function (element, dataSource, opts) {
  807. setElement(this, element, dataSource, opts, processTimelineData);
  808. },
  809. charts: {}
  810. };
  811. window.Chartkick = Chartkick;
  812. }(window));