RGraph.odo.js 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049
  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 odometer constructor. Pass it the ID of the canvas tag, the start value of the odo,
  17. * the end value, and the value that the pointer should point to.
  18. *
  19. * @param string id The ID of the canvas tag
  20. * @param int start The start value of the Odo
  21. * @param int end The end value of the odo
  22. * @param int value The indicated value (what the needle points to)
  23. */
  24. RGraph.Odometer = function (id, start, end, value)
  25. {
  26. this.id = id
  27. this.canvas = document.getElementById(typeof id === 'object' ? id.id : id);
  28. this.context = this.canvas.getContext('2d');
  29. this.canvas.__object__ = this;
  30. this.type = 'odo';
  31. this.isRGraph = true;
  32. this.start = start;
  33. this.min = start;
  34. this.end = end;
  35. this.max = end;
  36. this.value = value;
  37. this.currentValue = null;
  38. this.uid = RGraph.CreateUID();
  39. this.canvas.uid = this.canvas.uid ? this.canvas.uid : RGraph.CreateUID();
  40. this.colorsParsed = false;
  41. this.coordsText = [];
  42. /**
  43. * Compatibility with older browsers
  44. */
  45. RGraph.OldBrowserCompat(this.context);
  46. this.properties = {
  47. 'chart.centerx': null,
  48. 'chart.centery': null,
  49. 'chart.radius': null,
  50. 'chart.value.text': false,
  51. 'chart.value.text.decimals': 0,
  52. 'chart.needle.color': 'black',
  53. 'chart.needle.width': 2,
  54. 'chart.needle.head': true,
  55. 'chart.needle.tail': true,
  56. 'chart.needle.type': 'pointer',
  57. 'chart.needle.extra': [],
  58. 'chart.needle.triangle.border': '#aaa',
  59. 'chart.text.size': 10,
  60. 'chart.text.color': 'black',
  61. 'chart.text.font': 'Arial',
  62. 'chart.green.max': end * 0.75,
  63. 'chart.red.min': end * 0.9,
  64. 'chart.green.color': 'Gradient(white:#0c0)',
  65. 'chart.yellow.color': 'Gradient(white:#ff0)',
  66. 'chart.red.color': 'Gradient(white:#f00)',
  67. 'chart.label.area': 35,
  68. 'chart.gutter.left': 25,
  69. 'chart.gutter.right': 25,
  70. 'chart.gutter.top': 25,
  71. 'chart.gutter.bottom': 25,
  72. 'chart.title': '',
  73. 'chart.title.background': null,
  74. 'chart.title.hpos': null,
  75. 'chart.title.vpos': null,
  76. 'chart.title.font': null,
  77. 'chart.title.bold': true,
  78. 'chart.title.x': null,
  79. 'chart.title.y': null,
  80. 'chart.title.halign': null,
  81. 'chart.title.valign': null,
  82. 'chart.contextmenu': null,
  83. 'chart.linewidth': 1,
  84. 'chart.shadow.inner': false,
  85. 'chart.shadow.inner.color': 'black',
  86. 'chart.shadow.inner.offsetx': 3,
  87. 'chart.shadow.inner.offsety': 3,
  88. 'chart.shadow.inner.blur': 6,
  89. 'chart.shadow.outer': false,
  90. 'chart.shadow.outer.color': '#666',
  91. 'chart.shadow.outer.offsetx': 0,
  92. 'chart.shadow.outer.offsety': 0,
  93. 'chart.shadow.outer.blur': 15,
  94. 'chart.annotatable': false,
  95. 'chart.annotate.color': 'black',
  96. 'chart.scale.decimals': 0,
  97. 'chart.scale.point': '.',
  98. 'chart.scale.thousand': ',',
  99. 'chart.zoom.factor': 1.5,
  100. 'chart.zoom.fade.in': true,
  101. 'chart.zoom.fade.out': true,
  102. 'chart.zoom.hdir': 'right',
  103. 'chart.zoom.vdir': 'down',
  104. 'chart.zoom.frames': 25,
  105. 'chart.zoom.delay': 16.666,
  106. 'chart.zoom.shadow': true,
  107. 'chart.zoom.background': true,
  108. 'chart.zoom.action': 'zoom',
  109. 'chart.resizable': false,
  110. 'chart.resize.handle.adjust': [0,0],
  111. 'chart.resize.handle.background': null,
  112. 'chart.units.pre': '',
  113. 'chart.units.post': '',
  114. 'chart.border': false,
  115. 'chart.border.color1': '#BEBCB0',
  116. 'chart.border.color2': '#F0EFEA',
  117. 'chart.border.color3': '#BEBCB0',
  118. 'chart.tickmarks': true,
  119. 'chart.tickmarks.highlighted': false,
  120. 'chart.zerostart': false,
  121. 'chart.labels': null,
  122. 'chart.units.pre': '',
  123. 'chart.units.post': '',
  124. 'chart.value.units.pre': '',
  125. 'chart.value.units.post': '',
  126. 'chart.key': null,
  127. 'chart.key.background': 'white',
  128. 'chart.key.position': 'graph',
  129. 'chart.key.shadow': false,
  130. 'chart.key.shadow.color': '#666',
  131. 'chart.key.shadow.blur': 3,
  132. 'chart.key.shadow.offsetx': 2,
  133. 'chart.key.shadow.offsety': 2,
  134. 'chart.key.position.gutter.boxed':false,
  135. 'chart.key.position.x': null,
  136. 'chart.key.position.y': null,
  137. 'chart.key.halign': 'right',
  138. 'chart.key.color.shape': 'square',
  139. 'chart.key.rounded': true,
  140. 'chart.key.text.size': 10,
  141. 'chart.key.colors': null,
  142. 'chart.key.text.color': 'black',
  143. 'chart.adjustable': false
  144. }
  145. /*
  146. * Translate half a pixel for antialiasing purposes - but only if it hasn't beeen
  147. * done already
  148. */
  149. if (!this.canvas.__rgraph_aa_translated__) {
  150. this.context.translate(0.5,0.5);
  151. this.canvas.__rgraph_aa_translated__ = true;
  152. }
  153. ///////////////////////////////// SHORT PROPERTIES /////////////////////////////////
  154. var RG = RGraph;
  155. var ca = this.canvas;
  156. var co = ca.getContext('2d');
  157. var prop = this.properties;
  158. //////////////////////////////////// METHODS ///////////////////////////////////////
  159. /**
  160. * A peudo setter
  161. *
  162. * @param name string The name of the property to set
  163. * @param value mixed The value of the property
  164. */
  165. this.Set = function (name, value)
  166. {
  167. name = name.toLowerCase();
  168. /**
  169. * This should be done first - prepend the propertyy name with "chart." if necessary
  170. */
  171. if (name.substr(0,6) != 'chart.') {
  172. name = 'chart.' + name;
  173. }
  174. if (name == 'chart.needle.style') {
  175. alert('[RGRAPH] The RGraph property chart.needle.style has changed to chart.needle.color');
  176. }
  177. if (name == 'chart.needle.thickness') {
  178. name = 'chart.needle.width';
  179. }
  180. if (name == 'chart.value') {
  181. this.value = value;
  182. return;
  183. }
  184. prop[name] = value;
  185. return this;
  186. }
  187. /**
  188. * A getter
  189. *
  190. * @param name string The name of the property to get
  191. */
  192. this.Get = function (name)
  193. {
  194. /**
  195. * This should be done first - prepend the property name with "chart." if necessary
  196. */
  197. if (name.substr(0,6) != 'chart.') {
  198. name = 'chart.' + name;
  199. }
  200. if (name == 'chart.value') {
  201. return this.value;
  202. }
  203. return prop[name.toLowerCase()];
  204. }
  205. /**
  206. * Draws the odometer
  207. */
  208. this.Draw = function ()
  209. {
  210. /**
  211. * Fire the onbeforedraw event
  212. */
  213. RG.FireCustomEvent(this, 'onbeforedraw');
  214. /**
  215. * Set the current value
  216. */
  217. this.currentValue = this.value;
  218. /**
  219. * No longer allow values outside the range of the Odo
  220. */
  221. if (this.value > this.end) {
  222. this.value = this.end;
  223. }
  224. if (this.value < this.start) {
  225. this.value = this.start;
  226. }
  227. /**
  228. * This is new in May 2011 and facilitates indiviual gutter settings,
  229. * eg chart.gutter.left
  230. */
  231. this.gutterLeft = prop['chart.gutter.left'];
  232. this.gutterRight = prop['chart.gutter.right'];
  233. this.gutterTop = prop['chart.gutter.top'];
  234. this.gutterBottom = prop['chart.gutter.bottom'];
  235. // Work out a few things
  236. this.radius = Math.min(
  237. (ca.width - this.gutterLeft - this.gutterRight) / 2,
  238. (ca.height - this.gutterTop - this.gutterBottom) / 2
  239. )
  240. - (prop['chart.border'] ? 25 : 0);
  241. this.diameter = 2 * this.radius;
  242. this.centerx = ((ca.width - this.gutterLeft- this.gutterRight) / 2) + this.gutterLeft;
  243. this.centery = ((ca.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop;
  244. this.range = this.end - this.start;
  245. /**
  246. * Move the centerx if the key is defined
  247. */
  248. if (prop['chart.key'] && prop['chart.key'].length > 0 && ca.width > ca.height) this.centerx = 5 + this.radius;
  249. if (typeof(prop['chart.centerx']) == 'number') this.centerx = prop['chart.centerx'];
  250. if (typeof(prop['chart.centery']) == 'number') this.centery = prop['chart.centery'];
  251. /**
  252. * Allow custom setting of the radius
  253. */
  254. if (typeof(prop['chart.radius']) == 'number') {
  255. this.radius = prop['chart.radius'];
  256. if (prop['chart.border']) {
  257. this.radius -= 25;
  258. }
  259. }
  260. /**
  261. * Parse the colors for gradients. Its down here so that the center X/Y can be used
  262. */
  263. if (!this.colorsParsed) {
  264. this.parseColors();
  265. // Don't want to do this again
  266. this.colorsParsed = true;
  267. }
  268. co.lineWidth = prop['chart.linewidth'];
  269. // Draw the background
  270. this.DrawBackground();
  271. // And lastly, draw the labels
  272. this.DrawLabels();
  273. // Draw the needle
  274. this.DrawNeedle(this.value, prop['chart.needle.color']);
  275. /**
  276. * Draw any extra needles
  277. */
  278. if (prop['chart.needle.extra'].length > 0) {
  279. for (var i=0; i<prop['chart.needle.extra'].length; ++i) {
  280. var needle = prop['chart.needle.extra'][i];
  281. this.DrawNeedle(needle[0], needle[1], needle[2]);
  282. }
  283. }
  284. /**
  285. * Draw the key if requested
  286. */
  287. if (prop['chart.key'] && prop['chart.key'].length > 0) {
  288. // Build a colors array out of the needle colors
  289. var colors = [prop['chart.needle.color']];
  290. if (prop['chart.needle.extra'].length > 0) {
  291. for (var i=0; i<prop['chart.needle.extra'].length; ++i) {
  292. var needle = prop['chart.needle.extra'][i];
  293. colors.push(needle[1]);
  294. }
  295. }
  296. RG.DrawKey(this, prop['chart.key'], colors);
  297. }
  298. /**
  299. * Setup the context menu if required
  300. */
  301. if (prop['chart.contextmenu']) {
  302. RG.ShowContext(this);
  303. }
  304. /**
  305. * This function enables resizing
  306. */
  307. if (prop['chart.resizable']) {
  308. RG.AllowResizing(this);
  309. }
  310. /**
  311. * This installs the event listeners
  312. */
  313. RG.InstallEventListeners(this);
  314. /**
  315. * Fire the RGraph ondraw event
  316. */
  317. RG.FireCustomEvent(this, 'ondraw');
  318. return this;
  319. }
  320. /**
  321. * Draws the background
  322. */
  323. this.DrawBackground = function ()
  324. {
  325. co.beginPath();
  326. /**
  327. * Turn on the shadow if need be
  328. */
  329. if (prop['chart.shadow.outer']) {
  330. RG.SetShadow(this, prop['chart.shadow.outer.color'], prop['chart.shadow.outer.offsetx'], prop['chart.shadow.outer.offsety'], prop['chart.shadow.outer.blur']);
  331. }
  332. var backgroundColor = '#eee';
  333. // Draw the grey border
  334. co.fillStyle = backgroundColor;
  335. co.arc(this.centerx, this.centery, this.radius, 0.0001, TWOPI, false);
  336. co.fill();
  337. /**
  338. * Turn off the shadow
  339. */
  340. RG.NoShadow(this);
  341. // Draw a circle
  342. co.strokeStyle = '#666';
  343. co.arc(this.centerx, this.centery, this.radius, 0, TWOPI, false);
  344. // Now draw a big white circle to make the lines appear as tick marks
  345. // This is solely for Chrome
  346. co.fillStyle = backgroundColor;
  347. co.arc(this.centerx, this.centery, this.radius, 0, TWOPI, false);
  348. co.fill();
  349. /**
  350. * Draw more tickmarks
  351. */
  352. if (prop['chart.tickmarks']) {
  353. co.beginPath();
  354. co.strokeStyle = '#bbb';
  355. for (var i=0; i<=360; i+=3) {
  356. co.arc(this.centerx, this.centery, this.radius, 0, i / 57.3, false);
  357. co.lineTo(this.centerx, this.centery);
  358. }
  359. co.stroke();
  360. }
  361. co.beginPath();
  362. co.lineWidth = 1;
  363. co.strokeStyle = 'black';
  364. // Now draw a big white circle to make the lines appear as tick marks
  365. co.fillStyle = backgroundColor;
  366. co.strokeStyle = backgroundColor;
  367. co.arc(this.centerx, this.centery, this.radius - 5, 0, TWOPI, false);
  368. co.fill();
  369. co.stroke();
  370. // Gray lines at 18 degree intervals
  371. co.beginPath();
  372. co.strokeStyle = '#ddd';
  373. for (var i=0; i<360; i+=18) {
  374. co.arc(this.centerx, this.centery, this.radius, 0, RG.degrees2Radians(i), false);
  375. co.lineTo(this.centerx, this.centery);
  376. }
  377. co.stroke();
  378. // Redraw the outer circle
  379. co.beginPath();
  380. co.strokeStyle = 'black';
  381. co.arc(this.centerx, this.centery, this.radius, 0, TWOPI, false);
  382. co.stroke();
  383. /**
  384. * Now draw the center bits shadow if need be
  385. */
  386. if (prop['chart.shadow.inner']) {
  387. co.beginPath();
  388. RG.SetShadow(this, prop['chart.shadow.inner.color'], prop['chart.shadow.inner.offsetx'], prop['chart.shadow.inner.offsety'], prop['chart.shadow.inner.blur']);
  389. co.arc(this.centerx, this.centery, this.radius - prop['chart.label.area'], 0, TWOPI, 0);
  390. co.fill();
  391. co.stroke();
  392. /**
  393. * Turn off the shadow
  394. */
  395. RG.NoShadow(this);
  396. }
  397. /*******************************************************
  398. * Draw the green area
  399. *******************************************************/
  400. var greengrad = prop['chart.green.color'];
  401. // Draw the "tick highlight"
  402. if (prop['chart.tickmarks.highlighted']) {
  403. co.beginPath();
  404. co.lineWidth = 5;
  405. co.strokeStyle = greengrad;
  406. co.arc(this.centerx, this.centery, this.radius - 2.5,
  407. -1 * HALFPI,
  408. (((prop['chart.green.max'] - this.start)/ (this.end - this.start)) * TWOPI) - HALFPI,
  409. 0);
  410. co.stroke();
  411. co.lineWidth = 1;
  412. }
  413. co.beginPath();
  414. co.fillStyle = greengrad;
  415. co.arc(
  416. this.centerx,
  417. this.centery,
  418. this.radius - prop['chart.label.area'],
  419. 0 - HALFPI,
  420. (((prop['chart.green.max'] - this.start)/ (this.end - this.start)) * TWOPI) - HALFPI,
  421. false
  422. );
  423. co.lineTo(this.centerx, this.centery);
  424. co.closePath();
  425. co.fill();
  426. /*******************************************************
  427. * Draw the yellow area
  428. *******************************************************/
  429. var yellowgrad = prop['chart.yellow.color'];
  430. // Draw the "tick highlight"
  431. if (prop['chart.tickmarks.highlighted']) {
  432. co.beginPath();
  433. co.lineWidth = 5;
  434. co.strokeStyle = yellowgrad;
  435. co.arc(this.centerx, this.centery, this.radius - 2.5, (
  436. ((prop['chart.green.max'] - this.start) / (this.end - this.start)) * TWOPI) - HALFPI,
  437. (((prop['chart.red.min'] - this.start) / (this.end - this.start)) * TWOPI) - HALFPI,
  438. 0);
  439. co.stroke();
  440. co.lineWidth = 1;
  441. }
  442. co.beginPath();
  443. co.fillStyle = yellowgrad;
  444. co.arc(
  445. this.centerx,
  446. this.centery,
  447. this.radius - prop['chart.label.area'],
  448. ( ((prop['chart.green.max'] - this.start) / (this.end - this.start)) * TWOPI) - HALFPI,
  449. ( ((prop['chart.red.min'] - this.start) / (this.end - this.start)) * TWOPI) - HALFPI,
  450. false
  451. );
  452. co.lineTo(this.centerx, this.centery);
  453. co.closePath();
  454. co.fill();
  455. /*******************************************************
  456. * Draw the red area
  457. *******************************************************/
  458. var redgrad = prop['chart.red.color'];
  459. // Draw the "tick highlight"
  460. if (prop['chart.tickmarks.highlighted']) {
  461. co.beginPath();
  462. co.lineWidth = 5;
  463. co.strokeStyle = redgrad;
  464. co.arc(this.centerx, this.centery, this.radius - 2.5,(((prop['chart.red.min'] - this.start) / (this.end - this.start)) * TWOPI) - HALFPI,TWOPI - HALFPI,0);
  465. co.stroke();
  466. co.lineWidth = 1;
  467. }
  468. co.beginPath();
  469. co.fillStyle = redgrad;
  470. co.strokeStyle = redgrad;
  471. co.arc(
  472. this.centerx,
  473. this.centery,
  474. this.radius - prop['chart.label.area'],
  475. (((prop['chart.red.min'] - this.start) / (this.end - this.start)) * TWOPI) - HALFPI,
  476. TWOPI - HALFPI,
  477. false
  478. );
  479. co.lineTo(this.centerx, this.centery);
  480. co.closePath();
  481. co.fill();
  482. /**
  483. * Draw the thick border
  484. */
  485. if (prop['chart.border']) {
  486. var grad = co.createRadialGradient(this.centerx, this.centery, this.radius, this.centerx, this.centery, this.radius + 20);
  487. grad.addColorStop(0, prop['chart.border.color1']);
  488. grad.addColorStop(0.5, prop['chart.border.color2']);
  489. grad.addColorStop(1, prop['chart.border.color3']);
  490. co.beginPath();
  491. co.fillStyle = grad;
  492. co.strokeStyle = 'rgba(0,0,0,0)'
  493. co.lineWidth = 0.001;
  494. co.arc(this.centerx, this.centery, this.radius + 20, 0, TWOPI, 0);
  495. co.arc(this.centerx, this.centery, this.radius - 2, TWOPI, 0, 1);
  496. co.fill();
  497. }
  498. // Put the linewidth back to what it was
  499. co.lineWidth = prop['chart.linewidth'];
  500. /**
  501. * Draw the title if specified
  502. */
  503. if (prop['chart.title']) {
  504. RG.DrawTitle(this,
  505. prop['chart.title'],
  506. this.centery - this.radius,
  507. null,
  508. prop['chart.title.size'] ? prop['chart.title.size'] : prop['chart.text.size'] + 2);
  509. }
  510. // Draw the big tick marks
  511. if (!prop['chart.tickmarks.highlighted']) {
  512. for (var i=18; i<=360; i+=36) {
  513. co.beginPath();
  514. co.strokeStyle = '#999';
  515. co.lineWidth = 2;
  516. co.arc(this.centerx, this.centery, this.radius - 1, RG.degrees2Radians(i), RG.degrees2Radians(i+0.01), false);
  517. co.arc(this.centerx, this.centery, this.radius - 7, RG.degrees2Radians(i), RG.degrees2Radians(i+0.01), false);
  518. co.stroke();
  519. }
  520. }
  521. }
  522. /**
  523. * Draws the needle of the odometer
  524. *
  525. * @param number value The value to represent
  526. * @param string color The color of the needle
  527. * @param number The OPTIONAL length of the needle
  528. */
  529. this.DrawNeedle = function (value, color)
  530. {
  531. // The optional length of the needle
  532. var length = arguments[2] ? arguments[2] : this.radius - prop['chart.label.area'];
  533. // ===== First draw a grey background circle =====
  534. co.fillStyle = '#999';
  535. co.beginPath();
  536. co.moveTo(this.centerx, this.centery);
  537. co.arc(this.centerx, this.centery, 10, 0, TWOPI, false);
  538. co.fill();
  539. co.closePath();
  540. co.fill();
  541. // ===============================================
  542. co.fillStyle = color
  543. co.strokeStyle = '#666';
  544. // Draw the centre bit
  545. co.beginPath();
  546. co.moveTo(this.centerx, this.centery);
  547. co.arc(this.centerx, this.centery, 8, 0, TWOPI, false);
  548. co.fill();
  549. co.closePath();
  550. co.stroke();
  551. co.fill();
  552. if (prop['chart.needle.type'] == 'pointer') {
  553. co.strokeStyle = color;
  554. co.lineWidth = prop['chart.needle.width'];
  555. co.lineCap = 'round';
  556. co.lineJoin = 'round';
  557. // Draw the needle
  558. co.beginPath();
  559. // The trailing bit on the opposite side of the dial
  560. co.beginPath();
  561. co.moveTo(this.centerx, this.centery);
  562. if (prop['chart.needle.tail']) {
  563. co.arc(this.centerx,
  564. this.centery,
  565. 20,
  566. (((value / this.range) * 360) + 90) / (180 / PI),
  567. (((value / this.range) * 360) + 90 + 0.01) / (180 / PI), // The 0.01 avoids a bug in ExCanvas and Chrome 6
  568. false
  569. );
  570. }
  571. // Draw the long bit on the opposite side
  572. co.arc(this.centerx,
  573. this.centery,
  574. length - 10,
  575. (((value / this.range) * 360) - 90) / (180 / PI),
  576. (((value / this.range) * 360) - 90 + 0.1 ) / (180 / PI), // The 0.1 avoids a bug in ExCanvas and Chrome 6
  577. false
  578. );
  579. co.closePath();
  580. //co.stroke();
  581. //co.fill();
  582. } else if (prop['chart.needle.type'] == 'triangle') {
  583. co.lineWidth = 0.01;
  584. co.lineEnd = 'square';
  585. co.lineJoin = 'miter';
  586. /**
  587. * This draws the version of the pointer that becomes the border
  588. */
  589. co.beginPath();
  590. co.fillStyle = prop['chart.needle.triangle.border'];
  591. co.arc(this.centerx, this.centery, 11, (((value / this.range) * 360)) / 57.3, ((((value / this.range) * 360)) + 0.01) / 57.3, 0);
  592. co.arc(this.centerx, this.centery, 11, (((value / this.range) * 360) + 180) / 57.3, ((((value / this.range) * 360) + 180) + 0.01)/ 57.3, 0);
  593. co.arc(this.centerx, this.centery, length - 5, (((value / this.range) * 360) - 90) / 57.3, ((((value / this.range) * 360) - 90) / 57.3) + 0.01, 0);
  594. co.closePath();
  595. co.fill();
  596. co.beginPath();
  597. co.arc(this.centerx, this.centery, 15, 0, TWOPI, 0);
  598. co.closePath();
  599. co.fill();
  600. // This draws the pointer
  601. co.beginPath();
  602. co.strokeStyle = 'black';
  603. co.fillStyle = color;
  604. co.arc(this.centerx, this.centery, 7, (((value / this.range) * 360)) / 57.3, ((((value / this.range) * 360)) + 0.01) / 57.3, 0);
  605. co.arc(this.centerx, this.centery, 7, (((value / this.range) * 360) + 180) / 57.3, ((((value / this.range) * 360) + 180) + 0.01)/ 57.3, 0);
  606. co.arc(this.centerx, this.centery, length - 13, (((value / this.range) * 360) - 90) / 57.3, ((((value / this.range) * 360) - 90) / 57.3) + 0.01, 0);
  607. co.closePath();
  608. co.stroke();
  609. co.fill();
  610. /**
  611. * This is here to accomodate the MSIE/ExCanvas combo
  612. */
  613. co.beginPath();
  614. co.arc(this.centerx, this.centery, 7, 0, TWOPI, 0);
  615. co.closePath();
  616. co.fill();
  617. }
  618. co.stroke();
  619. co.fill();
  620. // Draw the mini center circle
  621. co.beginPath();
  622. co.fillStyle = color;
  623. co.arc(this.centerx, this.centery, prop['chart.needle.type'] == 'pointer' ? 7 : 12, 0.01, TWOPI, false);
  624. co.fill();
  625. // This draws the arrow at the end of the line
  626. if (prop['chart.needle.head'] && prop['chart.needle.type'] == 'pointer') {
  627. co.lineWidth = 1;
  628. co.fillStyle = color;
  629. // round, bevel, miter
  630. co.lineJoin = 'miter';
  631. co.lineCap = 'butt';
  632. co.beginPath();
  633. co.arc(this.centerx, this.centery, length - 5, (((value / this.range) * 360) - 90) / 57.3, (((value / this.range) * 360) - 90 + 0.1) / 57.3, false);
  634. co.arc(this.centerx,
  635. this.centery,
  636. length - 20,
  637. RG.degrees2Radians( ((value / this.range) * 360) - (length < 60 ? 80 : 85) ),
  638. RG.degrees2Radians( ((value / this.range) * 360) - (length < 60 ? 100 : 95) ),
  639. 1);
  640. co.closePath();
  641. co.fill();
  642. //co.stroke();
  643. }
  644. /**
  645. * Draw a white circle at the centre
  646. */
  647. co.beginPath();
  648. co.fillStyle = 'gray';
  649. co.moveTo(this.centerx, this.centery);
  650. co.arc(this.centerx,this.centery,2,0,6.2795,false);
  651. co.closePath();
  652. co.fill();
  653. }
  654. /**
  655. * Draws the labels for the Odo
  656. */
  657. this.DrawLabels = function ()
  658. {
  659. var size = prop['chart.text.size'];
  660. var font = prop['chart.text.font'];
  661. var centerx = this.centerx;
  662. var centery = this.centery;
  663. var r = this.radius - (prop['chart.label.area'] / 2);
  664. var start = this.start;
  665. var end = this.end;
  666. var decimals = prop['chart.scale.decimals'];
  667. var labels = prop['chart.labels'];
  668. var units_pre = prop['chart.units.pre'];
  669. var units_post = prop['chart.units.post'];
  670. co.beginPath();
  671. co.fillStyle = prop['chart.text.color'];
  672. /**
  673. * If label are specified, use those
  674. */
  675. if (labels) {
  676. for (var i=0; i<labels.length; ++i) {
  677. RG.Text2(this, {'font':font,
  678. 'size':size,
  679. 'x':centerx + (Math.cos(((i / labels.length) * TWOPI) - HALFPI) * (this.radius - (prop['chart.label.area'] / 2) ) ), // Sin A = Opp / Hyp
  680. 'y':centery + (Math.sin(((i / labels.length) * TWOPI) - HALFPI) * (this.radius - (prop['chart.label.area'] / 2) ) ), // Cos A = Adj / Hyp
  681. 'text': String(labels[i]),
  682. 'valign':'center',
  683. 'halign':'center',
  684. 'tag': 'labels'
  685. });
  686. }
  687. /**
  688. * If not, use the maximum value
  689. */
  690. } else {
  691. RG.Text2(this, {'font':font,'size':size,'x':centerx + (0.588 * r ),'y':centery - (0.809 * r ),'text':RG.number_format(this, (((end - start) * (1/10)) + start).toFixed(decimals), units_pre, units_post),'halign':'center','valign':'center','angle':36,'tag': 'scale'});
  692. RG.Text2(this, {'font':font,'size':size,'x':centerx + (0.951 * r ),'y':centery - (0.309 * r),'text':RG.number_format(this, (((end - start) * (2/10)) + start).toFixed(decimals), units_pre, units_post),'halign':'center','valign':'center','angle':72,'tag': 'scale'});
  693. RG.Text2(this, {'font':font,'size':size,'x':centerx + (0.949 * r),'y':centery + (0.31 * r),'text':RG.number_format(this, (((end - start) * (3/10)) + start).toFixed(decimals), units_pre, units_post),'halign':'center','valign':'center','angle':108,'tag': 'scale'});
  694. RG.Text2(this, {'font':font,'size':size,'x':centerx + (0.588 * r ),'y':centery + (0.809 * r ),'text':RG.number_format(this, (((end - start) * (4/10)) + start).toFixed(decimals), units_pre, units_post),'halign':'center','valign':'center','angle':144,'tag': 'scale'});
  695. RG.Text2(this, {'font':font,'size':size,'x':centerx,'y':centery + r,'text':RG.number_format(this, (((end - start) * (5/10)) + start).toFixed(decimals),units_pre, units_post),'halign':'center','valign':'center','angle':180,'tag': 'scale'});
  696. RG.Text2(this, {'font':font,'size':size,'x':centerx - (0.588 * r ),'y':centery + (0.809 * r ),'text':RG.number_format(this, (((end - start) * (6/10)) + start).toFixed(decimals), units_pre, units_post),'halign':'center','valign':'center','angle':216,'tag': 'scale'});
  697. RG.Text2(this, {'font':font,'size':size,'x':centerx - (0.949 * r),'y':centery + (0.300 * r),'text':RG.number_format(this, (((end - start) * (7/10)) + start).toFixed(decimals), units_pre, units_post),'halign':'center','valign':'center','angle':252,'tag': 'scale'});
  698. RG.Text2(this, {'font':font,'size':size,'x':centerx - (0.951 * r),'y':centery - (0.309 * r),'text':RG.number_format(this, (((end - start) * (8/10)) + start).toFixed(decimals), units_pre, units_post),'halign':'center','valign':'center','angle':288,'tag': 'scale'});
  699. RG.Text2(this, {'font':font,'size':size,'x':centerx - (0.588 * r ),'y':centery - (0.809 * r ),'text':RG.number_format(this, (((end - start) * (9/10)) + start).toFixed(decimals), units_pre, units_post),'halign':'center','valign':'center','angle':324,'tag': 'scale'});
  700. RG.Text2(this, {'font':font,'size':size,'x':centerx,'y':centery - r,'text': prop['chart.zerostart'] ? RG.number_format(this, this.start.toFixed(decimals), units_pre, units_post) : RG.number_format(this, (((end - start) * (10/10)) + start).toFixed(decimals), units_pre, units_post),'halign':'center','valign':'center','tag': 'scale'});
  701. }
  702. co.fill();
  703. /**
  704. * Draw the text label below the center point
  705. */
  706. if (prop['chart.value.text']) {
  707. co.strokeStyle = 'black';
  708. RG.Text2(this, {'font':font,
  709. 'size':size+2,
  710. 'x':centerx,
  711. 'y':centery + size + 15,
  712. 'text':String(prop['chart.value.units.pre'] + this.value.toFixed(prop['chart.value.text.decimals']) + prop['chart.value.units.post']),
  713. 'halign':'center',
  714. 'valign':'center',
  715. 'bounding':true,
  716. 'boundingFill':'white',
  717. 'tag': 'value.text'
  718. });
  719. }
  720. }
  721. /**
  722. * A placeholder function
  723. *
  724. * @param object The event object
  725. */
  726. this.getShape = function (e)
  727. {
  728. }
  729. /**
  730. * This function returns the pertinent value at the point of click
  731. *
  732. * @param object The event object
  733. */
  734. this.getValue = function (e)
  735. {
  736. var mouseXY = RG.getMouseXY(e)
  737. var angle = RG.getAngleByXY(this.centerx, this.centery, mouseXY[0], mouseXY[1]);
  738. angle += HALFPI;
  739. if (mouseXY[0] >= this.centerx && mouseXY[1] <= this.centery) {
  740. angle -= TWOPI;
  741. }
  742. var value = ((angle / TWOPI) * (this.max - this.min)) + this.min;
  743. return value;
  744. }
  745. /**
  746. * The getObjectByXY() worker method. Don't call this call:
  747. *
  748. * RGraph.ObjectRegistry.getObjectByXY(e)
  749. *
  750. * @param object e The event object
  751. */
  752. this.getObjectByXY = function (e)
  753. {
  754. var mouseXY = RG.getMouseXY(e);
  755. var radius = RG.getHypLength(this.centerx, this.centery, mouseXY[0], mouseXY[1]);
  756. if (
  757. mouseXY[0] > (this.centerx - this.radius)
  758. && mouseXY[0] < (this.centerx + this.radius)
  759. && mouseXY[1] > (this.centery - this.radius)
  760. && mouseXY[1] < (this.centery + this.radius)
  761. && radius <= this.radius
  762. ) {
  763. return this;
  764. }
  765. }
  766. /**
  767. * This method handles the adjusting calculation for when the mouse is moved
  768. *
  769. * @param object e The event object
  770. */
  771. this.Adjusting_mousemove = function (e)
  772. {
  773. /**
  774. * Handle adjusting for the Bar
  775. */
  776. if (prop['chart.adjustable'] && RG.Registry.Get('chart.adjusting') && RG.Registry.Get('chart.adjusting').uid == this.uid) {
  777. this.value = this.getValue(e);
  778. RG.Clear(ca);
  779. RG.RedrawCanvas(ca);
  780. RG.FireCustomEvent(this, 'onadjust');
  781. }
  782. }
  783. /**
  784. * This method returns the appropriate angle for a value
  785. *
  786. * @param number value The value
  787. */
  788. this.getAngle = function (value)
  789. {
  790. // Higher than max or lower than min
  791. if (value > this.max || value < this.min) {
  792. return null;
  793. }
  794. var angle = (((value - this.min) / (this.max - this.min)) * TWOPI);
  795. angle -= HALFPI;
  796. return angle;
  797. }
  798. /**
  799. * This allows for easy specification of gradients
  800. */
  801. this.parseColors = function ()
  802. {
  803. // Parse the basic colors
  804. prop['chart.green.color'] = this.parseSingleColorForGradient(prop['chart.green.color']);
  805. prop['chart.yellow.color'] = this.parseSingleColorForGradient(prop['chart.yellow.color']);
  806. prop['chart.red.color'] = this.parseSingleColorForGradient(prop['chart.red.color']);
  807. }
  808. /**
  809. * This parses a single color value
  810. */
  811. this.parseSingleColorForGradient = function (color)
  812. {
  813. if (!color || typeof(color) != 'string') {
  814. return color;
  815. }
  816. if (color.match(/^gradient\((.*)\)$/i)) {
  817. var parts = RegExp.$1.split(':');
  818. // Create the gradient
  819. var grad = co.createRadialGradient(this.centerx, this.centery, 0, this.centerx, this.centery, this.radius);
  820. var diff = 1 / (parts.length - 1);
  821. grad.addColorStop(0, RG.trim(parts[0]));
  822. for (var j=1; j<parts.length; ++j) {
  823. grad.addColorStop(j * diff, RG.trim(parts[j]));
  824. }
  825. }
  826. return grad ? grad : color;
  827. }
  828. /**
  829. * Register the object
  830. */
  831. RG.Register(this);
  832. }