g.bar.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663
  1. /*!
  2. * g.Raphael 0.5 - Charting library, based on Raphaël
  3. *
  4. * Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com)
  5. * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
  6. */
  7. (function () {
  8. var mmin = Math.min,
  9. mmax = Math.max;
  10. function finger(x, y, width, height, dir, ending, isPath, paper) {
  11. var path,
  12. ends = { round: 'round', sharp: 'sharp', soft: 'soft', square: 'square' };
  13. // dir 0 for horizontal and 1 for vertical
  14. if ((dir && !height) || (!dir && !width)) {
  15. return isPath ? "" : paper.path();
  16. }
  17. ending = ends[ending] || "square";
  18. height = Math.round(height);
  19. width = Math.round(width);
  20. x = Math.round(x);
  21. y = Math.round(y);
  22. switch (ending) {
  23. case "round":
  24. if (!dir) {
  25. var r = ~~(height / 2);
  26. if (width < r) {
  27. r = width;
  28. path = [
  29. "M", x + .5, y + .5 - ~~(height / 2),
  30. "l", 0, 0,
  31. "a", r, ~~(height / 2), 0, 0, 1, 0, height,
  32. "l", 0, 0,
  33. "z"
  34. ];
  35. } else {
  36. path = [
  37. "M", x + .5, y + .5 - r,
  38. "l", width - r, 0,
  39. "a", r, r, 0, 1, 1, 0, height,
  40. "l", r - width, 0,
  41. "z"
  42. ];
  43. }
  44. } else {
  45. r = ~~(width / 2);
  46. if (height < r) {
  47. r = height;
  48. path = [
  49. "M", x - ~~(width / 2), y,
  50. "l", 0, 0,
  51. "a", ~~(width / 2), r, 0, 0, 1, width, 0,
  52. "l", 0, 0,
  53. "z"
  54. ];
  55. } else {
  56. path = [
  57. "M", x - r, y,
  58. "l", 0, r - height,
  59. "a", r, r, 0, 1, 1, width, 0,
  60. "l", 0, height - r,
  61. "z"
  62. ];
  63. }
  64. }
  65. break;
  66. case "sharp":
  67. if (!dir) {
  68. var half = ~~(height / 2);
  69. path = [
  70. "M", x, y + half,
  71. "l", 0, -height, mmax(width - half, 0), 0, mmin(half, width), half, -mmin(half, width), half + (half * 2 < height),
  72. "z"
  73. ];
  74. } else {
  75. half = ~~(width / 2);
  76. path = [
  77. "M", x + half, y,
  78. "l", -width, 0, 0, -mmax(height - half, 0), half, -mmin(half, height), half, mmin(half, height), half,
  79. "z"
  80. ];
  81. }
  82. break;
  83. case "square":
  84. if (!dir) {
  85. path = [
  86. "M", x, y + ~~(height / 2),
  87. "l", 0, -height, width, 0, 0, height,
  88. "z"
  89. ];
  90. } else {
  91. path = [
  92. "M", x + ~~(width / 2), y,
  93. "l", 1 - width, 0, 0, -height, width - 1, 0,
  94. "z"
  95. ];
  96. }
  97. break;
  98. case "soft":
  99. if (!dir) {
  100. r = mmin(width, Math.round(height / 5));
  101. path = [
  102. "M", x + .5, y + .5 - ~~(height / 2),
  103. "l", width - r, 0,
  104. "a", r, r, 0, 0, 1, r, r,
  105. "l", 0, height - r * 2,
  106. "a", r, r, 0, 0, 1, -r, r,
  107. "l", r - width, 0,
  108. "z"
  109. ];
  110. } else {
  111. r = mmin(Math.round(width / 5), height);
  112. path = [
  113. "M", x - ~~(width / 2), y,
  114. "l", 0, r - height,
  115. "a", r, r, 0, 0, 1, r, -r,
  116. "l", width - 2 * r, 0,
  117. "a", r, r, 0, 0, 1, r, r,
  118. "l", 0, height - r,
  119. "z"
  120. ];
  121. }
  122. }
  123. if (isPath) {
  124. return path.join(",");
  125. } else {
  126. return paper.path(path);
  127. }
  128. }
  129. /*
  130. * Vertical Barchart
  131. */
  132. function VBarchart(paper, x, y, width, height, values, opts) {
  133. opts = opts || {};
  134. var chartinst = this,
  135. type = opts.type || "square",
  136. gutter = parseFloat(opts.gutter || "20%"),
  137. chart = paper.set(),
  138. bars = paper.set(),
  139. covers = paper.set(),
  140. covers2 = paper.set(),
  141. total = Math.max.apply(Math, values),
  142. stacktotal = [],
  143. multi = 0,
  144. colors = opts.colors || chartinst.colors,
  145. len = values.length;
  146. if (Raphael.is(values[0], "array")) {
  147. total = [];
  148. multi = len;
  149. len = 0;
  150. for (var i = values.length; i--;) {
  151. bars.push(paper.set());
  152. total.push(Math.max.apply(Math, values[i]));
  153. len = Math.max(len, values[i].length);
  154. }
  155. if (opts.stacked) {
  156. for (var i = len; i--;) {
  157. var tot = 0;
  158. for (var j = values.length; j--;) {
  159. tot +=+ values[j][i] || 0;
  160. }
  161. stacktotal.push(tot);
  162. }
  163. }
  164. for (var i = values.length; i--;) {
  165. if (values[i].length < len) {
  166. for (var j = len; j--;) {
  167. values[i].push(0);
  168. }
  169. }
  170. }
  171. total = Math.max.apply(Math, opts.stacked ? stacktotal : total);
  172. }
  173. total = (opts.to) || total;
  174. var barwidth = width / (len * (100 + gutter) + gutter) * 100,
  175. barhgutter = barwidth * gutter / 100,
  176. barvgutter = opts.vgutter == null ? 20 : opts.vgutter,
  177. stack = [],
  178. X = x + barhgutter,
  179. Y = (height - 2 * barvgutter) / total;
  180. if (!opts.stretch) {
  181. barhgutter = Math.round(barhgutter);
  182. barwidth = Math.floor(barwidth);
  183. }
  184. !opts.stacked && (barwidth /= multi || 1);
  185. for (var i = 0; i < len; i++) {
  186. stack = [];
  187. for (var j = 0; j < (multi || 1); j++) {
  188. var h = Math.round((multi ? values[j][i] : values[i]) * Y),
  189. top = y + height - barvgutter - h,
  190. bar = finger(Math.round(X + barwidth / 2), top + h, barwidth, h, true, type, null, paper).attr({ stroke: "none", fill: colors[multi ? j : i] });
  191. if (multi) {
  192. bars[j].push(bar);
  193. } else {
  194. bars.push(bar);
  195. }
  196. bar.y = top;
  197. bar.x = Math.round(X + barwidth / 2);
  198. bar.w = barwidth;
  199. bar.h = h;
  200. bar.value = multi ? values[j][i] : values[i];
  201. if (!opts.stacked) {
  202. X += barwidth;
  203. } else {
  204. stack.push(bar);
  205. }
  206. }
  207. if (opts.stacked) {
  208. var cvr;
  209. covers2.push(cvr = paper.rect(stack[0].x - stack[0].w / 2, y, barwidth, height).attr(chartinst.shim));
  210. cvr.bars = paper.set();
  211. var size = 0;
  212. for (var s = stack.length; s--;) {
  213. stack[s].toFront();
  214. }
  215. for (var s = 0, ss = stack.length; s < ss; s++) {
  216. var bar = stack[s],
  217. cover,
  218. h = (size + bar.value) * Y,
  219. path = finger(bar.x, y + height - barvgutter - !!size * .5, barwidth, h, true, type, 1, paper);
  220. cvr.bars.push(bar);
  221. size && bar.attr({path: path});
  222. bar.h = h;
  223. bar.y = y + height - barvgutter - !!size * .5 - h;
  224. covers.push(cover = paper.rect(bar.x - bar.w / 2, bar.y, barwidth, bar.value * Y).attr(chartinst.shim));
  225. cover.bar = bar;
  226. cover.value = bar.value;
  227. size += bar.value;
  228. }
  229. X += barwidth;
  230. }
  231. X += barhgutter;
  232. }
  233. covers2.toFront();
  234. X = x + barhgutter;
  235. if (!opts.stacked) {
  236. for (var i = 0; i < len; i++) {
  237. for (var j = 0; j < (multi || 1); j++) {
  238. var cover;
  239. covers.push(cover = paper.rect(Math.round(X), y + barvgutter, barwidth, height - barvgutter).attr(chartinst.shim));
  240. cover.bar = multi ? bars[j][i] : bars[i];
  241. cover.value = cover.bar.value;
  242. X += barwidth;
  243. }
  244. X += barhgutter;
  245. }
  246. }
  247. var xdim = chartinst.snapEnds(0, len, 1),
  248. minx = xdim.from,
  249. maxx = xdim.to,
  250. ydim = chartinst.snapEnds(0, total, 1),
  251. miny = ydim.from,
  252. maxy = ydim.to;
  253. var axis = paper.set();
  254. if (opts.axis) {
  255. var ax = (opts.axis + "").split(/[,\s]+/);
  256. // +ax[0] && axis.push(chartinst.axis(x, y + gutter, width, minx, maxx, opts.axisxstep || Math.floor((width) / 20), 2, paper));
  257. +ax[0] && axis.push(paper.path(["M", x, y + gutter + 0.5,"h", width]));
  258. +ax[1] && axis.push(chartinst.axis(x + width, y + height - gutter, height - 2 * gutter, miny, maxy, opts.axisystep || Math.floor((height - gutter) / 20), 3, paper));
  259. // +ax[2] && axis.push(chartinst.axis(x, y + height - gutter, width, minx, maxx, opts.axisxstep || Math.floor((width) / 20), 0, paper));
  260. +ax[2] && axis.push(paper.path(["M", x, y + height - gutter + 0.5,"h", width]));
  261. +ax[3] && axis.push(chartinst.axis(x, y + height - gutter, height - 2 * gutter, miny, maxy, opts.axisystep || Math.floor((height - gutter) / 20), 1, paper));
  262. }
  263. chart.label = function (labels, isBottom) {
  264. labels = labels || [];
  265. this.labels = paper.set();
  266. var L, l = -Infinity;
  267. if (opts.stacked) {
  268. for (var i = 0; i < len; i++) {
  269. var tot = 0;
  270. for (var j = 0; j < (multi || 1); j++) {
  271. tot += multi ? values[j][i] : values[i];
  272. var bar = multi ? bars[j][i] : bars[i];
  273. if (j == multi - 1) {
  274. var label = chartinst.labelise(labels[i], tot, total);
  275. L = paper.text(bar.x, y + height - barvgutter / 2, label).insertBefore(covers[i * (multi || 1) + j]);
  276. var bb = L.getBBox();
  277. if (bb.x - 7 < l) {
  278. L.remove();
  279. } else {
  280. this.labels.push(L);
  281. l = bb.x + bb.width;
  282. }
  283. }
  284. }
  285. }
  286. } else {
  287. for (var i = 0; i < len; i++) {
  288. for (var j = 0; j < (multi || 1); j++) {
  289. var label = chartinst.labelise(multi ? labels[j] && labels[j][i] : labels[i], multi ? values[j][i] : values[i], total);
  290. var bar = multi ? bars[j][i] : bars[i];
  291. L = paper.text(bar.x, isBottom ? y + height - barvgutter / 2 : bar.y - 10, label).insertBefore(covers[i * (multi || 1) + j]);
  292. var bb = L.getBBox();
  293. if (bb.x - 7 < l) {
  294. L.remove();
  295. } else {
  296. this.labels.push(L);
  297. l = bb.x + bb.width;
  298. }
  299. }
  300. }
  301. }
  302. return this;
  303. };
  304. chart.hover = function (fin, fout) {
  305. covers2.hide();
  306. covers.show();
  307. covers.mouseover(fin).mouseout(fout);
  308. return this;
  309. };
  310. chart.hoverColumn = function (fin, fout) {
  311. covers.hide();
  312. covers2.show();
  313. fout = fout || function () {};
  314. covers2.mouseover(fin).mouseout(fout);
  315. return this;
  316. };
  317. chart.click = function (f) {
  318. covers2.hide();
  319. covers.show();
  320. covers.click(f);
  321. return this;
  322. };
  323. chart.each = function (f) {
  324. if (!Raphael.is(f, "function")) {
  325. return this;
  326. }
  327. for (var i = covers.length; i--;) {
  328. f.call(covers[i]);
  329. }
  330. return this;
  331. };
  332. chart.eachColumn = function (f) {
  333. if (!Raphael.is(f, "function")) {
  334. return this;
  335. }
  336. for (var i = covers2.length; i--;) {
  337. f.call(covers2[i]);
  338. }
  339. return this;
  340. };
  341. chart.clickColumn = function (f) {
  342. covers.hide();
  343. covers2.show();
  344. covers2.click(f);
  345. return this;
  346. };
  347. chart.push(bars, covers, covers2);
  348. chart.bars = bars;
  349. chart.covers = covers;
  350. chart.axis = axis;
  351. return chart;
  352. };
  353. /**
  354. * Horizontal Barchart
  355. */
  356. function HBarchart(paper, x, y, width, height, values, opts) {
  357. opts = opts || {};
  358. var chartinst = this,
  359. type = opts.type || "square",
  360. gutter = parseFloat(opts.gutter || "20%"),
  361. chart = paper.set(),
  362. bars = paper.set(),
  363. covers = paper.set(),
  364. covers2 = paper.set(),
  365. total = Math.max.apply(Math, values),
  366. stacktotal = [],
  367. multi = 0,
  368. colors = opts.colors || chartinst.colors,
  369. len = values.length;
  370. if (Raphael.is(values[0], "array")) {
  371. total = [];
  372. multi = len;
  373. len = 0;
  374. for (var i = values.length; i--;) {
  375. bars.push(paper.set());
  376. total.push(Math.max.apply(Math, values[i]));
  377. len = Math.max(len, values[i].length);
  378. }
  379. if (opts.stacked) {
  380. for (var i = len; i--;) {
  381. var tot = 0;
  382. for (var j = values.length; j--;) {
  383. tot +=+ values[j][i] || 0;
  384. }
  385. stacktotal.push(tot);
  386. }
  387. }
  388. for (var i = values.length; i--;) {
  389. if (values[i].length < len) {
  390. for (var j = len; j--;) {
  391. values[i].push(0);
  392. }
  393. }
  394. }
  395. total = Math.max.apply(Math, opts.stacked ? stacktotal : total);
  396. }
  397. total = (opts.to) || total;
  398. var barheight = Math.floor(height / (len * (100 + gutter) + gutter) * 100),
  399. bargutter = Math.floor(barheight * gutter / 100),
  400. stack = [],
  401. Y = y + bargutter,
  402. X = (width - 1) / total;
  403. !opts.stacked && (barheight /= multi || 1);
  404. for (var i = 0; i < len; i++) {
  405. stack = [];
  406. for (var j = 0; j < (multi || 1); j++) {
  407. var val = multi ? values[j][i] : values[i],
  408. bar = finger(x, Y + barheight / 2, Math.round(val * X), barheight - 1, false, type, null, paper).attr({stroke: "none", fill: colors[multi ? j : i]});
  409. if (multi) {
  410. bars[j].push(bar);
  411. } else {
  412. bars.push(bar);
  413. }
  414. bar.x = x + Math.round(val * X);
  415. bar.y = Y + barheight / 2;
  416. bar.w = Math.round(val * X);
  417. bar.h = barheight;
  418. bar.value = +val;
  419. if (!opts.stacked) {
  420. Y += barheight;
  421. } else {
  422. stack.push(bar);
  423. }
  424. }
  425. if (opts.stacked) {
  426. var cvr = paper.rect(x, stack[0].y - stack[0].h / 2, width, barheight).attr(chartinst.shim);
  427. covers2.push(cvr);
  428. cvr.bars = paper.set();
  429. var size = 0;
  430. for (var s = stack.length; s--;) {
  431. stack[s].toFront();
  432. }
  433. for (var s = 0, ss = stack.length; s < ss; s++) {
  434. var bar = stack[s],
  435. cover,
  436. val = Math.round((size + bar.value) * X),
  437. path = finger(x, bar.y, val, barheight - 1, false, type, 1, paper);
  438. cvr.bars.push(bar);
  439. size && bar.attr({ path: path });
  440. bar.w = val;
  441. bar.x = x + val;
  442. covers.push(cover = paper.rect(x + size * X, bar.y - bar.h / 2, bar.value * X, barheight).attr(chartinst.shim));
  443. cover.bar = bar;
  444. size += bar.value;
  445. }
  446. Y += barheight;
  447. }
  448. Y += bargutter;
  449. }
  450. covers2.toFront();
  451. Y = y + bargutter;
  452. if (!opts.stacked) {
  453. for (var i = 0; i < len; i++) {
  454. for (var j = 0; j < (multi || 1); j++) {
  455. var cover = paper.rect(x, Y, width, barheight).attr(chartinst.shim);
  456. covers.push(cover);
  457. cover.bar = multi ? bars[j][i] : bars[i];
  458. cover.value = cover.bar.value;
  459. Y += barheight;
  460. }
  461. Y += bargutter;
  462. }
  463. }
  464. var xdim = chartinst.snapEnds(0, total, 1),
  465. minx = xdim.from,
  466. maxx = xdim.to,
  467. ydim = chartinst.snapEnds(0, len, 1),
  468. miny = ydim.from,
  469. maxy = ydim.to;
  470. var axis = paper.set();
  471. if (opts.axis) {
  472. var ax = (opts.axis + "").split(/[,\s]+/);
  473. +ax[0] && axis.push(chartinst.axis(x, y, width, minx, maxx, opts.axisxstep || Math.floor((width) / 20), 2, paper));
  474. //+ax[1] && axis.push(chartinst.axis(x + width, y + height, height, miny, maxy, opts.axisystep || Math.floor((height - gutter) / 20), 3, paper));
  475. +ax[1] && axis.push(paper.path(["M", x+width+ 0.5, y,"v", height]));
  476. +ax[2] && axis.push(chartinst.axis(x, y + height, width, minx, maxx, opts.axisxstep || Math.floor((width) / 20), 0, paper));
  477. //+ax[3] && axis.push(chartinst.axis(x, y + height, height, miny, maxy, opts.axisystep || Math.floor((height - gutter) / 20), 1, paper));
  478. +ax[3] && axis.push(paper.path(["M", x+ 0.5, y,"v", height]));
  479. }
  480. chart.label = function (labels, isRight) {
  481. labels = labels || [];
  482. this.labels = paper.set();
  483. for (var i = 0; i < len; i++) {
  484. for (var j = 0; j < multi; j++) {
  485. var bar = multi ? bars[j][i] : bars[i];
  486. var label = chartinst.labelise(multi ? labels[j] && labels[j][i] : labels[i], multi ? values[j][i] : values[i], total),
  487. X = isRight ? bar.x - barheight / 2 + 3 : x + 5,
  488. A = isRight ? "end" : "start",
  489. L;
  490. this.labels.push(L = paper.text(X, bar.y, label).attr({ "text-anchor": A }).insertBefore(covers[i * (multi || 1) + j]));
  491. if (L.getBBox().x < x + 5) {
  492. L.attr({x: x + 5, "text-anchor": "start"});
  493. } else {
  494. bar.label = L;
  495. }
  496. }
  497. }
  498. return this;
  499. };
  500. chart.hover = function (fin, fout) {
  501. covers2.hide();
  502. covers.show();
  503. fout = fout || function () {};
  504. covers.mouseover(fin).mouseout(fout);
  505. return this;
  506. };
  507. chart.hoverColumn = function (fin, fout) {
  508. covers.hide();
  509. covers2.show();
  510. fout = fout || function () {};
  511. covers2.mouseover(fin).mouseout(fout);
  512. return this;
  513. };
  514. chart.each = function (f) {
  515. if (!Raphael.is(f, "function")) {
  516. return this;
  517. }
  518. for (var i = covers.length; i--;) {
  519. f.call(covers[i]);
  520. }
  521. return this;
  522. };
  523. chart.eachColumn = function (f) {
  524. if (!Raphael.is(f, "function")) {
  525. return this;
  526. }
  527. for (var i = covers2.length; i--;) {
  528. f.call(covers2[i]);
  529. }
  530. return this;
  531. };
  532. chart.click = function (f) {
  533. covers2.hide();
  534. covers.show();
  535. covers.click(f);
  536. return this;
  537. };
  538. chart.clickColumn = function (f) {
  539. covers.hide();
  540. covers2.show();
  541. covers2.click(f);
  542. return this;
  543. };
  544. chart.push(bars, covers, covers2);
  545. chart.bars = bars;
  546. chart.covers = covers;
  547. return chart;
  548. };
  549. //inheritance
  550. var F = function() {};
  551. F.prototype = Raphael.g;
  552. HBarchart.prototype = VBarchart.prototype = new F;
  553. Raphael.fn.hbarchart = function(x, y, width, height, values, opts) {
  554. return new HBarchart(this, x, y, width, height, values, opts);
  555. };
  556. Raphael.fn.barchart = function(x, y, width, height, values, opts) {
  557. return new VBarchart(this, x, y, width, height, values, opts);
  558. };
  559. })();