g.raphael.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887
  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. /*
  8. * Tooltips on Element prototype
  9. */
  10. /*\
  11. * Element.popup
  12. [ method ]
  13. **
  14. * Puts the context Element in a 'popup' tooltip. Can also be used on sets.
  15. **
  16. > Parameters
  17. **
  18. - dir (string) location of Element relative to the tail: `'down'`, `'left'`, `'up'` [default], or `'right'`.
  19. - size (number) amount of bevel/padding around the Element, as well as half the width and height of the tail [default: `5`]
  20. - x (number) x coordinate of the popup's tail [default: Element's `x` or `cx`]
  21. - y (number) y coordinate of the popup's tail [default: Element's `y` or `cy`]
  22. **
  23. = (object) path element of the popup
  24. > Usage
  25. | paper.circle(50, 50, 5).attr({
  26. | stroke: "#fff",
  27. | fill: "0-#c9de96-#8ab66b:44-#398235"
  28. | }).popup();
  29. \*/
  30. Raphael.el.popup = function (dir, size, x, y) {
  31. var paper = this.paper || this[0].paper,
  32. bb, xy, center, cw, ch;
  33. if (!paper) return;
  34. switch (this.type) {
  35. case 'text':
  36. case 'circle':
  37. case 'ellipse': center = true; break;
  38. default: center = false;
  39. }
  40. dir = dir == null ? 'up' : dir;
  41. size = size || 5;
  42. bb = this.getBBox();
  43. x = typeof x == 'number' ? x : (center ? bb.x + bb.width / 2 : bb.x);
  44. y = typeof y == 'number' ? y : (center ? bb.y + bb.height / 2 : bb.y);
  45. cw = Math.max(bb.width / 2 - size, 0);
  46. ch = Math.max(bb.height / 2 - size, 0);
  47. this.translate(x - bb.x - (center ? bb.width / 2 : 0), y - bb.y - (center ? bb.height / 2 : 0));
  48. bb = this.getBBox();
  49. var paths = {
  50. up: [
  51. 'M', x, y,
  52. 'l', -size, -size, -cw, 0,
  53. 'a', size, size, 0, 0, 1, -size, -size,
  54. 'l', 0, -bb.height,
  55. 'a', size, size, 0, 0, 1, size, -size,
  56. 'l', size * 2 + cw * 2, 0,
  57. 'a', size, size, 0, 0, 1, size, size,
  58. 'l', 0, bb.height,
  59. 'a', size, size, 0, 0, 1, -size, size,
  60. 'l', -cw, 0,
  61. 'z'
  62. ].join(','),
  63. down: [
  64. 'M', x, y,
  65. 'l', size, size, cw, 0,
  66. 'a', size, size, 0, 0, 1, size, size,
  67. 'l', 0, bb.height,
  68. 'a', size, size, 0, 0, 1, -size, size,
  69. 'l', -(size * 2 + cw * 2), 0,
  70. 'a', size, size, 0, 0, 1, -size, -size,
  71. 'l', 0, -bb.height,
  72. 'a', size, size, 0, 0, 1, size, -size,
  73. 'l', cw, 0,
  74. 'z'
  75. ].join(','),
  76. left: [
  77. 'M', x, y,
  78. 'l', -size, size, 0, ch,
  79. 'a', size, size, 0, 0, 1, -size, size,
  80. 'l', -bb.width, 0,
  81. 'a', size, size, 0, 0, 1, -size, -size,
  82. 'l', 0, -(size * 2 + ch * 2),
  83. 'a', size, size, 0, 0, 1, size, -size,
  84. 'l', bb.width, 0,
  85. 'a', size, size, 0, 0, 1, size, size,
  86. 'l', 0, ch,
  87. 'z'
  88. ].join(','),
  89. right: [
  90. 'M', x, y,
  91. 'l', size, -size, 0, -ch,
  92. 'a', size, size, 0, 0, 1, size, -size,
  93. 'l', bb.width, 0,
  94. 'a', size, size, 0, 0, 1, size, size,
  95. 'l', 0, size * 2 + ch * 2,
  96. 'a', size, size, 0, 0, 1, -size, size,
  97. 'l', -bb.width, 0,
  98. 'a', size, size, 0, 0, 1, -size, -size,
  99. 'l', 0, -ch,
  100. 'z'
  101. ].join(',')
  102. };
  103. xy = {
  104. up: { x: -!center * (bb.width / 2), y: -size * 2 - (center ? bb.height / 2 : bb.height) },
  105. down: { x: -!center * (bb.width / 2), y: size * 2 + (center ? bb.height / 2 : bb.height) },
  106. left: { x: -size * 2 - (center ? bb.width / 2 : bb.width), y: -!center * (bb.height / 2) },
  107. right: { x: size * 2 + (center ? bb.width / 2 : bb.width), y: -!center * (bb.height / 2) }
  108. }[dir];
  109. this.translate(xy.x, xy.y);
  110. return paper.path(paths[dir]).attr({ fill: "#000", stroke: "none" }).insertBefore(this.node ? this : this[0]);
  111. };
  112. /*\
  113. * Element.tag
  114. [ method ]
  115. **
  116. * Puts the context Element in a 'tag' tooltip. Can also be used on sets.
  117. **
  118. > Parameters
  119. **
  120. - angle (number) angle of orientation in degrees [default: `0`]
  121. - r (number) radius of the loop [default: `5`]
  122. - x (number) x coordinate of the center of the tag loop [default: Element's `x` or `cx`]
  123. - y (number) y coordinate of the center of the tag loop [default: Element's `x` or `cx`]
  124. **
  125. = (object) path element of the tag
  126. > Usage
  127. | paper.circle(50, 50, 15).attr({
  128. | stroke: "#fff",
  129. | fill: "0-#c9de96-#8ab66b:44-#398235"
  130. | }).tag(60);
  131. \*/
  132. Raphael.el.tag = function (angle, r, x, y) {
  133. var d = 3,
  134. paper = this.paper || this[0].paper;
  135. if (!paper) return;
  136. var p = paper.path().attr({ fill: '#000', stroke: '#000' }),
  137. bb = this.getBBox(),
  138. dx, R, center, tmp;
  139. switch (this.type) {
  140. case 'text':
  141. case 'circle':
  142. case 'ellipse': center = true; break;
  143. default: center = false;
  144. }
  145. angle = angle || 0;
  146. x = typeof x == 'number' ? x : (center ? bb.x + bb.width / 2 : bb.x);
  147. y = typeof y == 'number' ? y : (center ? bb.y + bb.height / 2 : bb.y);
  148. r = r == null ? 5 : r;
  149. R = .5522 * r;
  150. if (bb.height >= r * 2) {
  151. p.attr({
  152. path: [
  153. "M", x, y + r,
  154. "a", r, r, 0, 1, 1, 0, -r * 2, r, r, 0, 1, 1, 0, r * 2,
  155. "m", 0, -r * 2 -d,
  156. "a", r + d, r + d, 0, 1, 0, 0, (r + d) * 2,
  157. "L", x + r + d, y + bb.height / 2 + d,
  158. "l", bb.width + 2 * d, 0, 0, -bb.height - 2 * d, -bb.width - 2 * d, 0,
  159. "L", x, y - r - d
  160. ].join(",")
  161. });
  162. } else {
  163. dx = Math.sqrt(Math.pow(r + d, 2) - Math.pow(bb.height / 2 + d, 2));
  164. p.attr({
  165. path: [
  166. "M", x, y + r,
  167. "c", -R, 0, -r, R - r, -r, -r, 0, -R, r - R, -r, r, -r, R, 0, r, r - R, r, r, 0, R, R - r, r, -r, r,
  168. "M", x + dx, y - bb.height / 2 - d,
  169. "a", r + d, r + d, 0, 1, 0, 0, bb.height + 2 * d,
  170. "l", r + d - dx + bb.width + 2 * d, 0, 0, -bb.height - 2 * d,
  171. "L", x + dx, y - bb.height / 2 - d
  172. ].join(",")
  173. });
  174. }
  175. angle = 360 - angle;
  176. p.rotate(angle, x, y);
  177. if (this.attrs) {
  178. //elements
  179. this.attr(this.attrs.x ? 'x' : 'cx', x + r + d + (!center ? this.type == 'text' ? bb.width : 0 : bb.width / 2)).attr('y', center ? y : y - bb.height / 2);
  180. this.rotate(angle, x, y);
  181. angle > 90 && angle < 270 && this.attr(this.attrs.x ? 'x' : 'cx', x - r - d - (!center ? bb.width : bb.width / 2)).rotate(180, x, y);
  182. } else {
  183. //sets
  184. if (angle > 90 && angle < 270) {
  185. this.translate(x - bb.x - bb.width - r - d, y - bb.y - bb.height / 2);
  186. this.rotate(angle - 180, bb.x + bb.width + r + d, bb.y + bb.height / 2);
  187. } else {
  188. this.translate(x - bb.x + r + d, y - bb.y - bb.height / 2);
  189. this.rotate(angle, bb.x - r - d, bb.y + bb.height / 2);
  190. }
  191. }
  192. return p.insertBefore(this.node ? this : this[0]);
  193. };
  194. /*\
  195. * Element.drop
  196. [ method ]
  197. **
  198. * Puts the context Element in a 'drop' tooltip. Can also be used on sets.
  199. **
  200. > Parameters
  201. **
  202. - angle (number) angle of orientation in degrees [default: `0`]
  203. - x (number) x coordinate of the drop's point [default: Element's `x` or `cx`]
  204. - y (number) y coordinate of the drop's point [default: Element's `x` or `cx`]
  205. **
  206. = (object) path element of the drop
  207. > Usage
  208. | paper.circle(50, 50, 8).attr({
  209. | stroke: "#fff",
  210. | fill: "0-#c9de96-#8ab66b:44-#398235"
  211. | }).drop(60);
  212. \*/
  213. Raphael.el.drop = function (angle, x, y) {
  214. var bb = this.getBBox(),
  215. paper = this.paper || this[0].paper,
  216. center, size, p, dx, dy;
  217. if (!paper) return;
  218. switch (this.type) {
  219. case 'text':
  220. case 'circle':
  221. case 'ellipse': center = true; break;
  222. default: center = false;
  223. }
  224. angle = angle || 0;
  225. x = typeof x == 'number' ? x : (center ? bb.x + bb.width / 2 : bb.x);
  226. y = typeof y == 'number' ? y : (center ? bb.y + bb.height / 2 : bb.y);
  227. size = Math.max(bb.width, bb.height) + Math.min(bb.width, bb.height);
  228. p = paper.path([
  229. "M", x, y,
  230. "l", size, 0,
  231. "A", size * .4, size * .4, 0, 1, 0, x + size * .7, y - size * .7,
  232. "z"
  233. ]).attr({fill: "#000", stroke: "none"}).rotate(22.5 - angle, x, y);
  234. angle = (angle + 90) * Math.PI / 180;
  235. dx = (x + size * Math.sin(angle)) - (center ? 0 : bb.width / 2);
  236. dy = (y + size * Math.cos(angle)) - (center ? 0 : bb.height / 2);
  237. this.attrs ?
  238. this.attr(this.attrs.x ? 'x' : 'cx', dx).attr(this.attrs.y ? 'y' : 'cy', dy) :
  239. this.translate(dx - bb.x, dy - bb.y);
  240. return p.insertBefore(this.node ? this : this[0]);
  241. };
  242. /*\
  243. * Element.flag
  244. [ method ]
  245. **
  246. * Puts the context Element in a 'flag' tooltip. Can also be used on sets.
  247. **
  248. > Parameters
  249. **
  250. - angle (number) angle of orientation in degrees [default: `0`]
  251. - x (number) x coordinate of the flag's point [default: Element's `x` or `cx`]
  252. - y (number) y coordinate of the flag's point [default: Element's `x` or `cx`]
  253. **
  254. = (object) path element of the flag
  255. > Usage
  256. | paper.circle(50, 50, 10).attr({
  257. | stroke: "#fff",
  258. | fill: "0-#c9de96-#8ab66b:44-#398235"
  259. | }).flag(60);
  260. \*/
  261. Raphael.el.flag = function (angle, x, y) {
  262. var d = 3,
  263. paper = this.paper || this[0].paper;
  264. if (!paper) return;
  265. var p = paper.path().attr({ fill: '#000', stroke: '#000' }),
  266. bb = this.getBBox(),
  267. h = bb.height / 2,
  268. center;
  269. switch (this.type) {
  270. case 'text':
  271. case 'circle':
  272. case 'ellipse': center = true; break;
  273. default: center = false;
  274. }
  275. angle = angle || 0;
  276. x = typeof x == 'number' ? x : (center ? bb.x + bb.width / 2 : bb.x);
  277. y = typeof y == 'number' ? y : (center ? bb.y + bb.height / 2: bb.y);
  278. p.attr({
  279. path: [
  280. "M", x, y,
  281. "l", h + d, -h - d, bb.width + 2 * d, 0, 0, bb.height + 2 * d, -bb.width - 2 * d, 0,
  282. "z"
  283. ].join(",")
  284. });
  285. angle = 360 - angle;
  286. p.rotate(angle, x, y);
  287. if (this.attrs) {
  288. //elements
  289. this.attr(this.attrs.x ? 'x' : 'cx', x + h + d + (!center ? this.type == 'text' ? bb.width : 0 : bb.width / 2)).attr('y', center ? y : y - bb.height / 2);
  290. this.rotate(angle, x, y);
  291. angle > 90 && angle < 270 && this.attr(this.attrs.x ? 'x' : 'cx', x - h - d - (!center ? bb.width : bb.width / 2)).rotate(180, x, y);
  292. } else {
  293. //sets
  294. if (angle > 90 && angle < 270) {
  295. this.translate(x - bb.x - bb.width - h - d, y - bb.y - bb.height / 2);
  296. this.rotate(angle - 180, bb.x + bb.width + h + d, bb.y + bb.height / 2);
  297. } else {
  298. this.translate(x - bb.x + h + d, y - bb.y - bb.height / 2);
  299. this.rotate(angle, bb.x - h - d, bb.y + bb.height / 2);
  300. }
  301. }
  302. return p.insertBefore(this.node ? this : this[0]);
  303. };
  304. /*\
  305. * Element.label
  306. [ method ]
  307. **
  308. * Puts the context Element in a 'label' tooltip. Can also be used on sets.
  309. **
  310. = (object) path element of the label.
  311. > Usage
  312. | paper.circle(50, 50, 10).attr({
  313. | stroke: "#fff",
  314. | fill: "0-#c9de96-#8ab66b:44-#398235"
  315. | }).label();
  316. \*/
  317. Raphael.el.label = function () {
  318. var bb = this.getBBox(),
  319. paper = this.paper || this[0].paper,
  320. r = Math.min(20, bb.width + 10, bb.height + 10) / 2;
  321. if (!paper) return;
  322. return paper.rect(bb.x - r / 2, bb.y - r / 2, bb.width + r, bb.height + r, r).attr({ stroke: 'none', fill: '#000' }).insertBefore(this.node ? this : this[0]);
  323. };
  324. /*\
  325. * Element.blob
  326. [ method ]
  327. **
  328. * Puts the context Element in a 'blob' tooltip. Can also be used on sets.
  329. **
  330. > Parameters
  331. **
  332. - angle (number) angle of orientation in degrees [default: `0`]
  333. - x (number) x coordinate of the blob's tail [default: Element's `x` or `cx`]
  334. - y (number) y coordinate of the blob's tail [default: Element's `x` or `cx`]
  335. **
  336. = (object) path element of the blob
  337. > Usage
  338. | paper.circle(50, 50, 8).attr({
  339. | stroke: "#fff",
  340. | fill: "0-#c9de96-#8ab66b:44-#398235"
  341. | }).blob(60);
  342. \*/
  343. Raphael.el.blob = function (angle, x, y) {
  344. var bb = this.getBBox(),
  345. rad = Math.PI / 180,
  346. paper = this.paper || this[0].paper,
  347. p, center, size;
  348. if (!paper) return;
  349. switch (this.type) {
  350. case 'text':
  351. case 'circle':
  352. case 'ellipse': center = true; break;
  353. default: center = false;
  354. }
  355. p = paper.path().attr({ fill: "#000", stroke: "none" });
  356. angle = (+angle + 1 ? angle : 45) + 90;
  357. size = Math.min(bb.height, bb.width);
  358. x = typeof x == 'number' ? x : (center ? bb.x + bb.width / 2 : bb.x);
  359. y = typeof y == 'number' ? y : (center ? bb.y + bb.height / 2 : bb.y);
  360. var w = Math.max(bb.width + size, size * 25 / 12),
  361. h = Math.max(bb.height + size, size * 25 / 12),
  362. x2 = x + size * Math.sin((angle - 22.5) * rad),
  363. y2 = y + size * Math.cos((angle - 22.5) * rad),
  364. x1 = x + size * Math.sin((angle + 22.5) * rad),
  365. y1 = y + size * Math.cos((angle + 22.5) * rad),
  366. dx = (x1 - x2) / 2,
  367. dy = (y1 - y2) / 2,
  368. rx = w / 2,
  369. ry = h / 2,
  370. k = -Math.sqrt(Math.abs(rx * rx * ry * ry - rx * rx * dy * dy - ry * ry * dx * dx) / (rx * rx * dy * dy + ry * ry * dx * dx)),
  371. cx = k * rx * dy / ry + (x1 + x2) / 2,
  372. cy = k * -ry * dx / rx + (y1 + y2) / 2;
  373. p.attr({
  374. x: cx,
  375. y: cy,
  376. path: [
  377. "M", x, y,
  378. "L", x1, y1,
  379. "A", rx, ry, 0, 1, 1, x2, y2,
  380. "z"
  381. ].join(",")
  382. });
  383. this.translate(cx - bb.x - bb.width / 2, cy - bb.y - bb.height / 2);
  384. return p.insertBefore(this.node ? this : this[0]);
  385. };
  386. /*
  387. * Tooltips on Paper prototype
  388. */
  389. /*\
  390. * Paper.label
  391. [ method ]
  392. **
  393. * Puts the given `text` into a 'label' tooltip. The text is given a default style according to @g.txtattr. See @Element.label
  394. **
  395. > Parameters
  396. **
  397. - x (number) x coordinate of the center of the label
  398. - y (number) y coordinate of the center of the label
  399. - text (string) text to place inside the label
  400. **
  401. = (object) set containing the label path and the text element
  402. > Usage
  403. | paper.label(50, 50, "$9.99");
  404. \*/
  405. Raphael.fn.label = function (x, y, text) {
  406. var set = this.set();
  407. text = this.text(x, y, text).attr(Raphael.g.txtattr);
  408. return set.push(text.label(), text);
  409. };
  410. /*\
  411. * Paper.popup
  412. [ method ]
  413. **
  414. * Puts the given `text` into a 'popup' tooltip. The text is given a default style according to @g.txtattr. See @Element.popup
  415. *
  416. * Note: The `dir` parameter has changed from g.Raphael 0.4.1 to 0.5. The options `0`, `1`, `2`, and `3` has been changed to `'down'`, `'left'`, `'up'`, and `'right'` respectively.
  417. **
  418. > Parameters
  419. **
  420. - x (number) x coordinate of the popup's tail
  421. - y (number) y coordinate of the popup's tail
  422. - text (string) text to place inside the popup
  423. - dir (string) location of the text relative to the tail: `'down'`, `'left'`, `'up'` [default], or `'right'`.
  424. - size (number) amount of padding around the Element [default: `5`]
  425. **
  426. = (object) set containing the popup path and the text element
  427. > Usage
  428. | paper.popup(50, 50, "$9.99", 'down');
  429. \*/
  430. Raphael.fn.popup = function (x, y, text, dir, size) {
  431. var set = this.set();
  432. text = this.text(x, y, text).attr(Raphael.g.txtattr);
  433. return set.push(text.popup(dir, size), text);
  434. };
  435. /*\
  436. * Paper.tag
  437. [ method ]
  438. **
  439. * Puts the given text into a 'tag' tooltip. The text is given a default style according to @g.txtattr. See @Element.tag
  440. **
  441. > Parameters
  442. **
  443. - x (number) x coordinate of the center of the tag loop
  444. - y (number) y coordinate of the center of the tag loop
  445. - text (string) text to place inside the tag
  446. - angle (number) angle of orientation in degrees [default: `0`]
  447. - r (number) radius of the loop [default: `5`]
  448. **
  449. = (object) set containing the tag path and the text element
  450. > Usage
  451. | paper.tag(50, 50, "$9.99", 60);
  452. \*/
  453. Raphael.fn.tag = function (x, y, text, angle, r) {
  454. var set = this.set();
  455. text = this.text(x, y, text).attr(Raphael.g.txtattr);
  456. return set.push(text.tag(angle, r), text);
  457. };
  458. /*\
  459. * Paper.flag
  460. [ method ]
  461. **
  462. * Puts the given `text` into a 'flag' tooltip. The text is given a default style according to @g.txtattr. See @Element.flag
  463. **
  464. > Parameters
  465. **
  466. - x (number) x coordinate of the flag's point
  467. - y (number) y coordinate of the flag's point
  468. - text (string) text to place inside the flag
  469. - angle (number) angle of orientation in degrees [default: `0`]
  470. **
  471. = (object) set containing the flag path and the text element
  472. > Usage
  473. | paper.flag(50, 50, "$9.99", 60);
  474. \*/
  475. Raphael.fn.flag = function (x, y, text, angle) {
  476. var set = this.set();
  477. text = this.text(x, y, text).attr(Raphael.g.txtattr);
  478. return set.push(text.flag(angle), text);
  479. };
  480. /*\
  481. * Paper.drop
  482. [ method ]
  483. **
  484. * Puts the given text into a 'drop' tooltip. The text is given a default style according to @g.txtattr. See @Element.drop
  485. **
  486. > Parameters
  487. **
  488. - x (number) x coordinate of the drop's point
  489. - y (number) y coordinate of the drop's point
  490. - text (string) text to place inside the drop
  491. - angle (number) angle of orientation in degrees [default: `0`]
  492. **
  493. = (object) set containing the drop path and the text element
  494. > Usage
  495. | paper.drop(50, 50, "$9.99", 60);
  496. \*/
  497. Raphael.fn.drop = function (x, y, text, angle) {
  498. var set = this.set();
  499. text = this.text(x, y, text).attr(Raphael.g.txtattr);
  500. return set.push(text.drop(angle), text);
  501. };
  502. /*\
  503. * Paper.blob
  504. [ method ]
  505. **
  506. * Puts the given text into a 'blob' tooltip. The text is given a default style according to @g.txtattr. See @Element.blob
  507. **
  508. > Parameters
  509. **
  510. - x (number) x coordinate of the blob's tail
  511. - y (number) y coordinate of the blob's tail
  512. - text (string) text to place inside the blob
  513. - angle (number) angle of orientation in degrees [default: `0`]
  514. **
  515. = (object) set containing the blob path and the text element
  516. > Usage
  517. | paper.blob(50, 50, "$9.99", 60);
  518. \*/
  519. Raphael.fn.blob = function (x, y, text, angle) {
  520. var set = this.set();
  521. text = this.text(x, y, text).attr(Raphael.g.txtattr);
  522. return set.push(text.blob(angle), text);
  523. };
  524. /**
  525. * Brightness functions on the Element prototype
  526. */
  527. /*\
  528. * Element.lighter
  529. [ method ]
  530. **
  531. * Makes the context element lighter by increasing the brightness and reducing the saturation by a given factor. Can be called on Sets.
  532. **
  533. > Parameters
  534. **
  535. - times (number) adjustment factor [default: `2`]
  536. **
  537. = (object) Element
  538. > Usage
  539. | paper.circle(50, 50, 20).attr({
  540. | fill: "#ff0000",
  541. | stroke: "#fff",
  542. | "stroke-width": 2
  543. | }).lighter(6);
  544. \*/
  545. Raphael.el.lighter = function (times) {
  546. times = times || 2;
  547. var fs = [this.attrs.fill, this.attrs.stroke];
  548. this.fs = this.fs || [fs[0], fs[1]];
  549. fs[0] = Raphael.rgb2hsb(Raphael.getRGB(fs[0]).hex);
  550. fs[1] = Raphael.rgb2hsb(Raphael.getRGB(fs[1]).hex);
  551. fs[0].b = Math.min(fs[0].b * times, 1);
  552. fs[0].s = fs[0].s / times;
  553. fs[1].b = Math.min(fs[1].b * times, 1);
  554. fs[1].s = fs[1].s / times;
  555. this.attr({fill: "hsb(" + [fs[0].h, fs[0].s, fs[0].b] + ")", stroke: "hsb(" + [fs[1].h, fs[1].s, fs[1].b] + ")"});
  556. return this;
  557. };
  558. /*\
  559. * Element.darker
  560. [ method ]
  561. **
  562. * Makes the context element darker by decreasing the brightness and increasing the saturation by a given factor. Can be called on Sets.
  563. **
  564. > Parameters
  565. **
  566. - times (number) adjustment factor [default: `2`]
  567. **
  568. = (object) Element
  569. > Usage
  570. | paper.circle(50, 50, 20).attr({
  571. | fill: "#ff0000",
  572. | stroke: "#fff",
  573. | "stroke-width": 2
  574. | }).darker(6);
  575. \*/
  576. Raphael.el.darker = function (times) {
  577. times = times || 2;
  578. var fs = [this.attrs.fill, this.attrs.stroke];
  579. this.fs = this.fs || [fs[0], fs[1]];
  580. fs[0] = Raphael.rgb2hsb(Raphael.getRGB(fs[0]).hex);
  581. fs[1] = Raphael.rgb2hsb(Raphael.getRGB(fs[1]).hex);
  582. fs[0].s = Math.min(fs[0].s * times, 1);
  583. fs[0].b = fs[0].b / times;
  584. fs[1].s = Math.min(fs[1].s * times, 1);
  585. fs[1].b = fs[1].b / times;
  586. this.attr({fill: "hsb(" + [fs[0].h, fs[0].s, fs[0].b] + ")", stroke: "hsb(" + [fs[1].h, fs[1].s, fs[1].b] + ")"});
  587. return this;
  588. };
  589. /*\
  590. * Element.resetBrightness
  591. [ method ]
  592. **
  593. * Resets brightness and saturation levels to their original values. See @Element.lighter and @Element.darker. Can be called on Sets.
  594. **
  595. = (object) Element
  596. > Usage
  597. | paper.circle(50, 50, 20).attr({
  598. | fill: "#ff0000",
  599. | stroke: "#fff",
  600. | "stroke-width": 2
  601. | }).lighter(6).resetBrightness();
  602. \*/
  603. Raphael.el.resetBrightness = function () {
  604. if (this.fs) {
  605. this.attr({ fill: this.fs[0], stroke: this.fs[1] });
  606. delete this.fs;
  607. }
  608. return this;
  609. };
  610. //alias to set prototype
  611. (function () {
  612. var brightness = ['lighter', 'darker', 'resetBrightness'],
  613. tooltips = ['popup', 'tag', 'flag', 'label', 'drop', 'blob'];
  614. for (var f in tooltips) (function (name) {
  615. Raphael.st[name] = function () {
  616. return Raphael.el[name].apply(this, arguments);
  617. };
  618. })(tooltips[f]);
  619. for (var f in brightness) (function (name) {
  620. Raphael.st[name] = function () {
  621. for (var i = 0; i < this.length; i++) {
  622. this[i][name].apply(this[i], arguments);
  623. }
  624. return this;
  625. };
  626. })(brightness[f]);
  627. })();
  628. //chart prototype for storing common functions
  629. Raphael.g = {
  630. /*\
  631. * g.shim
  632. [ object ]
  633. **
  634. * An attribute object that charts will set on all generated shims (shims being the invisible objects that mouse events are bound to)
  635. **
  636. > Default value
  637. | { stroke: 'none', fill: '#000', 'fill-opacity': 0 }
  638. \*/
  639. shim: { stroke: 'none', fill: '#000', 'fill-opacity': 0 },
  640. /*\
  641. * g.txtattr
  642. [ object ]
  643. **
  644. * An attribute object that charts and tooltips will set on any generated text
  645. **
  646. > Default value
  647. | { font: '12px Arial, sans-serif', fill: '#fff' }
  648. \*/
  649. txtattr: { font: '12px Arial, sans-serif', fill: '#fff' },
  650. /*\
  651. * g.colors
  652. [ array ]
  653. **
  654. * An array of color values that charts will iterate through when drawing chart data values.
  655. **
  656. \*/
  657. colors: (function () {
  658. var hues = [.6, .2, .05, .1333, .75, 0],
  659. colors = [];
  660. for (var i = 0; i < 10; i++) {
  661. if (i < hues.length) {
  662. colors.push('hsb(' + hues[i] + ',.75, .75)');
  663. } else {
  664. colors.push('hsb(' + hues[i - hues.length] + ', 1, .5)');
  665. }
  666. }
  667. return colors;
  668. })(),
  669. snapEnds: function(from, to, steps) {
  670. var f = from,
  671. t = to;
  672. if (f == t) {
  673. return {from: f, to: t, power: 0};
  674. }
  675. function round(a) {
  676. return Math.abs(a - .5) < .25 ? ~~(a) + .5 : Math.round(a);
  677. }
  678. var d = (t - f) / steps,
  679. r = ~~(d),
  680. R = r,
  681. i = 0;
  682. if (r) {
  683. while (R) {
  684. i--;
  685. R = ~~(d * Math.pow(10, i)) / Math.pow(10, i);
  686. }
  687. i ++;
  688. } else {
  689. while (!r) {
  690. i = i || 1;
  691. r = ~~(d * Math.pow(10, i)) / Math.pow(10, i);
  692. i++;
  693. }
  694. i && i--;
  695. }
  696. t = round(to * Math.pow(10, i)) / Math.pow(10, i);
  697. if (t < to) {
  698. t = round((to + .5) * Math.pow(10, i)) / Math.pow(10, i);
  699. }
  700. f = round((from - (i > 0 ? 0 : .5)) * Math.pow(10, i)) / Math.pow(10, i);
  701. return { from: f, to: t, power: i };
  702. },
  703. axis: function (x, y, length, from, to, steps, orientation, labels, type, dashsize, paper) {
  704. dashsize = dashsize == null ? 2 : dashsize;
  705. type = type || "t";
  706. steps = steps || 10;
  707. paper = arguments[arguments.length-1] //paper is always last argument
  708. var path = type == "|" || type == " " ? ["M", x + .5, y, "l", 0, .001] : orientation == 1 || orientation == 3 ? ["M", x + .5, y, "l", 0, -length] : ["M", x, y + .5, "l", length, 0],
  709. ends = this.snapEnds(from, to, steps),
  710. f = ends.from,
  711. t = ends.to,
  712. i = ends.power,
  713. j = 0,
  714. txtattr = { font: "11px 'Fontin Sans', Fontin-Sans, sans-serif" },
  715. text = paper.set(),
  716. d;
  717. d = (t - f) / steps;
  718. var label = f,
  719. rnd = i > 0 ? i : 0;
  720. dx = length / steps;
  721. if (+orientation == 1 || +orientation == 3) {
  722. var Y = y,
  723. addon = (orientation - 1 ? 1 : -1) * (dashsize + 3 + !!(orientation - 1));
  724. while (Y >= y - length) {
  725. type != "-" && type != " " && (path = path.concat(["M", x - (type == "+" || type == "|" ? dashsize : !(orientation - 1) * dashsize * 2), Y + .5, "l", dashsize * 2 + 1, 0]));
  726. text.push(paper.text(x + addon, Y, (labels && labels[j++]) || (Math.round(label) == label ? label : +label.toFixed(rnd))).attr(txtattr).attr({ "text-anchor": orientation - 1 ? "start" : "end" }));
  727. label += d;
  728. Y -= dx;
  729. }
  730. if (Math.round(Y + dx - (y - length))) {
  731. type != "-" && type != " " && (path = path.concat(["M", x - (type == "+" || type == "|" ? dashsize : !(orientation - 1) * dashsize * 2), y - length + .5, "l", dashsize * 2 + 1, 0]));
  732. text.push(paper.text(x + addon, y - length, (labels && labels[j]) || (Math.round(label) == label ? label : +label.toFixed(rnd))).attr(txtattr).attr({ "text-anchor": orientation - 1 ? "start" : "end" }));
  733. }
  734. } else {
  735. label = f;
  736. rnd = (i > 0) * i;
  737. addon = (orientation ? -1 : 1) * (dashsize + 9 + !orientation);
  738. var X = x,
  739. dx = length / steps,
  740. txt = 0,
  741. prev = 0;
  742. while (X <= x + length) {
  743. type != "-" && type != " " && (path = path.concat(["M", X + .5, y - (type == "+" ? dashsize : !!orientation * dashsize * 2), "l", 0, dashsize * 2 + 1]));
  744. text.push(txt = paper.text(X, y + addon, (labels && labels[j++]) || (Math.round(label) == label ? label : +label.toFixed(rnd))).attr(txtattr));
  745. var bb = txt.getBBox();
  746. if (prev >= bb.x - 5) {
  747. text.pop(text.length - 1).remove();
  748. } else {
  749. prev = bb.x + bb.width;
  750. }
  751. label += d;
  752. X += dx;
  753. }
  754. if (Math.round(X - dx - x - length)) {
  755. type != "-" && type != " " && (path = path.concat(["M", x + length + .5, y - (type == "+" ? dashsize : !!orientation * dashsize * 2), "l", 0, dashsize * 2 + 1]));
  756. text.push(paper.text(x + length, y + addon, (labels && labels[j]) || (Math.round(label) == label ? label : +label.toFixed(rnd))).attr(txtattr));
  757. }
  758. }
  759. var res = paper.path(path);
  760. res.text = text;
  761. res.all = paper.set([res, text]);
  762. res.remove = function () {
  763. this.text.remove();
  764. this.constructor.prototype.remove.call(this);
  765. };
  766. return res;
  767. },
  768. labelise: function(label, val, total) {
  769. if (label) {
  770. return (label + "").replace(/(##+(?:\.#+)?)|(%%+(?:\.%+)?)/g, function (all, value, percent) {
  771. if (value) {
  772. return (+val).toFixed(value.replace(/^#+\.?/g, "").length);
  773. }
  774. if (percent) {
  775. return (val * 100 / total).toFixed(percent.replace(/^%+\.?/g, "").length) + "%";
  776. }
  777. });
  778. } else {
  779. return (+val).toFixed(0);
  780. }
  781. }
  782. }