RGraph.hprogress.js 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206
  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 progress bar constructor
  14. *
  15. * @param int id The ID of the canvas tag
  16. * @param int value The indicated value of the meter.
  17. * @param int max The end value (the upper most) of the meter
  18. */
  19. RGraph.HProgress = 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('2d');
  26. this.canvas.__object__ = this;
  27. this.min = min;
  28. this.max = max;
  29. this.value = RGraph.stringsToNumbers(value);
  30. this.type = 'hprogress';
  31. this.coords = [];
  32. this.isRGraph = true;
  33. this.currentValue = null;
  34. this.uid = RGraph.CreateUID();
  35. this.canvas.uid = this.canvas.uid ? this.canvas.uid : RGraph.CreateUID();
  36. this.colorsParsed = false;
  37. this.coordsText = [];
  38. this.original_colors = [];
  39. this.firstDraw = true; // After the first draw this will be false
  40. /**
  41. * Compatibility with older browsers
  42. */
  43. //RGraph.OldBrowserCompat(this.context);
  44. this.properties =
  45. {
  46. 'chart.colors': ['Gradient(white:#0c0)','Gradient(white:red)','Gradient(white:green)','yellow','pink','cyan','black','white','gray'],
  47. 'chart.strokestyle.inner': '#999',
  48. 'chart.strokestyle.outer': '#999',
  49. 'chart.tickmarks': true,
  50. 'chart.tickmarks.color': '#999',
  51. 'chart.tickmarks.inner': false,
  52. 'chart.tickmarks.zerostart':true,
  53. 'chart.gutter.left': 25,
  54. 'chart.gutter.right': 25,
  55. 'chart.gutter.top': 25,
  56. 'chart.gutter.bottom': 25,
  57. 'chart.numticks': 10,
  58. 'chart.numticks.inner': 50,
  59. 'chart.background.color': '#eee',
  60. 'chart.shadow': false,
  61. 'chart.shadow.color': 'rgba(0,0,0,0.5)',
  62. 'chart.shadow.blur': 3,
  63. 'chart.shadow.offsetx': 3,
  64. 'chart.shadow.offsety': 3,
  65. 'chart.title': '',
  66. 'chart.title.background': null,
  67. 'chart.title.bold': true,
  68. 'chart.title.font': null,
  69. 'chart.title.x': null,
  70. 'chart.title.y': null,
  71. 'chart.title.halign': null,
  72. 'chart.title.valign': null,
  73. 'chart.text.size': 10,
  74. 'chart.text.color': 'black',
  75. 'chart.text.font': 'Arial',
  76. 'chart.contextmenu': null,
  77. 'chart.units.pre': '',
  78. 'chart.units.post': '',
  79. 'chart.tooltips': null,
  80. 'chart.tooltips.effect': 'fade',
  81. 'chart.tooltips.css.class': 'RGraph_tooltip',
  82. 'chart.tooltips.highlight': true,
  83. 'chart.tooltips.event': 'onclick',
  84. 'chart.highlight.stroke': 'rgba(0,0,0,0)',
  85. 'chart.highlight.fill': 'rgba(255,255,255,0.7)',
  86. 'chart.annotatable': false,
  87. 'chart.annotate.color': 'black',
  88. 'chart.zoom.factor': 1.5,
  89. 'chart.zoom.fade.in': true,
  90. 'chart.zoom.fade.out': true,
  91. 'chart.zoom.hdir': 'right',
  92. 'chart.zoom.vdir': 'down',
  93. 'chart.zoom.frames': 25,
  94. 'chart.zoom.delay': 16.666,
  95. 'chart.zoom.shadow': true,
  96. 'chart.zoom.background': true,
  97. 'chart.arrows': false,
  98. 'chart.margin': 0,
  99. 'chart.resizable': false,
  100. 'chart.resize.handle.adjust':[0,0],
  101. 'chart.resize.handle.background':null,
  102. 'chart.labels.specific': null,
  103. 'chart.labels.count': 10,
  104. 'chart.adjustable': false,
  105. 'chart.scale.decimals': 0,
  106. 'chart.scale.point': '.',
  107. 'chart.scale.thousand': ',',
  108. 'chart.key': null,
  109. 'chart.key.background': 'white',
  110. 'chart.key.position': 'gutter',
  111. 'chart.key.halign': 'right',
  112. 'chart.key.shadow': false,
  113. 'chart.key.shadow.color': '#666',
  114. 'chart.key.shadow.blur': 3,
  115. 'chart.key.shadow.offsetx': 2,
  116. 'chart.key.shadow.offsety': 2,
  117. 'chart.key.position.gutter.boxed': false,
  118. 'chart.key.position.x': null,
  119. 'chart.key.position.y': null,
  120. 'chart.key.color.shape': 'square',
  121. 'chart.key.rounded': true,
  122. 'chart.key.linewidth': 1,
  123. 'chart.key.colors': null,
  124. 'chart.key.color.shape': 'square',
  125. 'chart.key.interactive': false,
  126. 'chart.key.interactive.highlight.chart.stroke': 'black',
  127. 'chart.key.interactive.highlight.chart.fill': 'rgba(255,255,255,0.7)',
  128. 'chart.key.interactive.highlight.label': 'rgba(255,0,0,0.2)',
  129. 'chart.key.text.color': 'black',
  130. 'chart.labels.position': 'bottom',
  131. 'chart.events.mousemove': null,
  132. 'chart.events.click': null,
  133. 'chart.border.inner': true
  134. }
  135. // Check for support
  136. if (!this.canvas) {
  137. alert('[HPROGRESS] No canvas support');
  138. return;
  139. }
  140. /**
  141. * Create the dollar objects so that functions can be added to them
  142. */
  143. var linear_data = RGraph.array_linearize(value);
  144. for (var i=0; i<linear_data.length; ++i) {
  145. this['$' + i] = {};
  146. }
  147. /**
  148. * Translate half a pixel for antialiasing purposes - but only if it hasn't beeen
  149. * done already
  150. */
  151. if (!this.canvas.__rgraph_aa_translated__) {
  152. this.context.translate(0.5,0.5);
  153. this.canvas.__rgraph_aa_translated__ = true;
  154. }
  155. // Short variable names
  156. var RG = RGraph;
  157. var ca = this.canvas;
  158. var co = ca.getContext('2d');
  159. var prop = this.properties;
  160. var jq = jQuery;
  161. var pa = RG.Path;
  162. var win = window;
  163. var doc = document
  164. var ma = Math;
  165. /**
  166. * "Decorate" the object with the generic effects if the effects library has been included
  167. */
  168. if (RG.Effects && typeof RG.Effects.decorate === 'function') {
  169. RG.Effects.decorate(this);
  170. }
  171. /**
  172. * A generic setter
  173. *
  174. * @param string name The name of the property to set
  175. * @param string value The value of the poperty
  176. */
  177. this.set =
  178. this.Set = function (name, value)
  179. {
  180. name = name.toLowerCase();
  181. /**
  182. * This should be done first - prepend the propertyy name with "chart." if necessary
  183. */
  184. if (name.substr(0,6) != 'chart.') {
  185. name = 'chart.' + name;
  186. }
  187. /**
  188. * chart.strokestyle now sets both chart.strokestyle.inner and chart.strokestyle.outer
  189. */
  190. if (name == 'chart.strokestyle') {
  191. this.Set('chart.strokestyle.inner', value);
  192. this.Set('chart.strokestyle.outer', value);
  193. return;
  194. }
  195. prop[name] = value;
  196. return this;
  197. };
  198. /**
  199. * A generic getter
  200. *
  201. * @param string name The name of the property to get
  202. */
  203. this.get =
  204. this.Get = function (name)
  205. {
  206. /**
  207. * This should be done first - prepend the property name with "chart." if necessary
  208. */
  209. if (name.substr(0,6) != 'chart.') {
  210. name = 'chart.' + name;
  211. }
  212. return prop[name.toLowerCase()];
  213. };
  214. /**
  215. * Draws the progress bar
  216. */
  217. this.draw =
  218. this.Draw = function ()
  219. {
  220. /**
  221. * Fire the onbeforedraw event
  222. */
  223. RG.FireCustomEvent(this, 'onbeforedraw');
  224. /**
  225. * Parse the colors. This allows for simple gradient syntax
  226. */
  227. if (!this.colorsParsed) {
  228. this.parseColors();
  229. // Don't want to do this again
  230. this.colorsParsed = true;
  231. }
  232. /**
  233. * Set the current value
  234. */
  235. this.currentValue = this.value;
  236. /**
  237. * This is new in May 2011 and facilitates individual gutter settings,
  238. * eg chart.gutter.left
  239. */
  240. this.gutterLeft = prop['chart.gutter.left'];
  241. this.gutterRight = prop['chart.gutter.right'];
  242. this.gutterTop = prop['chart.gutter.top'];
  243. this.gutterBottom = prop['chart.gutter.bottom'];
  244. // Figure out the width and height
  245. this.width = ca.width - this.gutterLeft - this.gutterRight;
  246. this.height = ca.height - this.gutterTop - this.gutterBottom;
  247. this.coords = [];
  248. this.coordsText = [];
  249. this.Drawbar();
  250. this.DrawTickMarks();
  251. this.DrawLabels();
  252. this.DrawTitle();
  253. co.stroke();
  254. co.fill();
  255. /**
  256. * Draw the bevel effect if requested
  257. */
  258. if (prop['chart.bevel']) {
  259. this.DrawBevel();
  260. }
  261. /**
  262. * Setup the context menu if required
  263. */
  264. if (prop['chart.contextmenu']) {
  265. RG.ShowContext(this);
  266. }
  267. // Draw the key if necessary
  268. if (prop['chart.key'] && prop['chart.key'].length) {
  269. RG.DrawKey(this, prop['chart.key'], prop['chart.colors']);
  270. }
  271. /**
  272. * This function enables resizing
  273. */
  274. if (prop['chart.resizable']) {
  275. RG.AllowResizing(this);
  276. }
  277. /**
  278. * This installs the event listeners
  279. */
  280. RG.InstallEventListeners(this);
  281. /**
  282. * Fire the onfirstdraw event
  283. */
  284. if (this.firstDraw) {
  285. RG.fireCustomEvent(this, 'onfirstdraw');
  286. this.firstDrawFunc();
  287. this.firstDraw = false;
  288. }
  289. /**
  290. * Fire the RGraph ondraw event
  291. */
  292. RG.FireCustomEvent(this, 'ondraw');
  293. return this;
  294. };
  295. /**
  296. * Draws the bar
  297. */
  298. this.drawbar =
  299. this.Drawbar = function ()
  300. {
  301. /**
  302. * First get the scale
  303. */
  304. this.scale2 = RG.getScale2(this, {
  305. 'max':this.max,
  306. 'min':this.min,
  307. 'strict':true,
  308. 'scale.thousand':prop['chart.scale.thousand'],
  309. 'scale.point':prop['chart.scale.point'],
  310. 'scale.decimals':prop['chart.scale.decimals'],
  311. 'ylabels.count':prop['chart.labels.count'],
  312. 'scale.round':prop['chart.scale.round'],
  313. 'units.pre': prop['chart.units.pre'],
  314. 'units.post': prop['chart.units.post']
  315. });
  316. // Set a shadow if requested
  317. if (prop['chart.shadow']) {
  318. RG.SetShadow(this, prop['chart.shadow.color'], prop['chart.shadow.offsetx'], prop['chart.shadow.offsety'], prop['chart.shadow.blur']);
  319. }
  320. // Draw the shadow for MSIE
  321. if (RG.ISOLD && prop['chart.shadow']) {
  322. co.fillStyle = prop['chart.shadow.color'];
  323. co.fillRect(this.gutterLeft + prop['chart.shadow.offsetx'], this.gutterTop + prop['chart.shadow.offsety'], this.width, this.height);
  324. }
  325. // Draw the outline
  326. co.fillStyle = prop['chart.background.color'];
  327. co.strokeStyle = prop['chart.strokestyle.outer'];
  328. co.strokeRect(this.gutterLeft, this.gutterTop, this.width, this.height);
  329. co.fillRect(this.gutterLeft, this.gutterTop, this.width, this.height);
  330. // Turn off any shadow
  331. RG.NoShadow(this);
  332. co.fillStyle = prop['chart.colors'][0];
  333. co.strokeStyle = prop['chart.strokestyle.outer'];
  334. var margin = prop['chart.margin'];
  335. // Draw the actual bar itself
  336. var barWidth = Math.min(this.width, ((RG.array_sum(this.value) - this.min) / (this.max - this.min) ) * this.width);
  337. if (prop['chart.tickmarks.inner']) {
  338. var spacing = (ca.width - this.gutterLeft - this.gutterRight) / prop['chart.numticks.inner'];
  339. co.lineWidth = 1;
  340. co.strokeStyle = prop['chart.strokestyle.outer'];
  341. co.beginPath();
  342. for (var x = this.gutterLeft; x<ca.width - this.gutterRight; x+=spacing) {
  343. co.moveTo(Math.round(x), this.gutterTop);
  344. co.lineTo(Math.round(x), this.gutterTop + 2);
  345. co.moveTo(Math.round(x), ca.height - this.gutterBottom);
  346. co.lineTo(Math.round(x), ca.height - this.gutterBottom - 2);
  347. }
  348. co.stroke();
  349. }
  350. /**
  351. * This bit draws the actual progress bar
  352. */
  353. if (typeof(this.value) == 'number') {
  354. co.beginPath();
  355. co.strokeStyle = prop['chart.strokestyle.inner'];
  356. co.fillStyle = prop['chart.colors'][0];
  357. if (prop['chart.border.inner']) {
  358. co.strokeRect(this.gutterLeft, this.gutterTop + margin, barWidth, this.height - margin - margin);
  359. }
  360. co.fillRect(this.gutterLeft, this.gutterTop + margin, barWidth, this.height - margin - margin);
  361. // Store the coords
  362. this.coords.push([this.gutterLeft,
  363. this.gutterTop + margin,
  364. barWidth,
  365. this.height - margin - margin]);
  366. } else if (typeof(this.value) == 'object') {
  367. co.beginPath();
  368. co.strokeStyle = prop['chart.strokestyle.inner'];
  369. var startPoint = this.gutterLeft;
  370. for (var i=0; i<this.value.length; ++i) {
  371. var segmentLength = (this.value[i] / RG.array_sum(this.value)) * barWidth;
  372. co.fillStyle = prop['chart.colors'][i];
  373. if (prop['chart.border.inner']) {
  374. co.strokeRect(startPoint, this.gutterTop + margin, segmentLength, this.height - margin - margin);
  375. }
  376. co.fillRect(startPoint, this.gutterTop + margin, segmentLength, this.height - margin - margin);
  377. // Store the coords
  378. this.coords.push([startPoint,
  379. this.gutterTop + margin,
  380. segmentLength,
  381. this.height - margin - margin]);
  382. startPoint += segmentLength;
  383. }
  384. }
  385. /**
  386. * Draw the arrows indicating the level if requested
  387. */
  388. if (prop['chart.arrows']) {
  389. var x = this.gutterLeft + barWidth;
  390. var y = this.gutterTop;
  391. co.lineWidth = 1;
  392. co.fillStyle = 'black';
  393. co.strokeStyle = 'black';
  394. co.beginPath();
  395. co.moveTo(x, y - 3);
  396. co.lineTo(x + 2, y - 7);
  397. co.lineTo(x - 2, y - 7);
  398. co.closePath();
  399. co.stroke();
  400. co.fill();
  401. co.beginPath();
  402. co.moveTo(x, y + this.height + 4);
  403. co.lineTo(x + 2, y + this.height + 9);
  404. co.lineTo(x - 2, y + this.height + 9);
  405. co.closePath();
  406. co.stroke();
  407. co.fill()
  408. }
  409. /**
  410. * Draw the "in-bar" label
  411. */
  412. if (prop['chart.label.inner']) {
  413. co.fillStyle = 'black';
  414. RG.Text2(this, {'font':prop['chart.text.font'],
  415. 'size':prop['chart.text.size'] + 2,
  416. 'x':this.gutterLeft + barWidth + 5,
  417. 'y':this.gutterTop + (this.height / 2),
  418. 'text': String(prop['chart.units.pre'] + this.value + prop['chart.units.post']),
  419. 'valign':'bottom',
  420. 'halign':'left',
  421. 'bounding':true,
  422. 'boundingFill':'white',
  423. 'tag': 'label.inner'
  424. });
  425. }
  426. };
  427. /**
  428. * The function that draws the tick marks. Apt name...
  429. */
  430. this.drawTickMarks =
  431. this.DrawTickMarks = function ()
  432. {
  433. co.strokeStyle = prop['chart.tickmarks.color'];
  434. if (prop['chart.tickmarks']) {
  435. co.beginPath();
  436. // This is used by the label function below
  437. this.tickInterval = this.width / prop['chart.numticks'];
  438. var start = prop['chart.tickmarks.zerostart'] ? 0 : this.tickInterval;
  439. if (prop['chart.labels.position'] == 'top') {
  440. for (var i=this.gutterLeft + start; i<=(this.width + this.gutterLeft + 0.1); i+=this.tickInterval) {
  441. co.moveTo(Math.round(i), this.gutterTop);
  442. co.lineTo(Math.round(i), this.gutterTop - 4);
  443. }
  444. } else {
  445. for (var i=this.gutterLeft + start; i<=(this.width + this.gutterLeft + 0.1); i+=this.tickInterval) {
  446. co.moveTo(Math.round(i), this.gutterTop + this.height);
  447. co.lineTo(Math.round(i), this.gutterTop + this.height + 4);
  448. }
  449. }
  450. co.stroke();
  451. }
  452. };
  453. /**
  454. * The function that draws the labels
  455. */
  456. this.drawLabels =
  457. this.DrawLabels = function ()
  458. {
  459. if (!RG.is_null(prop['chart.labels.specific'])) {
  460. return this.DrawSpecificLabels();
  461. }
  462. co.fillStyle = prop['chart.text.color'];
  463. var xPoints = [];
  464. var yPoints = [];
  465. var font = prop['chart.text.font'];
  466. var size = prop['chart.text.size'];
  467. for (i=0,len=this.scale2.labels.length; i<len; i++) {
  468. if (prop['chart.labels.position'] == 'top') {
  469. var x = this.width * (i/this.scale2.labels.length) + this.gutterLeft + (this.width / this.scale2.labels.length);
  470. var y = this.gutterTop - 6;
  471. var valign = 'bottom';
  472. } else {
  473. var x = this.width * (i/this.scale2.labels.length) + this.gutterLeft + (this.width / this.scale2.labels.length);
  474. var y = this.height + this.gutterTop + 4;
  475. var valign = 'top';
  476. }
  477. RG.Text2(this, {'font':font,
  478. 'size':size,
  479. 'x':x,
  480. 'y':y,
  481. 'text': this.scale2.labels[i],
  482. 'valign':valign,
  483. 'halign':'center',
  484. 'tag': 'scale'
  485. });
  486. }
  487. if (prop['chart.tickmarks.zerostart']) {
  488. if (prop['chart.labels.position'] == 'top') {
  489. RG.Text2(this, {'font':font,
  490. 'size':size,
  491. 'x':this.gutterLeft,
  492. 'y':this.gutterTop - 6,
  493. 'text': prop['chart.units.pre'] + Number(this.min).toFixed(prop['chart.scale.decimals']) + prop['chart.units.post'],
  494. 'valign':'bottom',
  495. 'halign':'center',
  496. 'tag': 'scale'
  497. });
  498. } else {
  499. RG.Text2(this, {'font':font,
  500. 'size':size,
  501. 'x':this.gutterLeft,
  502. 'y':ca.height - this.gutterBottom + 5,
  503. 'text': prop['chart.units.pre'] + Number(this.min).toFixed(prop['chart.scale.decimals']) + prop['chart.units.post'],
  504. 'valign':'top',
  505. 'halign':'center',
  506. 'tag': 'scale'
  507. });
  508. }
  509. }
  510. };
  511. /**
  512. * Returns the focused bar
  513. *
  514. * @param event e The event object
  515. */
  516. this.getShape =
  517. this.getBar = function (e)
  518. {
  519. var mouseCoords = RG.getMouseXY(e)
  520. for (var i=0; i<this.coords.length; i++) {
  521. var mouseCoords = RG.getMouseXY(e);
  522. var mouseX = mouseCoords[0];
  523. var mouseY = mouseCoords[1];
  524. var left = this.coords[i][0];
  525. var top = this.coords[i][1];
  526. var width = this.coords[i][2];
  527. var height = this.coords[i][3];
  528. var idx = i;
  529. if (mouseX >= left && mouseX <= (left + width) && mouseY >= top && mouseY <= (top + height) ) {
  530. var tooltip = RG.parseTooltipText(prop['chart.tooltips'], idx);
  531. return {
  532. 0: this, 1: left, 2: top, 3: width, 4: height, 5: idx,
  533. 'object':this, 'x':left, 'y':top, 'width': width, 'height': height, 'index': idx, 'tooltip': tooltip
  534. }
  535. }
  536. }
  537. };
  538. /**
  539. * This function returns the value that the mouse is positioned at, regardless of
  540. * the actual indicated value.
  541. *
  542. * @param object e The event object
  543. */
  544. this.getValue = function (e)
  545. {
  546. var mouseXY = RG.getMouseXY(e);
  547. var value = (mouseXY[0] - this.gutterLeft) / this.width;
  548. value *= this.max - this.min;
  549. value += this.min;
  550. if (mouseXY[0] < this.gutterLeft) {
  551. value = this.min;
  552. }
  553. if (mouseXY[0] > (ca.width - this.gutterRight) ) {
  554. value = this.max
  555. }
  556. return value;
  557. };
  558. /**
  559. * Each object type has its own Highlight() function which highlights the appropriate shape
  560. *
  561. * @param object shape The shape to highlight
  562. */
  563. this.highlight =
  564. this.Highlight = function (shape)
  565. {
  566. // Add the new highlight
  567. RG.Highlight.Rect(this, shape);
  568. };
  569. /**
  570. * The getObjectByXY() worker method. Don't call this call:
  571. *
  572. * RGraph.ObjectRegistry.getObjectByXY(e)
  573. *
  574. * @param object e The event object
  575. */
  576. this.getObjectByXY = function (e)
  577. {
  578. var mouseXY = RG.getMouseXY(e);
  579. if (
  580. mouseXY[0] > this.gutterLeft
  581. && mouseXY[0] < (ca.width - this.gutterRight)
  582. && mouseXY[1] > this.gutterTop
  583. && mouseXY[1] < (ca.height - this.gutterBottom)
  584. ) {
  585. return this;
  586. }
  587. };
  588. /**
  589. * This method handles the adjusting calculation for when the mouse is moved
  590. *
  591. * @param object e The event object
  592. */
  593. this.adjusting_mousemove =
  594. this.Adjusting_mousemove = function (e)
  595. {
  596. /**
  597. * Handle adjusting for the HProgress
  598. */
  599. if (prop['chart.adjustable'] && RG.Registry.Get('chart.adjusting') && RG.Registry.Get('chart.adjusting').uid == this.uid) {
  600. var mouseXY = RG.getMouseXY(e);
  601. var value = this.getValue(e);
  602. if (typeof(value) == 'number') {
  603. this.value = Number(value.toFixed(prop['chart.scale.decimals']));
  604. RG.redrawCanvas(ca);
  605. // Fire the onadjust event
  606. RG.fireCustomEvent(this, 'onadjust');
  607. }
  608. }
  609. };
  610. /**
  611. * Draws chart.labels.specific
  612. */
  613. this.drawSpecificLabels =
  614. this.DrawSpecificLabels = function ()
  615. {
  616. var labels = prop['chart.labels.specific'];
  617. if (labels) {
  618. var font = prop['chart.text.font'];
  619. var size = prop['chart.text.size'];
  620. var valign = (prop['chart.labels.position'] == 'top' ? 'bottom' : 'top');
  621. var step = this.width / (labels.length - 1);
  622. co.beginPath();
  623. co.fillStyle = prop['chart.text.color'];
  624. for (var i=0; i<labels.length; ++i) {
  625. RG.Text2(this, {'font':font,
  626. 'size':size,
  627. 'x': this.gutterLeft + (step * i),
  628. 'y':prop['chart.labels.position'] == 'top' ? this.gutterTop - 7 : ca.height - this.gutterBottom + 7,
  629. 'text': labels[i],
  630. 'valign':valign,
  631. 'halign':'center',
  632. 'tag': 'labels.specific'
  633. });
  634. }
  635. co.fill();
  636. }
  637. };
  638. /**
  639. * This function positions a tooltip when it is displayed
  640. *
  641. * @param obj object The chart object
  642. * @param int x The X coordinate specified for the tooltip
  643. * @param int y The Y coordinate specified for the tooltip
  644. * @param objec tooltip The tooltips DIV element
  645. */
  646. this.positionTooltip = function (obj, x, y, tooltip, idx)
  647. {
  648. var coordX = obj.coords[tooltip.__index__][0];
  649. var coordY = obj.coords[tooltip.__index__][1];
  650. var coordW = obj.coords[tooltip.__index__][2];
  651. var coordH = obj.coords[tooltip.__index__][3];
  652. var canvasXY = RG.getCanvasXY(obj.canvas);
  653. var gutterLeft = this.gutterLeft;
  654. var gutterTop = this.gutterTop;
  655. var width = tooltip.offsetWidth;
  656. var height = tooltip.offsetHeight;
  657. // Set the top position
  658. tooltip.style.left = 0;
  659. tooltip.style.top = canvasXY[1] + coordY - height - 7 + 'px';
  660. // By default any overflow is hidden
  661. tooltip.style.overflow = '';
  662. // The arrow
  663. var img = new Image();
  664. img.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAFCAYAAACjKgd3AAAARUlEQVQYV2NkQAN79+797+RkhC4M5+/bd47B2dmZEVkBCgcmgcsgbAaA9GA1BCSBbhAuA/AagmwQPgMIGgIzCD0M0AMMAEFVIAa6UQgcAAAAAElFTkSuQmCC';
  665. img.style.position = 'absolute';
  666. img.id = '__rgraph_tooltip_pointer__';
  667. img.style.top = (tooltip.offsetHeight - 2) + 'px';
  668. tooltip.appendChild(img);
  669. // Reposition the tooltip if at the edges:
  670. // LEFT edge
  671. if ((canvasXY[0] + coordX + (coordW / 2) - (width / 2)) < 10) {
  672. tooltip.style.left = (canvasXY[0] + coordX - (width * 0.1)) + (coordW / 2) + 'px';
  673. img.style.left = ((width * 0.1) - 8.5) + 'px';
  674. // RIGHT edge
  675. } else if ((canvasXY[0] + (coordW / 2) + coordX + (width / 2)) > document.body.offsetWidth) {
  676. tooltip.style.left = canvasXY[0] + coordX - (width * 0.9) + (coordW / 2) + 'px';
  677. img.style.left = ((width * 0.9) - 8.5) + 'px';
  678. // Default positioning - CENTERED
  679. } else {
  680. tooltip.style.left = (canvasXY[0] + coordX + (coordW / 2) - (width * 0.5)) + 'px';
  681. img.style.left = ((width * 0.5) - 8.5) + 'px';
  682. }
  683. };
  684. /**
  685. * This function returns the appropriate X coordinate for the given value
  686. *
  687. * @param int value The value you want the coordinate for
  688. * @returm int The coordinate
  689. */
  690. this.getXCoord = function (value)
  691. {
  692. var min = this.min;
  693. if (value < min || value > this.max) {
  694. return null;
  695. }
  696. var barWidth = ca.width - this.gutterLeft - this.gutterRight;
  697. var coord = ((value - min) / (this.max - min)) * barWidth;
  698. coord = this.gutterLeft + coord;
  699. return coord;
  700. };
  701. /**
  702. * This returns true/false as to whether the cursor is over the chart area.
  703. * The cursor does not necessarily have to be over the bar itself.
  704. */
  705. this.overChartArea = function (e)
  706. {
  707. var mouseXY = RGraph.getMouseXY(e);
  708. var mouseX = mouseXY[0];
  709. var mouseY = mouseXY[1];
  710. if ( mouseX >= this.gutterLeft
  711. && mouseX <= (ca.width - this.gutterRight)
  712. && mouseY >= this.gutterTop
  713. && mouseY <= (ca.height - this.gutterBottom)
  714. ) {
  715. return true;
  716. }
  717. return false;
  718. };
  719. /**
  720. *
  721. */
  722. this.parseColors = function ()
  723. {
  724. // Save the original colors so that they can be restored when the canvas is reset
  725. if (this.original_colors.length === 0) {
  726. this.original_colors['chart.colors'] = RG.array_clone(prop['chart.colors']);
  727. this.original_colors['chart.tickmarks.color'] = RG.array_clone(prop['chart.tickmarks.color']);
  728. this.original_colors['chart.strokestyle.inner'] = RG.array_clone(prop['chart.strokestyle.inner']);
  729. this.original_colors['chart.strokestyle.outer'] = RG.array_clone(prop['chart.strokestyle.outer']);
  730. this.original_colors['chart.highlight.fill'] = RG.array_clone(prop['chart.highlight.fill']);
  731. this.original_colors['chart.highlight.stroke'] = RG.array_clone(prop['chart.highlight.stroke']);
  732. this.original_colors['chart.highlight.color'] = RG.array_clone(prop['chart.highlight.color']);
  733. }
  734. var colors = prop['chart.colors'];
  735. for (var i=0; i<colors.length; ++i) {
  736. colors[i] = this.parseSingleColorForGradient(colors[i]);
  737. }
  738. prop['chart.tickmarks.color'] = this.parseSingleColorForGradient(prop['chart.tickmarks.color']);
  739. prop['chart.strokestyle.inner'] = this.parseSingleColorForGradient(prop['chart.strokestyle.inner']);
  740. prop['chart.strokestyle.outer'] = this.parseSingleColorForGradient(prop['chart.strokestyle.outer']);
  741. prop['chart.highlight.fill'] = this.parseSingleColorForGradient(prop['chart.highlight.fill']);
  742. prop['chart.highlight.stroke'] = this.parseSingleColorForGradient(prop['chart.highlight.stroke']);
  743. prop['chart.background.color'] = this.parseSingleColorForGradient(prop['chart.background.color']);
  744. };
  745. /**
  746. * This parses a single color value
  747. */
  748. this.parseSingleColorForGradient = function (color)
  749. {
  750. if (!color || typeof(color) != 'string') {
  751. return color;
  752. }
  753. if (color.match(/^gradient\((.*)\)$/i)) {
  754. var parts = RegExp.$1.split(':');
  755. // Create the gradient
  756. var grad = co.createLinearGradient(prop['chart.gutter.left'],0,ca.width - prop['chart.gutter.right'],0);
  757. var diff = 1 / (parts.length - 1);
  758. grad.addColorStop(0, RG.trim(parts[0]));
  759. for (var j=1; j<parts.length; ++j) {
  760. grad.addColorStop(j * diff, RG.trim(parts[j]));
  761. }
  762. }
  763. return grad ? grad : color;
  764. };
  765. /**
  766. * Draws the bevel effect
  767. */
  768. this.drawBevel =
  769. this.DrawBevel = function ()
  770. {
  771. // In case of multiple segments - this adds up all the lengths
  772. for (var i=0,len=0; i<this.coords.length; ++i) len += this.coords[i][2];
  773. co.save();
  774. // Draw a path to clip to
  775. co.beginPath();
  776. co.rect(this.coords[0][0], this.coords[0][1], len, this.coords[0][3]);
  777. co.clip();
  778. // Now draw the rect with a shadow
  779. co.beginPath();
  780. co.shadowColor = 'black';
  781. co.shadowOffsetX = 0;
  782. co.shadowOffsetY = 0;
  783. co.shadowBlur = 15;
  784. co.lineWidth = 2;
  785. co.rect(this.coords[0][0] - 1, this.coords[0][1] - 1, len + 2, this.coords[0][3] + 2);
  786. co.stroke();
  787. co.restore();
  788. };
  789. /**
  790. * Draw the titles
  791. */
  792. this.drawTitle =
  793. this.DrawTitle = function ()
  794. {
  795. // Draw the title text
  796. if (prop['chart.title'].length) {
  797. var x = ((ca.width - this.gutterLeft - this.gutterRight) / 2) + this.gutterLeft;
  798. var text = prop['chart.title'];
  799. var size = prop['chart.title.size'] ? prop['chart.title.size'] : prop['chart.text.size'] + 2;
  800. var font = prop['chart.title.font'] ? prop['chart.title.font'] : prop['chart.text.font'];
  801. if (prop['chart.labels.position'] == 'top') {
  802. y = ca.height - this.gutterBottom +5;
  803. x = ((ca.width - this.gutterLeft - this.gutterRight) / 2) + this.gutterLeft;
  804. valign = 'top';
  805. } else {
  806. x = ((ca.width - this.gutterLeft - this.gutterRight) / 2) + this.gutterLeft;
  807. y = this.gutterTop - 5;
  808. valign = 'bottom';
  809. }
  810. RG.Text2(this, {'font':font,
  811. 'size':size,
  812. 'x': typeof(prop['chart.title.x']) == 'number' ? prop['chart.title.x'] : x,
  813. 'y': typeof(prop['chart.title.y']) == 'number' ? prop['chart.title.y'] : y,
  814. 'text': text,
  815. 'valign': prop['chart.title.valign'] ? prop['chart.title.valign'] : valign,
  816. 'halign': prop['chart.title.halign'] ? prop['chart.title.halign'] : 'center',
  817. 'bold':prop['chart.title.bold'],
  818. 'bounding': prop['chart.title.background'] ? true : false,
  819. 'boundingFill': prop['chart.title.background'],
  820. 'tag': 'title'
  821. });
  822. }
  823. };
  824. /**
  825. * This function handles highlighting an entire data-series for the interactive
  826. * key
  827. *
  828. * @param int index The index of the data series to be highlighted
  829. */
  830. this.interactiveKeyHighlight = function (index)
  831. {
  832. var coords = this.coords[index];
  833. co.beginPath();
  834. co.strokeStyle = prop['chart.key.interactive.highlight.chart.stroke'];
  835. co.lineWidth = 2;
  836. co.fillStyle = prop['chart.key.interactive.highlight.chart.fill'];
  837. co.rect(coords[0], coords[1], coords[2], coords[3]);
  838. co.fill();
  839. co.stroke();
  840. // Reset the linewidth
  841. co.lineWidth = 1;
  842. };
  843. /**
  844. * Using a function to add events makes it easier to facilitate method chaining
  845. *
  846. * @param string type The type of even to add
  847. * @param function func
  848. */
  849. this.on = function (type, func)
  850. {
  851. if (type.substr(0,2) !== 'on') {
  852. type = 'on' + type;
  853. }
  854. this[type] = func;
  855. return this;
  856. };
  857. /**
  858. * This function runs once only
  859. * (put at the end of the file (before any effects))
  860. */
  861. this.firstDrawFunc = function ()
  862. {
  863. };
  864. /**
  865. * HProgress Grow effect (which is also the VPogress Grow effect)
  866. *
  867. * @param object obj The chart object
  868. */
  869. this.grow = function ()
  870. {
  871. var obj = this;
  872. var canvas = obj.canvas;
  873. var context = obj.context;
  874. var initial_value = obj.currentValue;
  875. var opt = arguments[0] || {};
  876. var numFrames = opt.frames || 30;
  877. var frame = 0
  878. var callback = arguments[1] || function () {};
  879. if (typeof obj.value === 'object') {
  880. if (RG.is_null(obj.currentValue)) {
  881. obj.currentValue = [];
  882. for (var i=0,len=obj.value.length; i<len; ++i) {
  883. obj.currentValue[i] = 0;
  884. }
  885. }
  886. var diff = [];
  887. var increment = [];
  888. for (var i=0,len=obj.value.length; i<len; ++i) {
  889. diff[i] = obj.value[i] - Number(obj.currentValue[i]);
  890. increment[i] = diff[i] / numFrames;
  891. }
  892. if (initial_value == null) {
  893. initial_value = [];
  894. for (var i=0,len=obj.value.length; i<len; ++i) {
  895. initial_value[i] = 0;
  896. }
  897. }
  898. } else {
  899. var diff = obj.value - Number(obj.currentValue);
  900. var increment = diff / numFrames;
  901. }
  902. function iterator ()
  903. {
  904. frame++;
  905. if (frame <= numFrames) {
  906. if (typeof obj.value == 'object') {
  907. obj.value = [];
  908. for (var i=0,len=initial_value.length; i<len; ++i) {
  909. obj.value[i] = initial_value[i] + (increment[i] * frame);
  910. }
  911. } else {
  912. obj.value = initial_value + (increment * frame);
  913. }
  914. RGraph.clear(obj.canvas);
  915. RGraph.redrawCanvas(obj.canvas);
  916. RGraph.Effects.updateCanvas(iterator);
  917. } else {
  918. callback();
  919. }
  920. }
  921. iterator();
  922. return this;
  923. };
  924. /**
  925. * Register the object for redrawing
  926. */
  927. RG.Register(this);
  928. };