RGraph.gauge.js 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273
  1. // version: 2014-06-26
  2. /**
  3. * o--------------------------------------------------------------------------------o
  4. * | This file is part of the RGraph package. RGraph is Free Software, licensed |
  5. * | under the MIT license - so it's free to use for all purposes. If you want to |
  6. * | donate to help keep the project going then you can do so here: |
  7. * | |
  8. * | http://www.rgraph.net/donate |
  9. * o--------------------------------------------------------------------------------o
  10. */
  11. RGraph = window.RGraph || {isRGraph: true};
  12. /**
  13. * The line chart constructor
  14. *
  15. * @param object canvas The canvas ID
  16. * @param array data The chart data
  17. * @param array ... Other lines to plot
  18. */
  19. RGraph.Gauge = function (id, min, max, value)
  20. {
  21. var tmp = RGraph.getCanvasTag(id);
  22. // Get the canvas and context objects
  23. this.id = tmp[0];
  24. this.canvas = tmp[1];
  25. this.context = this.canvas.getContext ? this.canvas.getContext("2d", {alpha: (typeof id === 'object' && id.alpha === false) ? false : true}) : null;
  26. this.canvas.__object__ = this;
  27. this.type = 'gauge';
  28. this.min = min;
  29. this.max = max;
  30. this.value = RGraph.stringsToNumbers(value);
  31. this.isRGraph = true;
  32. this.currentValue = null;
  33. this.uid = RGraph.CreateUID();
  34. this.canvas.uid = this.canvas.uid ? this.canvas.uid : RGraph.CreateUID();
  35. this.colorsParsed = false;
  36. this.coordsText = [];
  37. this.original_colors = [];
  38. this.firstDraw = true; // After the first draw this will be false
  39. /**
  40. * Range checking
  41. */
  42. if (typeof(this.value) == 'object') {
  43. for (var i=0; i<this.value.length; ++i) {
  44. if (this.value[i] > this.max) this.value[i] = max;
  45. if (this.value[i] < this.min) this.value[i] = min;
  46. }
  47. } else {
  48. if (this.value > this.max) this.value = max;
  49. if (this.value < this.min) this.value = min;
  50. }
  51. /**
  52. * Compatibility with older browsers
  53. */
  54. //RGraph.OldBrowserCompat(this.context);
  55. // Various config type stuff
  56. this.properties =
  57. {
  58. 'chart.angles.start': null,
  59. 'chart.angles.end': null,
  60. 'chart.centerx': null,
  61. 'chart.centery': null,
  62. 'chart.radius': null,
  63. 'chart.gutter.left': 15,
  64. 'chart.gutter.right': 15,
  65. 'chart.gutter.top': 15,
  66. 'chart.gutter.bottom': 15,
  67. 'chart.border.width': 10,
  68. 'chart.title.top': '',
  69. 'chart.title.top.font':'Arial',
  70. 'chart.title.top.size':14,
  71. 'chart.title.top.color':'#333',
  72. 'chart.title.top.bold':false,
  73. 'chart.title.top.pos': null,
  74. 'chart.title.bottom': '',
  75. 'chart.title.bottom.font':'Arial',
  76. 'chart.title.bottom.size':14,
  77. 'chart.title.bottom.color':'#333',
  78. 'chart.title.bottom.bold':false,
  79. 'chart.title.bottom.pos':null,
  80. 'chart.text.font': 'Arial',
  81. 'chart.text.color': '#666',
  82. 'chart.text.size': 10,
  83. 'chart.background.color': 'white',
  84. 'chart.background.gradient': false,
  85. 'chart.scale.decimals': 0,
  86. 'chart.scale.point': '.',
  87. 'chart.scale.thousand': ',',
  88. 'chart.units.pre': '',
  89. 'chart.units.post': '',
  90. 'chart.value.text': false,
  91. 'chart.value.text.y.pos': 0.5,
  92. 'chart.value.text.units.pre': null,
  93. 'chart.value.text.units.post': null,
  94. 'chart.red.start': 0.9 * this.max,
  95. 'chart.red.color': '#DC3912',
  96. 'chart.yellow.color': '#FF9900',
  97. 'chart.green.end': 0.7 * this.max,
  98. 'chart.green.color': 'rgba(0,0,0,0)',
  99. 'chart.colors.ranges': null,
  100. 'chart.needle.size': null,
  101. 'chart.needle.tail': false,
  102. 'chart.needle.colors': ['#D5604D', 'red', 'green', 'yellow'],
  103. 'chart.needle.type': 'triangle',
  104. 'chart.border.outer': '#ccc',
  105. 'chart.border.inner': '#f1f1f1',
  106. 'chart.border.outline': 'black',
  107. 'chart.centerpin.color': 'blue',
  108. 'chart.centerpin.radius': null,
  109. 'chart.zoom.background': true,
  110. 'chart.zoom.action': 'zoom',
  111. 'chart.tickmarks.small': 25,
  112. 'chart.tickmarks.small.color': 'black',
  113. 'chart.tickmarks.medium': 0,
  114. 'chart.tickmarks.medium.color': 'black',
  115. 'chart.tickmarks.big': 5,
  116. 'chart.tickmarks.big.color': 'black',
  117. 'chart.labels.count': 5,
  118. 'chart.labels.centered': false,
  119. 'chart.labels.offset': 0,
  120. 'chart.border.gradient': false,
  121. 'chart.adjustable': false,
  122. 'chart.shadow': true,
  123. 'chart.shadow.color': 'gray',
  124. 'chart.shadow.offsetx': 0,
  125. 'chart.shadow.offsety': 0,
  126. 'chart.shadow.blur': 15
  127. }
  128. /*
  129. * Translate half a pixel for antialiasing purposes - but only if it hasn't beeen
  130. * done already
  131. */
  132. if (!this.canvas.__rgraph_aa_translated__) {
  133. this.context.translate(0.5,0.5);
  134. this.canvas.__rgraph_aa_translated__ = true;
  135. }
  136. // Short variable names
  137. var RG = RGraph;
  138. var ca = this.canvas;
  139. var co = ca.getContext('2d');
  140. var prop = this.properties;
  141. var jq = jQuery;
  142. var pa = RG.Path;
  143. var win = window;
  144. var doc = document;
  145. var ma = Math;
  146. /**
  147. * "Decorate" the object with the generic effects if the effects library has been included
  148. */
  149. if (RG.Effects && typeof RG.Effects.decorate === 'function') {
  150. RG.Effects.decorate(this);
  151. }
  152. /**
  153. * An all encompassing accessor
  154. *
  155. * @param string name The name of the property
  156. * @param mixed value The value of the property
  157. */
  158. this.set =
  159. this.Set = function (name, value)
  160. {
  161. name = name.toLowerCase();
  162. /**
  163. * This should be done first - prepend the propertyy name with "chart." if necessary
  164. */
  165. if (name.substr(0,6) != 'chart.') {
  166. name = 'chart.' + name;
  167. }
  168. /**
  169. * Title compatibility
  170. */
  171. if (name == 'chart.title') name = 'chart.title.top';
  172. if (name == 'chart.title.font') name = 'chart.title.top.font';
  173. if (name == 'chart.title.size') name = 'chart.title.top.size';
  174. if (name == 'chart.title.color') name = 'chart.title.top.color';
  175. if (name == 'chart.title.bold') name = 'chart.title.top.bold';
  176. // BC
  177. if (name == 'chart.needle.color') {
  178. name = 'chart.needle.colors';
  179. }
  180. prop[name] = value;
  181. return this;
  182. };
  183. /**
  184. * An all encompassing accessor
  185. *
  186. * @param string name The name of the property
  187. */
  188. this.get =
  189. this.Get = function (name)
  190. {
  191. /**
  192. * This should be done first - prepend the property name with "chart." if necessary
  193. */
  194. if (name.substr(0,6) != 'chart.') {
  195. name = 'chart.' + name;
  196. }
  197. // BC
  198. if (name == 'chart.needle.color') {
  199. name = 'chart.needle.colors';
  200. }
  201. return prop[name];
  202. };
  203. /**
  204. * The function you call to draw the line chart
  205. *
  206. * @param bool An optional bool used internally to ditinguish whether the
  207. * line chart is being called by the bar chart
  208. */
  209. this.draw =
  210. this.Draw = function ()
  211. {
  212. /**
  213. * Fire the onbeforedraw event
  214. */
  215. RG.FireCustomEvent(this, 'onbeforedraw');
  216. /**
  217. * Store the value (for animation primarily
  218. */
  219. this.currentValue = this.value;
  220. /**
  221. * This is new in May 2011 and facilitates indiviual gutter settings,
  222. * eg chart.gutter.left
  223. */
  224. this.gutterLeft = prop['chart.gutter.left'];
  225. this.gutterRight = prop['chart.gutter.right'];
  226. this.gutterTop = prop['chart.gutter.top'];
  227. this.gutterBottom = prop['chart.gutter.bottom'];
  228. this.centerx = ((ca.width - this.gutterLeft - this.gutterRight) / 2) + this.gutterLeft;
  229. this.centery = ((ca.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop;
  230. this.radius = Math.min(
  231. ((ca.width - this.gutterLeft - this.gutterRight) / 2),
  232. ((ca.height - this.gutterTop - this.gutterBottom) / 2)
  233. );
  234. this.startAngle = prop['chart.angles.start'] ? prop['chart.angles.start'] : (RG.HALFPI / 3) + RG.HALFPI;
  235. this.endAngle = prop['chart.angles.end'] ? prop['chart.angles.end'] : RG.TWOPI + RG.HALFPI - (RG.HALFPI / 3);
  236. /**
  237. * Reset this so it doesn't keep growing
  238. */
  239. this.coordsText = [];
  240. /**
  241. * You can now override the positioning and radius if you so wish.
  242. */
  243. if (typeof(prop['chart.centerx']) == 'number') this.centerx = prop['chart.centerx'];
  244. if (typeof(prop['chart.centery']) == 'number') this.centery = prop['chart.centery'];
  245. if (typeof(prop['chart.radius']) == 'number') this.radius = prop['chart.radius'];
  246. /**
  247. * Parse the colors. This allows for simple gradient syntax
  248. */
  249. if (!this.colorsParsed) {
  250. this.parseColors();
  251. // Don't want to do this again
  252. this.colorsParsed = true;
  253. }
  254. // This has to be in the constructor
  255. this.centerpinRadius = 0.16 * this.radius;
  256. if (typeof(prop['chart.centerpin.radius']) == 'number') {
  257. this.centerpinRadius = prop['chart.centerpin.radius'];
  258. }
  259. /**
  260. * Setup the context menu if required
  261. */
  262. if (prop['chart.contextmenu']) {
  263. RG.ShowContext(this);
  264. }
  265. // DRAW THE CHART HERE
  266. this.DrawBackGround();
  267. this.DrawGradient();
  268. this.DrawColorBands();
  269. this.DrawSmallTickmarks();
  270. this.DrawMediumTickmarks();
  271. this.DrawBigTickmarks();
  272. this.DrawLabels();
  273. this.DrawTopTitle();
  274. this.DrawBottomTitle();
  275. if (typeof(this.value) == 'object') {
  276. for (var i=0; i<this.value.length; ++i) {
  277. this.DrawNeedle(this.value[i], prop['chart.needle.colors'][i], i);
  278. }
  279. } else {
  280. this.DrawNeedle(this.value, prop['chart.needle.colors'][0], 0);
  281. }
  282. this.DrawCenterpin();
  283. /**
  284. * This function enables resizing
  285. */
  286. if (prop['chart.resizable']) {
  287. RG.AllowResizing(this);
  288. }
  289. /**
  290. * This installs the event listeners
  291. */
  292. RG.InstallEventListeners(this);
  293. /**
  294. * Fire the onfirstdraw event
  295. */
  296. if (this.firstDraw) {
  297. RG.fireCustomEvent(this, 'onfirstdraw');
  298. this.firstDrawFunc();
  299. this.firstDraw = false;
  300. }
  301. /**
  302. * Fire the RGraph ondraw event
  303. */
  304. RG.FireCustomEvent(this, 'ondraw');
  305. return this;
  306. };
  307. /**
  308. * Draw the background
  309. */
  310. this.drawBackGround =
  311. this.DrawBackGround = function ()
  312. {
  313. // Shadow //////////////////////////////////////////////
  314. if (prop['chart.shadow']) {
  315. RG.SetShadow(this, prop['chart.shadow.color'], prop['chart.shadow.offsetx'], prop['chart.shadow.offsety'], prop['chart.shadow.blur']);
  316. }
  317. co.beginPath();
  318. co.fillStyle = prop['chart.background.color'];
  319. //co.moveTo(this.centerx, this.centery)
  320. co.arc(this.centerx, this.centery, this.radius, 0, RG.TWOPI, 0);
  321. co.fill();
  322. // Turn off the shadow
  323. RG.NoShadow(this);
  324. // Shadow //////////////////////////////////////////////
  325. var grad = co.createRadialGradient(this.centerx + 50, this.centery - 50, 0, this.centerx + 50, this.centery - 50, 150);
  326. grad.addColorStop(0, '#eee');
  327. grad.addColorStop(1, 'white');
  328. var borderWidth = prop['chart.border.width'];
  329. co.beginPath();
  330. co.fillStyle = prop['chart.background.color'];
  331. co.arc(this.centerx, this.centery, this.radius, 0, RG.TWOPI, 0);
  332. co.fill();
  333. /**
  334. * Draw the gray circle
  335. */
  336. co.beginPath();
  337. co.fillStyle = prop['chart.border.outer'];
  338. co.arc(this.centerx, this.centery, this.radius, 0, RG.TWOPI, 0);
  339. co.fill();
  340. /**
  341. * Draw the light gray inner border
  342. */
  343. co.beginPath();
  344. co.fillStyle = prop['chart.border.inner'];
  345. co.arc(this.centerx, this.centery, this.radius - borderWidth, 0, RG.TWOPI, 0);
  346. co.fill();
  347. // Draw the white circle inner border
  348. co.beginPath();
  349. co.fillStyle = prop['chart.background.color'];
  350. co.arc(this.centerx, this.centery, this.radius - borderWidth - 4, 0, RG.TWOPI, 0);
  351. co.fill();
  352. // Draw the circle background. Can be any colour now.
  353. co.beginPath();
  354. co.fillStyle = prop['chart.background.color'];
  355. co.arc(this.centerx, this.centery, this.radius - borderWidth - 4, 0, RG.TWOPI, 0);
  356. co.fill();
  357. if (prop['chart.background.gradient']) {
  358. // Draw a partially transparent gradient that sits on top of the background
  359. co.beginPath();
  360. co.fillStyle = RG.RadialGradient(this,
  361. this.centerx,
  362. this.centery,
  363. 0,
  364. this.centerx,
  365. this.centery,
  366. this.radius,
  367. 'rgba(255,255,255,0.6)',
  368. 'rgba(255,255,255,0.1)');
  369. co.arc(this.centerx, this.centery, this.radius - borderWidth - 4, 0, RG.TWOPI, 0);
  370. co.fill();
  371. }
  372. // Draw a black border around the chart
  373. co.beginPath();
  374. co.strokeStyle = prop['chart.border.outline'];
  375. co.arc(this.centerx, this.centery, this.radius, 0, RG.TWOPI, 0);
  376. co.stroke();
  377. };
  378. /**
  379. * This function draws the smaller tickmarks
  380. */
  381. this.drawSmallTickmarks =
  382. this.DrawSmallTickmarks = function ()
  383. {
  384. var numTicks = prop['chart.tickmarks.small'];
  385. co.lineWidth = 1;
  386. for (var i=0; i<=numTicks; ++i) {
  387. co.beginPath();
  388. co.strokeStyle = prop['chart.tickmarks.small.color'];
  389. var a = (((this.endAngle - this.startAngle) / numTicks) * i) + this.startAngle;
  390. co.arc(this.centerx, this.centery, this.radius - prop['chart.border.width'] - 10, a, a + 0.00001, 0);
  391. co.arc(this.centerx, this.centery, this.radius - prop['chart.border.width'] - 10 - 5, a, a + 0.00001, 0);
  392. co.stroke();
  393. }
  394. };
  395. /**
  396. * This function draws the medium sized tickmarks
  397. */
  398. this.drawMediumTickmarks =
  399. this.DrawMediumTickmarks = function ()
  400. {
  401. if (prop['chart.tickmarks.medium']) {
  402. var numTicks = prop['chart.tickmarks.medium'];
  403. co.lineWidth = 3;
  404. co.lineCap = 'round';
  405. co.strokeStyle = prop['chart.tickmarks.medium.color'];
  406. for (var i=0; i<=numTicks; ++i) {
  407. co.beginPath();
  408. var a = (((this.endAngle - this.startAngle) / numTicks) * i) + this.startAngle + (((this.endAngle - this.startAngle) / (2 * numTicks)));
  409. if (a > this.startAngle && a< this.endAngle) {
  410. co.arc(this.centerx, this.centery, this.radius - prop['chart.border.width'] - 10, a, a + 0.00001, 0);
  411. co.arc(this.centerx, this.centery, this.radius - prop['chart.border.width'] - 10 - 6, a, a + 0.00001, 0);
  412. }
  413. co.stroke();
  414. }
  415. }
  416. };
  417. /**
  418. * This function draws the large, bold tickmarks
  419. */
  420. this.drawBigTickmarks =
  421. this.DrawBigTickmarks = function ()
  422. {
  423. var numTicks = prop['chart.tickmarks.big'];
  424. co.lineWidth = 3;
  425. co.lineCap = 'round';
  426. for (var i=0; i<=numTicks; ++i) {
  427. co.beginPath();
  428. co.strokeStyle = prop['chart.tickmarks.big.color'];
  429. var a = (((this.endAngle - this.startAngle) / numTicks) * i) + this.startAngle;
  430. co.arc(this.centerx, this.centery, this.radius - prop['chart.border.width'] - 10, a, a + 0.00001, 0);
  431. co.arc(this.centerx, this.centery, this.radius - prop['chart.border.width'] - 10 - 10, a, a + 0.00001, 0);
  432. co.stroke();
  433. }
  434. };
  435. /**
  436. * This function draws the centerpin
  437. */
  438. this.drawCenterpin =
  439. this.DrawCenterpin = function ()
  440. {
  441. var offset = 6;
  442. var grad = co.createRadialGradient(this.centerx + offset, this.centery - offset, 0, this.centerx + offset, this.centery - offset, 25);
  443. grad.addColorStop(0, '#ddf');
  444. grad.addColorStop(1, prop['chart.centerpin.color']);
  445. co.beginPath();
  446. co.fillStyle = grad;
  447. co.arc(this.centerx, this.centery, this.centerpinRadius, 0, RG.TWOPI, 0);
  448. co.fill();
  449. };
  450. /**
  451. * This function draws the labels
  452. */
  453. this.drawLabels =
  454. this.DrawLabels = function ()
  455. {
  456. co.fillStyle = prop['chart.text.color'];
  457. var font = prop['chart.text.font'];
  458. var size = prop['chart.text.size'];
  459. var num = prop['chart.labels.specific'] ? (prop['chart.labels.specific'].length - 1) : prop['chart.labels.count'];
  460. co.beginPath();
  461. for (var i=0; i<=num; ++i) {
  462. var hyp = (this.radius - 25 - prop['chart.border.width']) - prop['chart.labels.offset'];
  463. var a = (this.endAngle - this.startAngle) / num
  464. a = this.startAngle + (i * a);
  465. a -= RG.HALFPI;
  466. var x = this.centerx - (Math.sin(a) * hyp);
  467. var y = this.centery + (Math.cos(a) * hyp);
  468. var hAlign = x > this.centerx ? 'right' : 'left';
  469. var vAlign = y > this.centery ? 'bottom' : 'top';
  470. // This handles the label alignment when the label is on a PI/HALFPI boundary
  471. if (a == RG.HALFPI) {
  472. vAlign = 'center';
  473. } else if (a == RG.PI) {
  474. hAlign = 'center';
  475. } else if (a == (RG.HALFPI + RG.PI) ) {
  476. vAlign = 'center';
  477. }
  478. /**
  479. * Can now force center alignment
  480. */
  481. if (prop['chart.labels.centered']) {
  482. hAlign = 'center';
  483. vAlign = 'center';
  484. }
  485. RG.Text2(this, {'font':font,
  486. 'size':size,
  487. 'x':x,
  488. 'y':y,
  489. 'text':prop['chart.labels.specific'] ? prop['chart.labels.specific'][i] : RG.number_format(this, (((this.max - this.min) * (i / num)) + this.min).toFixed(prop['chart.scale.decimals']), prop['chart.units.pre'], prop['chart.units.post']),
  490. 'halign':hAlign,
  491. 'valign':vAlign,
  492. 'tag': prop['chart.labels.specific'] ? 'labels.specific' : 'labels'
  493. });
  494. }
  495. co.fill();
  496. /**
  497. * Draw the textual value if requested
  498. */
  499. if (prop['chart.value.text']) {
  500. var x = this.centerx;
  501. var y = this.centery + (prop['chart.value.text.y.pos'] * this.radius);
  502. var units_pre = typeof(prop['chart.value.text.units.pre']) == 'string' ? prop['chart.value.text.units.pre'] : prop['chart.units.pre'];
  503. var units_post = typeof(prop['chart.value.text.units.post']) == 'string' ? prop['chart.value.text.units.post'] : prop['chart.units.post'];
  504. RG.Text2(this, {'font':font,
  505. 'size':size + 2,
  506. 'x':x,
  507. 'y':y,
  508. 'text':RG.number_format(this, this.value.toFixed(prop['chart.scale.decimals']), units_pre, units_post),
  509. 'halign':'center',
  510. 'valign':'center',
  511. 'bounding':true,
  512. 'boundingFill':'white',
  513. 'tag': 'value.text'
  514. });
  515. }
  516. };
  517. /**
  518. * This function draws the top title
  519. */
  520. this.drawTopTitle =
  521. this.DrawTopTitle = function ()
  522. {
  523. var x = this.centerx;
  524. var y = this.centery - 25;
  525. // Totally override the calculated positioning
  526. if (typeof(prop['chart.title.top.pos']) == 'number') {
  527. y = this.centery - (this.radius * prop['chart.title.top.pos']);
  528. }
  529. if (prop['chart.title.top']) {
  530. co.fillStyle = prop['chart.title.top.color'];
  531. RG.Text2(this, {'font':prop['chart.title.top.font'],
  532. 'size':prop['chart.title.top.size'],
  533. 'x':x,
  534. 'y':y,
  535. 'text':String(prop['chart.title.top']),
  536. 'halign':'center',
  537. 'valign':'bottom',
  538. 'bold':prop['chart.title.top.bold'],
  539. 'tag': 'title.top'
  540. });
  541. }
  542. };
  543. /**
  544. * This function draws the bottom title
  545. */
  546. this.drawBottomTitle =
  547. this.DrawBottomTitle = function ()
  548. {
  549. var x = this.centerx;
  550. var y = this.centery + this.centerpinRadius + 10;
  551. // Totally override the calculated positioning
  552. if (typeof(prop['chart.title.bottom.pos']) == 'number') {
  553. y = this.centery + (this.radius * prop['chart.title.bottom.pos']);
  554. }
  555. if (prop['chart.title.bottom']) {
  556. co.fillStyle = prop['chart.title.bottom.color'];
  557. RG.Text2(this, {'font':prop['chart.title.bottom.font'],
  558. 'size':prop['chart.title.bottom.size'],
  559. 'x':x,
  560. 'y':y,
  561. 'text':String(prop['chart.title.bottom']),
  562. 'halign':'center',
  563. 'valign':'top',
  564. 'bold':prop['chart.title.bottom.bold'],
  565. 'tag': 'title.bottom'
  566. });
  567. }
  568. };
  569. /**
  570. * This function draws the Needle
  571. *
  572. * @param number value The value to draw the needle for
  573. */
  574. this.drawNeedle =
  575. this.DrawNeedle = function (value, color, index)
  576. {
  577. var type = prop['chart.needle.type'];
  578. co.lineWidth = 0.5;
  579. co.strokeStyle = 'gray';
  580. co.fillStyle = color;
  581. var angle = (this.endAngle - this.startAngle) * ((value - this.min) / (this.max - this.min));
  582. angle += this.startAngle;
  583. // Work out the size of the needle
  584. if ( typeof(prop['chart.needle.size']) == 'object'
  585. && prop['chart.needle.size']
  586. && typeof(prop['chart.needle.size'][index]) == 'number') {
  587. var size = prop['chart.needle.size'][index];
  588. } else if (typeof(prop['chart.needle.size']) == 'number') {
  589. var size = prop['chart.needle.size'];
  590. } else {
  591. var size = this.radius - 25 - prop['chart.border.width'];
  592. }
  593. if (type == 'line') {
  594. co.beginPath();
  595. co.lineWidth = 7;
  596. co.strokeStyle = color;
  597. co.arc(this.centerx,
  598. this.centery,
  599. size,
  600. angle,
  601. angle + 0.0001,
  602. false);
  603. co.lineTo(this.centerx, this.centery);
  604. if (prop['chart.needle.tail']) {
  605. co.arc(this.centerx, this.centery, this.radius * 0.2 , angle + RG.PI, angle + 0.00001 + RG.PI, false);
  606. }
  607. co.lineTo(this.centerx, this.centery);
  608. co.stroke();
  609. //co.fill();
  610. } else {
  611. co.beginPath();
  612. co.arc(this.centerx, this.centery, size, angle, angle + 0.00001, false);
  613. co.arc(this.centerx, this.centery, this.centerpinRadius * 0.5, angle + RG.HALFPI, angle + 0.00001 + RG.HALFPI, false);
  614. if (prop['chart.needle.tail']) {
  615. co.arc(this.centerx, this.centery, this.radius * 0.2 , angle + RG.PI, angle + 0.00001 + RG.PI, false);
  616. }
  617. co.arc(this.centerx, this.centery, this.centerpinRadius * 0.5, angle - RG.HALFPI, angle - 0.00001 - RG.HALFPI, false);
  618. co.stroke();
  619. co.fill();
  620. /**
  621. * Store the angle in an object variable
  622. */
  623. this.angle = angle;
  624. }
  625. };
  626. /**
  627. * This draws the green background to the tickmarks
  628. */
  629. this.drawColorBands =
  630. this.DrawColorBands = function ()
  631. {
  632. if (RG.is_array(prop['chart.colors.ranges'])) {
  633. var ranges = prop['chart.colors.ranges'];
  634. for (var i=0; i<ranges.length; ++i) {
  635. //co.strokeStyle = prop['chart.strokestyle'] ? prop['chart.strokestyle'] : ranges[i][2];
  636. co.fillStyle = ranges[i][2];
  637. co.lineWidth = 0;//prop['chart.linewidth.segments'];
  638. co.beginPath();
  639. co.arc(this.centerx,
  640. this.centery,
  641. this.radius - 10 - prop['chart.border.width'],
  642. (((ranges[i][0] - this.min) / (this.max - this.min)) * (this.endAngle - this.startAngle)) + this.startAngle,
  643. (((ranges[i][1] - this.min) / (this.max - this.min)) * (this.endAngle - this.startAngle)) + this.startAngle,
  644. false);
  645. co.arc(this.centerx,
  646. this.centery,
  647. this.radius - 20 - prop['chart.border.width'],
  648. (((ranges[i][1] - this.min) / (this.max - this.min)) * (this.endAngle - this.startAngle)) + this.startAngle,
  649. (((ranges[i][0] - this.min) / (this.max - this.min)) * (this.endAngle - this.startAngle)) + this.startAngle,
  650. true);
  651. co.closePath();
  652. co.fill();
  653. }
  654. return;
  655. }
  656. /**
  657. * Draw the GREEN region
  658. */
  659. co.strokeStyle = prop['chart.green.color'];
  660. co.fillStyle = prop['chart.green.color'];
  661. var greenStart = this.startAngle;
  662. var greenEnd = this.startAngle + (this.endAngle - this.startAngle) * ((prop['chart.green.end'] - this.min) / (this.max - this.min))
  663. co.beginPath();
  664. co.arc(this.centerx, this.centery, this.radius - 10 - prop['chart.border.width'], greenStart, greenEnd, false);
  665. co.arc(this.centerx, this.centery, this.radius - 20 - prop['chart.border.width'], greenEnd, greenStart, true);
  666. co.fill();
  667. /**
  668. * Draw the YELLOW region
  669. */
  670. co.strokeStyle = prop['chart.yellow.color'];
  671. co.fillStyle = prop['chart.yellow.color'];
  672. var yellowStart = greenEnd;
  673. var yellowEnd = this.startAngle + (this.endAngle - this.startAngle) * ((prop['chart.red.start'] - this.min) / (this.max - this.min))
  674. co.beginPath();
  675. co.arc(this.centerx, this.centery, this.radius - 10 - prop['chart.border.width'], yellowStart, yellowEnd, false);
  676. co.arc(this.centerx, this.centery, this.radius - 20 - prop['chart.border.width'], yellowEnd, yellowStart, true);
  677. co.fill();
  678. /**
  679. * Draw the RED region
  680. */
  681. co.strokeStyle = prop['chart.red.color'];
  682. co.fillStyle = prop['chart.red.color'];
  683. var redStart = yellowEnd;
  684. var redEnd = this.startAngle + (this.endAngle - this.startAngle) * ((this.max - this.min) / (this.max - this.min))
  685. co.beginPath();
  686. co.arc(this.centerx, this.centery, this.radius - 10 - prop['chart.border.width'], redStart, redEnd, false);
  687. co.arc(this.centerx, this.centery, this.radius - 20 - prop['chart.border.width'], redEnd, redStart, true);
  688. co.fill();
  689. };
  690. /**
  691. * A placeholder function
  692. *
  693. * @param object The event object
  694. */
  695. this.getShape = function (e) {};
  696. /**
  697. * A getValue method
  698. *
  699. * @param object e An event object
  700. */
  701. this.getValue = function (e)
  702. {
  703. var mouseXY = RG.getMouseXY(e);
  704. var mouseX = mouseXY[0];
  705. var mouseY = mouseXY[1];
  706. var angle = RG.getAngleByXY(this.centerx, this.centery, mouseX, mouseY);
  707. if (angle >= 0 && angle <= RG.HALFPI) {
  708. angle += RG.TWOPI;
  709. }
  710. var value = ((angle - this.startAngle) / (this.endAngle - this.startAngle)) * (this.max - this.min);
  711. value = value + this.min;
  712. if (value < this.min) {
  713. value = this.min
  714. }
  715. if (value > this.max) {
  716. value = this.max
  717. }
  718. return value;
  719. };
  720. /**
  721. * The getObjectByXY() worker method. Don't call this call:
  722. *
  723. * RGraph.ObjectRegistry.getObjectByXY(e)
  724. *
  725. * @param object e The event object
  726. */
  727. this.getObjectByXY = function (e)
  728. {
  729. var mouseXY = RGraph.getMouseXY(e);
  730. if (
  731. mouseXY[0] > (this.centerx - this.radius)
  732. && mouseXY[0] < (this.centerx + this.radius)
  733. && mouseXY[1] > (this.centery - this.radius)
  734. && mouseXY[1] < (this.centery + this.radius)
  735. && RG.getHypLength(this.centerx, this.centery, mouseXY[0], mouseXY[1]) <= this.radius
  736. ) {
  737. return this;
  738. }
  739. };
  740. /**
  741. * This draws the gradient that goes around the Gauge chart
  742. */
  743. this.drawGradient =
  744. this.DrawGradient = function ()
  745. {
  746. if (prop['chart.border.gradient']) {
  747. co.beginPath();
  748. var grad = co.createRadialGradient(this.centerx, this.centery, this.radius, this.centerx, this.centery, this.radius - 15);
  749. grad.addColorStop(0, 'gray');
  750. grad.addColorStop(1, 'white');
  751. co.fillStyle = grad;
  752. co.arc(this.centerx, this.centery, this.radius, 0, RG.TWOPI, false)
  753. co.arc(this.centerx, this.centery, this.radius - 15, RG.TWOPI,0, true)
  754. co.fill();
  755. }
  756. };
  757. /**
  758. * This method handles the adjusting calculation for when the mouse is moved
  759. *
  760. * @param object e The event object
  761. */
  762. this.adjusting_mousemove =
  763. this.Adjusting_mousemove = function (e)
  764. {
  765. /**
  766. * Handle adjusting for the Bar
  767. */
  768. if (prop['chart.adjustable'] && RG.Registry.Get('chart.adjusting') && RG.Registry.Get('chart.adjusting').uid == this.uid) {
  769. this.value = this.getValue(e);
  770. //RG.Clear(this.canvas);
  771. RG.redrawCanvas(this.canvas);
  772. RG.fireCustomEvent(this, 'onadjust');
  773. }
  774. };
  775. /**
  776. * This method returns an appropriate angle for the given value (in RADIANS)
  777. *
  778. * @param number value The value to get the angle for
  779. */
  780. this.getAngle = function (value)
  781. {
  782. // Higher than max
  783. if (value > this.max || value < this.min) {
  784. return null;
  785. }
  786. //var value = ((angle - this.startAngle) / (this.endAngle - this.startAngle)) * (this.max - this.min);
  787. //value = value + this.min;
  788. var angle = (((value - this.min) / (this.max - this.min)) * (this.endAngle - this.startAngle)) + this.startAngle;
  789. return angle;
  790. };
  791. /**
  792. * This allows for easy specification of gradients. Could optimise this not to repeatedly call parseSingleColors()
  793. */
  794. this.parseColors = function ()
  795. {
  796. // Save the original colors so that they can be restored when the canvas is reset
  797. if (this.original_colors.length === 0) {
  798. this.original_colors['chart.background.color'] = RG.array_clone(prop['chart.background.color']);
  799. this.original_colors['chart.red.color'] = RG.array_clone(prop['chart.red.color']);
  800. this.original_colors['chart.yellow.color'] = RG.array_clone(prop['chart.yellow.color']);
  801. this.original_colors['chart.green.color'] = RG.array_clone(prop['chart.green.color']);
  802. this.original_colors['chart.border.inner'] = RG.array_clone(prop['chart.border.inner']);
  803. this.original_colors['chart.border.outer'] = RG.array_clone(prop['chart.border.outer']);
  804. this.original_colors['chart.colors.ranges'] = RG.array_clone(prop['chart.colors.ranges']);
  805. this.original_colors['chart.needle.colors'] = RG.array_clone(prop['chart.needle.colors']);
  806. }
  807. prop['chart.background.color'] = this.parseSingleColorForGradient(prop['chart.background.color']);
  808. prop['chart.red.color'] = this.parseSingleColorForGradient(prop['chart.red.color']);
  809. prop['chart.yellow.color'] = this.parseSingleColorForGradient(prop['chart.yellow.color']);
  810. prop['chart.green.color'] = this.parseSingleColorForGradient(prop['chart.green.color']);
  811. prop['chart.border.inner'] = this.parseSingleColorForGradient(prop['chart.border.inner']);
  812. prop['chart.border.outer'] = this.parseSingleColorForGradient(prop['chart.border.outer']);
  813. // Parse the chart.color.ranges value
  814. if (prop['chart.colors.ranges']) {
  815. var ranges = prop['chart.colors.ranges'];
  816. for (var i=0; i<ranges.length; ++i) {
  817. ranges[i][2] = this.parseSingleColorForGradient(ranges[i][2], this.radius - 30);
  818. }
  819. }
  820. // Parse the chart.needle.colors value
  821. if (prop['chart.needle.colors']) {
  822. var colors = prop['chart.needle.colors'];
  823. for (var i=0; i<colors.length; ++i) {
  824. colors[i] = this.parseSingleColorForGradient(colors[i]);
  825. }
  826. }
  827. };
  828. /**
  829. * This parses a single color value
  830. *
  831. * @param string color The color to look for a gradient in
  832. * @param radius OPTIONAL The start radius to start the gradient at.
  833. * If not suppllied the centerx value is used
  834. */
  835. this.parseSingleColorForGradient = function (color)
  836. {
  837. var radiusStart = arguments[1] || 0;
  838. if (!color || typeof(color) != 'string') {
  839. return color;
  840. }
  841. if (color.match(/^gradient\((.*)\)$/i)) {
  842. var parts = RegExp.$1.split(':');
  843. // Create the gradient
  844. var grad = co.createRadialGradient(this.centerx,
  845. this.centery,
  846. radiusStart,
  847. this.centerx,
  848. this.centery,
  849. this.radius
  850. );
  851. var diff = 1 / (parts.length - 1);
  852. grad.addColorStop(0, RG.trim(parts[0]));
  853. for (var j=1; j<parts.length; ++j) {
  854. grad.addColorStop(j * diff, RG.trim(parts[j]));
  855. }
  856. }
  857. return grad ? grad : color;
  858. };
  859. /**
  860. * Using a function to add events makes it easier to facilitate method chaining
  861. *
  862. * @param string type The type of even to add
  863. * @param function func
  864. */
  865. this.on = function (type, func)
  866. {
  867. if (type.substr(0,2) !== 'on') {
  868. type = 'on' + type;
  869. }
  870. this[type] = func;
  871. return this;
  872. };
  873. /**
  874. * This function runs once only
  875. * (put at the end of the file (before any effects))
  876. */
  877. this.firstDrawFunc = function ()
  878. {
  879. };
  880. /**
  881. * Gauge Grow
  882. *
  883. * This effect gradually increases the represented value
  884. *
  885. * @param object Options for the effect. You can pass frames here
  886. * @param function An optional callback function
  887. */
  888. this.grow = function ()
  889. {
  890. var obj = this;
  891. var opt = arguments[0] ? arguments[0] : {};
  892. var callback = arguments[1] ? arguments[1] : function () {};
  893. var frames = opt.frames || 30;
  894. var frame = 0;
  895. // Single pointer
  896. if (typeof obj.value === 'number') {
  897. var origValue = Number(obj.currentValue);
  898. if (obj.currentValue == null) {
  899. obj.currentValue = obj.min;
  900. origValue = obj.min;
  901. }
  902. var newValue = obj.value;
  903. var diff = newValue - origValue;
  904. var iterator = function ()
  905. {
  906. obj.value = ((frame / frames) * diff) + origValue;
  907. if (obj.value > obj.max) obj.value = obj.max;
  908. if (obj.value < obj.min) obj.value = obj.min;
  909. //RGraph.clear(obj.canvas);
  910. RG.redrawCanvas(obj.canvas);
  911. if (frame++ < frames) {
  912. RG.Effects.updateCanvas(iterator);
  913. } else {
  914. callback(obj);
  915. }
  916. };
  917. iterator();
  918. /**
  919. * Multiple pointers
  920. */
  921. } else {
  922. if (obj.currentValue == null) {
  923. obj.currentValue = [];
  924. for (var i=0; i<obj.value.length; ++i) {
  925. obj.currentValue[i] = obj.min;
  926. }
  927. origValue = RG.array_clone(obj.currentValue);
  928. }
  929. var origValue = RG.array_clone(obj.currentValue);
  930. var newValue = RG.array_clone(obj.value);
  931. var diff = [];
  932. for (var i=0,len=newValue.length; i<len; ++i) {
  933. diff[i] = newValue[i] - Number(obj.currentValue[i]);
  934. }
  935. var iterator = function ()
  936. {
  937. frame++;
  938. for (var i=0,len=obj.value.length; i<len; ++i) {
  939. obj.value[i] = ((frame / frames) * diff[i]) + origValue[i];
  940. if (obj.value[i] > obj.max) obj.value[i] = obj.max;
  941. if (obj.value[i] < obj.min) obj.value[i] = obj.min;
  942. }
  943. //RG.clear(obj.canvas);
  944. RG.redrawCanvas(obj.canvas);
  945. if (frame < frames) {
  946. RG.Effects.updateCanvas(iterator);
  947. } else {
  948. callback(obj);
  949. }
  950. };
  951. iterator();
  952. }
  953. return this;
  954. };
  955. /**
  956. * Register the object
  957. */
  958. RG.Register(this);
  959. };