RGraph.gauge.js 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071
  1. /**
  2. * o------------------------------------------------------------------------------o
  3. * | This file is part of the RGraph package - you can learn more at: |
  4. * | |
  5. * | http://www.rgraph.net |
  6. * | |
  7. * | This package is licensed under the RGraph license. For all kinds of business |
  8. * | purposes there is a small one-time licensing fee to pay and for non |
  9. * | commercial purposes it is free to use. You can read the full license here: |
  10. * | |
  11. * | http://www.rgraph.net/license |
  12. * o------------------------------------------------------------------------------o
  13. */
  14. if (typeof(RGraph) == 'undefined') RGraph = {};
  15. /**
  16. * The line chart constructor
  17. *
  18. * @param object canvas The cxanvas object
  19. * @param array data The chart data
  20. * @param array ... Other lines to plot
  21. */
  22. RGraph.Gauge = function (id, min, max, value)
  23. {
  24. // Get the canvas and context objects
  25. this.id = id;
  26. this.canvas = document.getElementById(typeof id === 'object' ? id.id : id);
  27. this.context = this.canvas.getContext ? this.canvas.getContext("2d") : null;
  28. this.canvas.__object__ = this;
  29. this.type = 'gauge';
  30. this.min = min;
  31. this.max = max;
  32. this.value = value;
  33. this.isRGraph = true;
  34. this.currentValue = null;
  35. this.uid = RGraph.CreateUID();
  36. this.canvas.uid = this.canvas.uid ? this.canvas.uid : RGraph.CreateUID();
  37. this.colorsParsed = false;
  38. this.coordsText = [];
  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. 'chart.angles.start': null,
  58. 'chart.angles.end': null,
  59. 'chart.centerx': null,
  60. 'chart.centery': null,
  61. 'chart.radius': null,
  62. 'chart.gutter.left': 15,
  63. 'chart.gutter.right': 15,
  64. 'chart.gutter.top': 15,
  65. 'chart.gutter.bottom': 15,
  66. 'chart.border.width': 10,
  67. 'chart.title.top': '',
  68. 'chart.title.top.font':'Arial',
  69. 'chart.title.top.size':14,
  70. 'chart.title.top.color':'#333',
  71. 'chart.title.top.bold':false,
  72. 'chart.title.top.pos': null,
  73. 'chart.title.bottom': '',
  74. 'chart.title.bottom.font':'Arial',
  75. 'chart.title.bottom.size':14,
  76. 'chart.title.bottom.color':'#333',
  77. 'chart.title.bottom.bold':false,
  78. 'chart.title.bottom.pos':null,
  79. 'chart.text.font': 'Arial',
  80. 'chart.text.color': '#666',
  81. 'chart.text.size': 10,
  82. 'chart.background.color': 'white',
  83. 'chart.background.gradient': false,
  84. 'chart.scale.decimals': 0,
  85. 'chart.scale.point': '.',
  86. 'chart.scale.thousand': ',',
  87. 'chart.units.pre': '',
  88. 'chart.units.post': '',
  89. 'chart.value.text': false,
  90. 'chart.value.text.y.pos': 0.5,
  91. 'chart.value.text.units.pre': null,
  92. 'chart.value.text.units.post': null,
  93. 'chart.red.start': 0.9 * this.max,
  94. 'chart.red.color': '#DC3912',
  95. 'chart.yellow.color': '#FF9900',
  96. 'chart.green.end': 0.7 * this.max,
  97. 'chart.green.color': 'rgba(0,0,0,0)',
  98. 'chart.colors.ranges': null,
  99. 'chart.needle.size': null,
  100. 'chart.needle.tail': false,
  101. 'chart.needle.colors': ['#D5604D', 'red', 'green', 'yellow'],
  102. 'chart.needle.type': 'triangle',
  103. 'chart.border.outer': '#ccc',
  104. 'chart.border.inner': '#f1f1f1',
  105. 'chart.border.outline': 'black',
  106. 'chart.centerpin.color': 'blue',
  107. 'chart.centerpin.radius': null,
  108. 'chart.zoom.background': true,
  109. 'chart.zoom.action': 'zoom',
  110. 'chart.tickmarks.small': 25,
  111. 'chart.tickmarks.small.color': 'black',
  112. 'chart.tickmarks.medium': 0,
  113. 'chart.tickmarks.medium.color': 'black',
  114. 'chart.tickmarks.big': 5,
  115. 'chart.tickmarks.big.color': 'black',
  116. 'chart.labels.count': 5,
  117. 'chart.labels.centered': false,
  118. 'chart.labels.offset': 0,
  119. 'chart.border.gradient': false,
  120. 'chart.adjustable': false,
  121. 'chart.shadow': true,
  122. 'chart.shadow.color': 'gray',
  123. 'chart.shadow.offsetx': 0,
  124. 'chart.shadow.offsety': 0,
  125. 'chart.shadow.blur': 15
  126. }
  127. /*
  128. * Translate half a pixel for antialiasing purposes - but only if it hasn't beeen
  129. * done already
  130. */
  131. if (!this.canvas.__rgraph_aa_translated__) {
  132. this.context.translate(0.5,0.5);
  133. this.canvas.__rgraph_aa_translated__ = true;
  134. }
  135. ///////////////////////////////// SHORT PROPERTIES /////////////////////////////////
  136. var RG = RGraph;
  137. var ca = this.canvas;
  138. var co = ca.getContext('2d');
  139. var prop = this.properties;
  140. //////////////////////////////////// METHODS ///////////////////////////////////////
  141. /**
  142. * An all encompassing accessor
  143. *
  144. * @param string name The name of the property
  145. * @param mixed value The value of the property
  146. */
  147. this.Set = function (name, value)
  148. {
  149. name = name.toLowerCase();
  150. /**
  151. * This should be done first - prepend the propertyy name with "chart." if necessary
  152. */
  153. if (name.substr(0,6) != 'chart.') {
  154. name = 'chart.' + name;
  155. }
  156. /**
  157. * Title compatibility
  158. */
  159. if (name == 'chart.title') name = 'chart.title.top';
  160. if (name == 'chart.title.font') name = 'chart.title.top.font';
  161. if (name == 'chart.title.size') name = 'chart.title.top.size';
  162. if (name == 'chart.title.color') name = 'chart.title.top.color';
  163. if (name == 'chart.title.bold') name = 'chart.title.top.bold';
  164. // BC
  165. if (name == 'chart.needle.color') {
  166. name = 'chart.needle.colors';
  167. }
  168. prop[name] = value;
  169. return this;
  170. }
  171. /**
  172. * An all encompassing accessor
  173. *
  174. * @param string name The name of the property
  175. */
  176. this.Get = function (name)
  177. {
  178. /**
  179. * This should be done first - prepend the property name with "chart." if necessary
  180. */
  181. if (name.substr(0,6) != 'chart.') {
  182. name = 'chart.' + name;
  183. }
  184. // BC
  185. if (name == 'chart.needle.color') {
  186. name = 'chart.needle.colors';
  187. }
  188. return prop[name];
  189. }
  190. /**
  191. * The function you call to draw the line chart
  192. *
  193. * @param bool An optional bool used internally to ditinguish whether the
  194. * line chart is being called by the bar chart
  195. */
  196. this.Draw = function ()
  197. {
  198. /**
  199. * Fire the onbeforedraw event
  200. */
  201. RG.FireCustomEvent(this, 'onbeforedraw');
  202. /**
  203. * Store the value (for animation primarily
  204. */
  205. this.currentValue = this.value;
  206. /**
  207. * This is new in May 2011 and facilitates indiviual gutter settings,
  208. * eg chart.gutter.left
  209. */
  210. this.gutterLeft = prop['chart.gutter.left'];
  211. this.gutterRight = prop['chart.gutter.right'];
  212. this.gutterTop = prop['chart.gutter.top'];
  213. this.gutterBottom = prop['chart.gutter.bottom'];
  214. this.centerx = ((ca.width - this.gutterLeft - this.gutterRight) / 2) + this.gutterLeft;
  215. this.centery = ((ca.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop;
  216. this.radius = Math.min(
  217. ((ca.width - this.gutterLeft - this.gutterRight) / 2),
  218. ((ca.height - this.gutterTop - this.gutterBottom) / 2)
  219. );
  220. this.startAngle = prop['chart.angles.start'] ? prop['chart.angles.start'] : (HALFPI / 3) + HALFPI;
  221. this.endAngle = prop['chart.angles.end'] ? prop['chart.angles.end'] : TWOPI + HALFPI - (HALFPI / 3);
  222. /**
  223. * You can now override the positioning and radius if you so wish.
  224. */
  225. if (typeof(prop['chart.centerx']) == 'number') this.centerx = prop['chart.centerx'];
  226. if (typeof(prop['chart.centery']) == 'number') this.centery = prop['chart.centery'];
  227. if (typeof(prop['chart.radius']) == 'number') this.radius = prop['chart.radius'];
  228. /**
  229. * Parse the colors. This allows for simple gradient syntax
  230. */
  231. if (!this.colorsParsed) {
  232. this.parseColors();
  233. // Don't want to do this again
  234. this.colorsParsed = true;
  235. }
  236. // This has to be in the constructor
  237. this.centerpinRadius = 0.16 * this.radius;
  238. if (typeof(prop['chart.centerpin.radius']) == 'number') {
  239. this.centerpinRadius = prop['chart.centerpin.radius'];
  240. }
  241. /**
  242. * Setup the context menu if required
  243. */
  244. if (prop['chart.contextmenu']) {
  245. RG.ShowContext(this);
  246. }
  247. // DRAW THE CHART HERE
  248. this.DrawBackGround();
  249. this.DrawGradient();
  250. this.DrawColorBands();
  251. this.DrawSmallTickmarks();
  252. this.DrawMediumTickmarks();
  253. this.DrawBigTickmarks();
  254. this.DrawLabels();
  255. this.DrawTopTitle();
  256. this.DrawBottomTitle();
  257. if (typeof(this.value) == 'object') {
  258. for (var i=0; i<this.value.length; ++i) {
  259. this.DrawNeedle(this.value[i], prop['chart.needle.colors'][i], i);
  260. }
  261. } else {
  262. this.DrawNeedle(this.value, prop['chart.needle.colors'][0], 0);
  263. }
  264. this.DrawCenterpin();
  265. /**
  266. * This function enables resizing
  267. */
  268. if (prop['chart.resizable']) {
  269. RG.AllowResizing(this);
  270. }
  271. /**
  272. * This installs the event listeners
  273. */
  274. RG.InstallEventListeners(this);
  275. /**
  276. * Fire the RGraph ondraw event
  277. */
  278. RG.FireCustomEvent(this, 'ondraw');
  279. return this;
  280. }
  281. /**
  282. * Draw the background
  283. */
  284. this.DrawBackGround = function ()
  285. {
  286. // Shadow //////////////////////////////////////////////
  287. if (prop['chart.shadow']) {
  288. RG.SetShadow(this, prop['chart.shadow.color'], prop['chart.shadow.offsetx'], prop['chart.shadow.offsety'], prop['chart.shadow.blur']);
  289. }
  290. co.beginPath();
  291. co.fillStyle = prop['chart.background.color'];
  292. //co.moveTo(this.centerx, this.centery)
  293. co.arc(this.centerx, this.centery, this.radius, 0, TWOPI, 0);
  294. co.fill();
  295. // Turn off the shadow
  296. RG.NoShadow(this);
  297. // Shadow //////////////////////////////////////////////
  298. var grad = co.createRadialGradient(this.centerx + 50, this.centery - 50, 0, this.centerx + 50, this.centery - 50, 150);
  299. grad.addColorStop(0, '#eee');
  300. grad.addColorStop(1, 'white');
  301. var borderWidth = prop['chart.border.width'];
  302. co.beginPath();
  303. co.fillStyle = prop['chart.background.color'];
  304. co.arc(this.centerx, this.centery, this.radius, 0, TWOPI, 0);
  305. co.fill();
  306. /**
  307. * Draw the gray circle
  308. */
  309. co.beginPath();
  310. co.fillStyle = prop['chart.border.outer'];
  311. co.arc(this.centerx, this.centery, this.radius, 0, TWOPI, 0);
  312. co.fill();
  313. /**
  314. * Draw the light gray inner border
  315. */
  316. co.beginPath();
  317. co.fillStyle = prop['chart.border.inner'];
  318. co.arc(this.centerx, this.centery, this.radius - borderWidth, 0, TWOPI, 0);
  319. co.fill();
  320. // Draw the white circle inner border
  321. co.beginPath();
  322. co.fillStyle = prop['chart.background.color'];
  323. co.arc(this.centerx, this.centery, this.radius - borderWidth - 4, 0, TWOPI, 0);
  324. co.fill();
  325. // Draw the circle background. Can be any colour now.
  326. co.beginPath();
  327. co.fillStyle = prop['chart.background.color'];
  328. co.arc(this.centerx, this.centery, this.radius - borderWidth - 4, 0, TWOPI, 0);
  329. co.fill();
  330. if (prop['chart.background.gradient']) {
  331. // Draw a partially transparent gradient that sits on top of the background
  332. co.beginPath();
  333. co.fillStyle = RG.RadialGradient(this,
  334. this.centerx,
  335. this.centery,
  336. 0,
  337. this.centerx,
  338. this.centery,
  339. this.radius,
  340. 'rgba(255,255,255,0.6)',
  341. 'rgba(255,255,255,0.1)');
  342. co.arc(this.centerx, this.centery, this.radius - borderWidth - 4, 0, TWOPI, 0);
  343. co.fill();
  344. }
  345. // Draw a black border around the chart
  346. co.beginPath();
  347. co.strokeStyle = prop['chart.border.outline'];
  348. co.arc(this.centerx, this.centery, this.radius, 0, TWOPI, 0);
  349. co.stroke();
  350. }
  351. /**
  352. * This function draws the smaller tickmarks
  353. */
  354. this.DrawSmallTickmarks = function ()
  355. {
  356. var numTicks = prop['chart.tickmarks.small'];
  357. co.lineWidth = 1;
  358. for (var i=0; i<=numTicks; ++i) {
  359. co.beginPath();
  360. co.strokeStyle = prop['chart.tickmarks.small.color'];
  361. var a = (((this.endAngle - this.startAngle) / numTicks) * i) + this.startAngle;
  362. co.arc(this.centerx, this.centery, this.radius - prop['chart.border.width'] - 10, a, a + 0.00001, 0);
  363. co.arc(this.centerx, this.centery, this.radius - prop['chart.border.width'] - 10 - 5, a, a + 0.00001, 0);
  364. co.stroke();
  365. }
  366. }
  367. /**
  368. * This function draws the medium sized tickmarks
  369. */
  370. this.DrawMediumTickmarks = function ()
  371. {
  372. if (prop['chart.tickmarks.medium']) {
  373. var numTicks = prop['chart.tickmarks.medium'];
  374. co.lineWidth = 3;
  375. co.lineCap = 'round';
  376. co.strokeStyle = prop['chart.tickmarks.medium.color'];
  377. for (var i=0; i<=numTicks; ++i) {
  378. co.beginPath();
  379. var a = (((this.endAngle - this.startAngle) / numTicks) * i) + this.startAngle + (((this.endAngle - this.startAngle) / (2 * numTicks)));
  380. if (a > this.startAngle && a< this.endAngle) {
  381. co.arc(this.centerx, this.centery, this.radius - prop['chart.border.width'] - 10, a, a + 0.00001, 0);
  382. co.arc(this.centerx, this.centery, this.radius - prop['chart.border.width'] - 10 - 6, a, a + 0.00001, 0);
  383. }
  384. co.stroke();
  385. }
  386. }
  387. }
  388. /**
  389. * This function draws the large, bold tickmarks
  390. */
  391. this.DrawBigTickmarks = function ()
  392. {
  393. var numTicks = prop['chart.tickmarks.big'];
  394. co.lineWidth = 3;
  395. co.lineCap = 'round';
  396. for (var i=0; i<=numTicks; ++i) {
  397. co.beginPath();
  398. co.strokeStyle = prop['chart.tickmarks.big.color'];
  399. var a = (((this.endAngle - this.startAngle) / numTicks) * i) + this.startAngle;
  400. co.arc(this.centerx, this.centery, this.radius - prop['chart.border.width'] - 10, a, a + 0.00001, 0);
  401. co.arc(this.centerx, this.centery, this.radius - prop['chart.border.width'] - 10 - 10, a, a + 0.00001, 0);
  402. co.stroke();
  403. }
  404. }
  405. /**
  406. * This function draws the centerpin
  407. */
  408. this.DrawCenterpin = function ()
  409. {
  410. var offset = 6;
  411. var grad = co.createRadialGradient(this.centerx + offset, this.centery - offset, 0, this.centerx + offset, this.centery - offset, 25);
  412. grad.addColorStop(0, '#ddf');
  413. grad.addColorStop(1, prop['chart.centerpin.color']);
  414. co.beginPath();
  415. co.fillStyle = grad;
  416. co.arc(this.centerx, this.centery, this.centerpinRadius, 0, TWOPI, 0);
  417. co.fill();
  418. }
  419. /**
  420. * This function draws the labels
  421. */
  422. this.DrawLabels = function ()
  423. {
  424. co.fillStyle = prop['chart.text.color'];
  425. var font = prop['chart.text.font'];
  426. var size = prop['chart.text.size'];
  427. var num = prop['chart.labels.specific'] ? (prop['chart.labels.specific'].length - 1) : prop['chart.labels.count'];
  428. co.beginPath();
  429. for (var i=0; i<=num; ++i) {
  430. var hyp = (this.radius - 25 - prop['chart.border.width']) - prop['chart.labels.offset'];
  431. var a = (this.endAngle - this.startAngle) / num
  432. a = this.startAngle + (i * a);
  433. a -= HALFPI;
  434. var x = this.centerx - (Math.sin(a) * hyp);
  435. var y = this.centery + (Math.cos(a) * hyp);
  436. var hAlign = x > this.centerx ? 'right' : 'left';
  437. var vAlign = y > this.centery ? 'bottom' : 'top';
  438. // This handles the label alignment when the label is on a PI/HALFPI boundary
  439. if (a == HALFPI) {
  440. vAlign = 'center';
  441. } else if (a == PI) {
  442. hAlign = 'center';
  443. } else if (a == (HALFPI + PI) ) {
  444. vAlign = 'center';
  445. }
  446. /**
  447. * Can now force center alignment
  448. */
  449. if (prop['chart.labels.centered']) {
  450. hAlign = 'center';
  451. vAlign = 'center';
  452. }
  453. RG.Text2(this, {'font':font,
  454. 'size':size,
  455. 'x':x,
  456. 'y':y,
  457. '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']),
  458. 'halign':hAlign,
  459. 'valign':vAlign,
  460. 'tag': prop['chart.labels.specific'] ? 'labels.specific' : 'labels'
  461. });
  462. }
  463. co.fill();
  464. /**
  465. * Draw the textual value if requested
  466. */
  467. if (prop['chart.value.text']) {
  468. var x = this.centerx;
  469. var y = this.centery + (prop['chart.value.text.y.pos'] * this.radius);
  470. var units_pre = typeof(prop['chart.value.text.units.pre']) == 'string' ? prop['chart.value.text.units.pre'] : prop['chart.units.pre'];
  471. var units_post = typeof(prop['chart.value.text.units.post']) == 'string' ? prop['chart.value.text.units.post'] : prop['chart.units.post'];
  472. RG.Text2(this, {'font':font,
  473. 'size':size + 2,
  474. 'x':x,
  475. 'y':y,
  476. 'text':RG.number_format(this, this.value.toFixed(prop['chart.scale.decimals']), units_pre, units_post),
  477. 'halign':'center',
  478. 'valign':'center',
  479. 'bounding':true,
  480. 'boundingFill':'white',
  481. 'tag': 'value.text'
  482. });
  483. }
  484. }
  485. /**
  486. * This function draws the top title
  487. */
  488. this.DrawTopTitle = function ()
  489. {
  490. var x = this.centerx;
  491. var y = this.centery - 25;
  492. // Totally override the calculated positioning
  493. if (typeof(prop['chart.title.top.pos']) == 'number') {
  494. y = this.centery - (this.radius * prop['chart.title.top.pos']);
  495. }
  496. if (prop['chart.title.top']) {
  497. co.fillStyle = prop['chart.title.top.color'];
  498. RG.Text2(this, {'font':prop['chart.title.top.font'],
  499. 'size':prop['chart.title.top.size'],
  500. 'x':x,
  501. 'y':y,
  502. 'text':String(prop['chart.title.top']),
  503. 'halign':'center',
  504. 'valign':'bottom',
  505. 'bold':prop['chart.title.top.bold'],
  506. 'tag': 'title.top'
  507. });
  508. }
  509. }
  510. /**
  511. * This function draws the bottom title
  512. */
  513. this.DrawBottomTitle = function ()
  514. {
  515. var x = this.centerx;
  516. var y = this.centery + this.centerpinRadius + 10;
  517. // Totally override the calculated positioning
  518. if (typeof(prop['chart.title.bottom.pos']) == 'number') {
  519. y = this.centery + (this.radius * prop['chart.title.bottom.pos']);
  520. }
  521. if (prop['chart.title.bottom']) {
  522. co.fillStyle = prop['chart.title.bottom.color'];
  523. RG.Text2(this, {'font':prop['chart.title.bottom.font'],
  524. 'size':prop['chart.title.bottom.size'],
  525. 'x':x,
  526. 'y':y,
  527. 'text':String(prop['chart.title.bottom']),
  528. 'halign':'center',
  529. 'valign':'top',
  530. 'bold':prop['chart.title.bottom.bold'],
  531. 'tag': 'title.bottom'
  532. });
  533. }
  534. }
  535. /**
  536. * This function draws the Needle
  537. *
  538. * @param number value The value to draw the needle for
  539. */
  540. this.DrawNeedle = function (value, color, index)
  541. {
  542. var type = prop['chart.needle.type'];
  543. co.lineWidth = 0.5;
  544. co.strokeStyle = 'gray';
  545. co.fillStyle = color;
  546. var angle = (this.endAngle - this.startAngle) * ((value - this.min) / (this.max - this.min));
  547. angle += this.startAngle;
  548. // Work out the size of the needle
  549. if ( typeof(prop['chart.needle.size']) == 'object'
  550. && prop['chart.needle.size']
  551. && typeof(prop['chart.needle.size'][index]) == 'number') {
  552. var size = prop['chart.needle.size'][index];
  553. } else if (typeof(prop['chart.needle.size']) == 'number') {
  554. var size = prop['chart.needle.size'];
  555. } else {
  556. var size = this.radius - 25 - prop['chart.border.width'];
  557. }
  558. if (type == 'line') {
  559. co.beginPath();
  560. co.lineWidth = 7;
  561. co.strokeStyle = color;
  562. co.arc(this.centerx,
  563. this.centery,
  564. size,
  565. angle,
  566. angle + 0.0001,
  567. false);
  568. co.lineTo(this.centerx, this.centery);
  569. if (prop['chart.needle.tail']) {
  570. co.arc(this.centerx, this.centery, this.radius * 0.2 , angle + PI, angle + 0.00001 + PI, false);
  571. }
  572. co.lineTo(this.centerx, this.centery);
  573. co.stroke();
  574. //co.fill();
  575. } else {
  576. co.beginPath();
  577. co.arc(this.centerx, this.centery, size, angle, angle + 0.00001, false);
  578. co.arc(this.centerx, this.centery, this.centerpinRadius * 0.5, angle + HALFPI, angle + 0.00001 + HALFPI, false);
  579. if (prop['chart.needle.tail']) {
  580. co.arc(this.centerx, this.centery, this.radius * 0.2 , angle + PI, angle + 0.00001 + PI, false);
  581. }
  582. co.arc(this.centerx, this.centery, this.centerpinRadius * 0.5, angle - HALFPI, angle - 0.00001 - HALFPI, false);
  583. co.stroke();
  584. co.fill();
  585. /**
  586. * Store the angle in an object variable
  587. */
  588. this.angle = angle;
  589. }
  590. }
  591. /**
  592. * This draws the green background to the tickmarks
  593. */
  594. this.DrawColorBands = function ()
  595. {
  596. if (RG.is_array(prop['chart.colors.ranges'])) {
  597. var ranges = prop['chart.colors.ranges'];
  598. for (var i=0; i<ranges.length; ++i) {
  599. //co.strokeStyle = prop['chart.strokestyle'] ? prop['chart.strokestyle'] : ranges[i][2];
  600. co.fillStyle = ranges[i][2];
  601. co.lineWidth = 0;//prop['chart.linewidth.segments'];
  602. co.beginPath();
  603. co.arc(this.centerx,
  604. this.centery,
  605. this.radius - 10 - prop['chart.border.width'],
  606. (((ranges[i][0] - this.min) / (this.max - this.min)) * (this.endAngle - this.startAngle)) + this.startAngle,
  607. (((ranges[i][1] - this.min) / (this.max - this.min)) * (this.endAngle - this.startAngle)) + this.startAngle,
  608. false);
  609. co.arc(this.centerx,
  610. this.centery,
  611. this.radius - 20 - prop['chart.border.width'],
  612. (((ranges[i][1] - this.min) / (this.max - this.min)) * (this.endAngle - this.startAngle)) + this.startAngle,
  613. (((ranges[i][0] - this.min) / (this.max - this.min)) * (this.endAngle - this.startAngle)) + this.startAngle,
  614. true);
  615. co.closePath();
  616. co.fill();
  617. }
  618. return;
  619. }
  620. /**
  621. * Draw the GREEN region
  622. */
  623. co.strokeStyle = prop['chart.green.color'];
  624. co.fillStyle = prop['chart.green.color'];
  625. var greenStart = this.startAngle;
  626. var greenEnd = this.startAngle + (this.endAngle - this.startAngle) * ((prop['chart.green.end'] - this.min) / (this.max - this.min))
  627. co.beginPath();
  628. co.arc(this.centerx, this.centery, this.radius - 10 - prop['chart.border.width'], greenStart, greenEnd, false);
  629. co.arc(this.centerx, this.centery, this.radius - 20 - prop['chart.border.width'], greenEnd, greenStart, true);
  630. co.fill();
  631. /**
  632. * Draw the YELLOW region
  633. */
  634. co.strokeStyle = prop['chart.yellow.color'];
  635. co.fillStyle = prop['chart.yellow.color'];
  636. var yellowStart = greenEnd;
  637. var yellowEnd = this.startAngle + (this.endAngle - this.startAngle) * ((prop['chart.red.start'] - this.min) / (this.max - this.min))
  638. co.beginPath();
  639. co.arc(this.centerx, this.centery, this.radius - 10 - prop['chart.border.width'], yellowStart, yellowEnd, false);
  640. co.arc(this.centerx, this.centery, this.radius - 20 - prop['chart.border.width'], yellowEnd, yellowStart, true);
  641. co.fill();
  642. /**
  643. * Draw the RED region
  644. */
  645. co.strokeStyle = prop['chart.red.color'];
  646. co.fillStyle = prop['chart.red.color'];
  647. var redStart = yellowEnd;
  648. var redEnd = this.startAngle + (this.endAngle - this.startAngle) * ((this.max - this.min) / (this.max - this.min))
  649. co.beginPath();
  650. co.arc(this.centerx, this.centery, this.radius - 10 - prop['chart.border.width'], redStart, redEnd, false);
  651. co.arc(this.centerx, this.centery, this.radius - 20 - prop['chart.border.width'], redEnd, redStart, true);
  652. co.fill();
  653. }
  654. /**
  655. * A placeholder function
  656. *
  657. * @param object The event object
  658. */
  659. this.getShape = function (e)
  660. {
  661. }
  662. /**
  663. * A getValue method
  664. *
  665. * @param object e An event object
  666. */
  667. this.getValue = function (e)
  668. {
  669. var mouseXY = RG.getMouseXY(e);
  670. var mouseX = mouseXY[0];
  671. var mouseY = mouseXY[1];
  672. var angle = RG.getAngleByXY(this.centerx, this.centery, mouseX, mouseY);
  673. if (angle >= 0 && angle <= HALFPI) {
  674. angle += TWOPI;
  675. }
  676. var value = ((angle - this.startAngle) / (this.endAngle - this.startAngle)) * (this.max - this.min);
  677. value = value + this.min;
  678. if (value < this.min) {
  679. value = this.min
  680. }
  681. if (value > this.max) {
  682. value = this.max
  683. }
  684. return value;
  685. }
  686. /**
  687. * The getObjectByXY() worker method. Don't call this call:
  688. *
  689. * RGraph.ObjectRegistry.getObjectByXY(e)
  690. *
  691. * @param object e The event object
  692. */
  693. this.getObjectByXY = function (e)
  694. {
  695. var mouseXY = RGraph.getMouseXY(e);
  696. if (
  697. mouseXY[0] > (this.centerx - this.radius)
  698. && mouseXY[0] < (this.centerx + this.radius)
  699. && mouseXY[1] > (this.centery - this.radius)
  700. && mouseXY[1] < (this.centery + this.radius)
  701. && RG.getHypLength(this.centerx, this.centery, mouseXY[0], mouseXY[1]) <= this.radius
  702. ) {
  703. return this;
  704. }
  705. }
  706. /**
  707. * This draws the gradient that goes around the Gauge chart
  708. */
  709. this.DrawGradient = function ()
  710. {
  711. if (prop['chart.border.gradient']) {
  712. co.beginPath();
  713. var grad = co.createRadialGradient(this.centerx, this.centery, this.radius, this.centerx, this.centery, this.radius - 15);
  714. grad.addColorStop(0, 'gray');
  715. grad.addColorStop(1, 'white');
  716. co.fillStyle = grad;
  717. co.arc(this.centerx, this.centery, this.radius, 0, TWOPI, false)
  718. co.arc(this.centerx, this.centery, this.radius - 15, TWOPI,0, true)
  719. co.fill();
  720. }
  721. }
  722. /**
  723. * This method handles the adjusting calculation for when the mouse is moved
  724. *
  725. * @param object e The event object
  726. */
  727. this.Adjusting_mousemove = function (e)
  728. {
  729. /**
  730. * Handle adjusting for the Bar
  731. */
  732. if (prop['chart.adjustable'] && RG.Registry.Get('chart.adjusting') && RG.Registry.Get('chart.adjusting').uid == this.uid) {
  733. this.value = this.getValue(e);
  734. RG.Clear(this.canvas);
  735. RG.RedrawCanvas(this.canvas);
  736. RG.FireCustomEvent(this, 'onadjust');
  737. }
  738. }
  739. /**
  740. * This method returns an appropriate angle for the given value (in RADIANS)
  741. *
  742. * @param number value The value to get the angle for
  743. */
  744. this.getAngle = function (value)
  745. {
  746. // Higher than max
  747. if (value > this.max || value < this.min) {
  748. return null;
  749. }
  750. //var value = ((angle - this.startAngle) / (this.endAngle - this.startAngle)) * (this.max - this.min);
  751. //value = value + this.min;
  752. var angle = (((value - this.min) / (this.max - this.min)) * (this.endAngle - this.startAngle)) + this.startAngle;
  753. return angle;
  754. }
  755. /**
  756. * This allows for easy specification of gradients. Could optimise this not to repeatedly call parseSingleColors()
  757. */
  758. this.parseColors = function ()
  759. {
  760. prop['chart.background.color'] = this.parseSingleColorForGradient(prop['chart.background.color']);
  761. prop['chart.red.color'] = this.parseSingleColorForGradient(prop['chart.red.color']);
  762. prop['chart.yellow.color'] = this.parseSingleColorForGradient(prop['chart.yellow.color']);
  763. prop['chart.green.color'] = this.parseSingleColorForGradient(prop['chart.green.color']);
  764. prop['chart.border.inner'] = this.parseSingleColorForGradient(prop['chart.border.inner']);
  765. prop['chart.border.outer'] = this.parseSingleColorForGradient(prop['chart.border.outer']);
  766. // Parse the chart.color.ranges value
  767. if (prop['chart.colors.ranges']) {
  768. var ranges = prop['chart.colors.ranges'];
  769. for (var i=0; i<ranges.length; ++i) {
  770. ranges[i][2] = this.parseSingleColorForGradient(ranges[i][2]);
  771. }
  772. }
  773. // Parse the chart.needle.colors value
  774. if (prop['chart.needle.colors']) {
  775. var colors = prop['chart.needle.colors'];
  776. for (var i=0; i<colors.length; ++i) {
  777. colors[i] = this.parseSingleColorForGradient(colors[i]);
  778. }
  779. }
  780. }
  781. /**
  782. * This parses a single color value
  783. */
  784. this.parseSingleColorForGradient = function (color)
  785. {
  786. if (!color || typeof(color) != 'string') {
  787. return color;
  788. }
  789. if (color.match(/^gradient\((.*)\)$/i)) {
  790. var parts = RegExp.$1.split(':');
  791. // Create the gradient
  792. var grad = co.createRadialGradient(this.centerx,this.centery,0,this.centerx,this.centery,this.radius);
  793. var diff = 1 / (parts.length - 1);
  794. grad.addColorStop(0, RG.trim(parts[0]));
  795. for (var j=1; j<parts.length; ++j) {
  796. grad.addColorStop(j * diff, RG.trim(parts[j]));
  797. }
  798. }
  799. return grad ? grad : color;
  800. }
  801. /**
  802. * Register the object
  803. */
  804. RG.Register(this);
  805. }