RGraph.vprogress.js 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080
  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.txt |
  12. * o------------------------------------------------------------------------------o
  13. */
  14. if (typeof(RGraph) == 'undefined') RGraph = {};
  15. /**
  16. * The progress bar constructor
  17. *
  18. * @param int id The ID of the canvas tag
  19. * @param int value The indicated value of the meter.
  20. * @param int max The end value (the upper most) of the meter
  21. */
  22. RGraph.VProgress = function (id, value, max)
  23. {
  24. this.id = id;
  25. this.max = max;
  26. this.value = value;
  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 = 'vprogress';
  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. /**
  39. * Compatibility with older browsers
  40. */
  41. RGraph.OldBrowserCompat(this.context);
  42. this.properties = {
  43. 'chart.colors': ['Gradient(white:#0c0)','Gradient(white:red)','Gradient(white:green)','yellow','pink','cyan','black','white','gray'],
  44. 'chart.strokestyle.inner': '#999',
  45. 'chart.strokestyle.outer': '#999',
  46. 'chart.tickmarks': true,
  47. 'chart.tickmarks.zerostart':true,
  48. 'chart.tickmarks.color': '#999',
  49. 'chart.tickmarks.inner': false,
  50. 'chart.gutter.left': 25,
  51. 'chart.gutter.right': 25,
  52. 'chart.gutter.top': 25,
  53. 'chart.gutter.bottom': 25,
  54. 'chart.numticks': 10,
  55. 'chart.numticks.inner': 50,
  56. 'chart.background.color': '#eee',
  57. 'chart.shadow': false,
  58. 'chart.shadow.color': 'rgba(0,0,0,0.5)',
  59. 'chart.shadow.blur': 3,
  60. 'chart.shadow.offsetx': 3,
  61. 'chart.shadow.offsety': 3,
  62. 'chart.title': '',
  63. 'chart.title.bold': true,
  64. 'chart.title.font': null,
  65. 'chart.title.size': null,
  66. 'chart.title.color': 'black',
  67. 'chart.title.side': null,
  68. 'chart.title.side.font': 'Arial',
  69. 'chart.title.side.size': 12,
  70. 'chart.title.side.color': 'black',
  71. 'chart.title.side.bold': true,
  72. 'chart.text.size': 10,
  73. 'chart.text.color': 'black',
  74. 'chart.text.font': 'Arial',
  75. 'chart.contextmenu': null,
  76. 'chart.units.pre': '',
  77. 'chart.units.post': '',
  78. 'chart.tooltips': null,
  79. 'chart.tooltips.effect': 'fade',
  80. 'chart.tooltips.css.class': 'RGraph_tooltip',
  81. 'chart.tooltips.highlight': true,
  82. 'chart.tooltips.event': 'onclick',
  83. 'chart.highlight.stroke': 'rgba(0,0,0,0)',
  84. 'chart.highlight.fill': 'rgba(255,255,255,0.7)',
  85. 'chart.annotatable': false,
  86. 'chart.annotate.color': 'black',
  87. 'chart.zoom.factor': 1.5,
  88. 'chart.zoom.fade.in': true,
  89. 'chart.zoom.fade.out': true,
  90. 'chart.zoom.hdir': 'right',
  91. 'chart.zoom.vdir': 'down',
  92. 'chart.zoom.frames': 25,
  93. 'chart.zoom.delay': 16.666,
  94. 'chart.zoom.shadow': true,
  95. 'chart.zoom.background': true,
  96. 'chart.zoom.action': 'zoom',
  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.label.inner': false,
  103. 'chart.labels.count': 10,
  104. 'chart.labels.position': 'right',
  105. 'chart.adjustable': false,
  106. 'chart.min': 0,
  107. 'chart.scale.decimals': 0,
  108. 'chart.scale.thousand': ',',
  109. 'chart.scale.point': '.',
  110. 'chart.key': null,
  111. 'chart.key.background': 'white',
  112. 'chart.key.position': 'graph',
  113. 'chart.key.halign': 'right',
  114. 'chart.key.shadow': false,
  115. 'chart.key.shadow.color': '#666',
  116. 'chart.key.shadow.blur': 3,
  117. 'chart.key.shadow.offsetx': 2,
  118. 'chart.key.shadow.offsety': 2,
  119. 'chart.key.position.gutter.boxed': false,
  120. 'chart.key.position.x': null,
  121. 'chart.key.position.y': null,
  122. 'chart.key.color.shape': 'square',
  123. 'chart.key.rounded': true,
  124. 'chart.key.linewidth': 1,
  125. 'chart.key.colors': null,
  126. 'chart.key.interactive': false,
  127. 'chart.key.interactive.highlight.chart.stroke': '#000',
  128. 'chart.key.interactive.highlight.chart.fill': 'rgba(255,255,255,0.7)',
  129. 'chart.key.interactive.highlight.label': 'rgba(255,0,0,0.2)',
  130. 'chart.key.text.color': 'black',
  131. 'chart.events.click': null,
  132. 'chart.events.mousemove': null,
  133. 'chart.border.inner': true
  134. }
  135. /**
  136. * Allow for new style method of passing arguments to the constructor
  137. */
  138. if (arguments.length == 4) {
  139. this.min = arguments[1];
  140. this.max = arguments[2];
  141. this.value = arguments[3];
  142. this.properties['chart.min'] = arguments[1];
  143. } else if (arguments.length == 3) {
  144. this.min = 0;
  145. this.max = arguments[2];
  146. this.value = arguments[1];
  147. this.properties['chart.min'] = 0;
  148. }
  149. // Check for support
  150. if (!this.canvas) {
  151. alert('[PROGRESS] No canvas support');
  152. return;
  153. }
  154. /**
  155. * Create the dollar objects so that functions can be added to them
  156. */
  157. var linear_data = RGraph.array_linearize(value);
  158. for (var i=0; i<linear_data.length; ++i) {
  159. this['$' + i] = {};
  160. }
  161. /**
  162. * Translate half a pixel for antialiasing purposes - but only if it hasn't beeen
  163. * done already
  164. */
  165. if (!this.canvas.__rgraph_aa_translated__) {
  166. this.context.translate(0.5,0.5);
  167. this.canvas.__rgraph_aa_translated__ = true;
  168. }
  169. ///////////////////////////////// SHORT PROPERTIES /////////////////////////////////
  170. var RG = RGraph;
  171. var ca = this.canvas;
  172. var co = ca.getContext('2d');
  173. var prop = this.properties;
  174. //////////////////////////////////// METHODS ///////////////////////////////////////
  175. /**
  176. * A generic setter
  177. *
  178. * @param string name The name of the property to set
  179. * @param string value The value of the poperty
  180. */
  181. this.Set = function (name, value)
  182. {
  183. /**
  184. * This should be done first - prepend the propertyy name with "chart." if necessary
  185. */
  186. if (name.substr(0,6) != 'chart.') {
  187. name = 'chart.' + name;
  188. }
  189. /**
  190. * chart.strokestyle now sets both chart.strokestyle.inner and chart.strokestyle.outer
  191. */
  192. if (name == 'chart.strokestyle') {
  193. prop['chart.strokestyle.inner'] = value;
  194. prop['chart.strokestyle.outer'] = value;
  195. return;
  196. }
  197. prop[name.toLowerCase()] = value;
  198. return this;
  199. }
  200. /**
  201. * A generic getter
  202. *
  203. * @param string name The name of the property to get
  204. */
  205. this.Get = function (name)
  206. {
  207. /**
  208. * This should be done first - prepend the property name with "chart." if necessary
  209. */
  210. if (name.substr(0,6) != 'chart.') {
  211. name = 'chart.' + name;
  212. }
  213. return prop[name.toLowerCase()];
  214. }
  215. /**
  216. * Draws the progress bar
  217. */
  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 indiviual 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.Drawbar();
  249. this.DrawTickMarks();
  250. this.DrawLabels();
  251. this.DrawTitles();
  252. co.stroke();
  253. co.fill();
  254. /**
  255. * Draw the bevel effect if requested
  256. */
  257. if (prop['chart.bevel']) {
  258. this.DrawBevel();
  259. }
  260. /**
  261. * Setup the context menu if required
  262. */
  263. if (prop['chart.contextmenu']) {
  264. RG.ShowContext(this);
  265. }
  266. /**
  267. * This installs the event listeners
  268. */
  269. RG.InstallEventListeners(this);
  270. // Draw a key if necessary
  271. if (prop['chart.key'] && prop['chart.key'].length) {
  272. RG.DrawKey(this, prop['chart.key'], prop['chart.colors']);
  273. }
  274. /**
  275. * This function enables resizing
  276. */
  277. if (prop['chart.resizable']) {
  278. RG.AllowResizing(this);
  279. }
  280. /**
  281. * Instead of using RGraph.common.adjusting.js, handle them here
  282. */
  283. this.AllowAdjusting();
  284. /**
  285. * Fire the RGraph ondraw event
  286. */
  287. RG.FireCustomEvent(this, 'ondraw');
  288. return this;
  289. }
  290. /**
  291. * Draw the bar itself
  292. */
  293. this.Drawbar = function ()
  294. {
  295. /**
  296. * First get the scale
  297. */
  298. this.scale2 = RGraph.getScale2(this, {
  299. 'max':this.max,
  300. 'min':this.min,
  301. 'strict':true,
  302. 'scale.thousand':prop['chart.scale.thousand'],
  303. 'scale.point':prop['chart.scale.point'],
  304. 'scale.decimals':prop['chart.scale.decimals'],
  305. 'ylabels.count':prop['chart.labels.count'],
  306. 'scale.round':prop['chart.scale.round'],
  307. 'units.pre': prop['chart.units.pre'],
  308. 'units.post': prop['chart.units.post']
  309. });
  310. // Set a shadow if requested
  311. if (prop['chart.shadow']) {
  312. RG.SetShadow(this, prop['chart.shadow.color'], prop['chart.shadow.offsetx'], prop['chart.shadow.offsety'], prop['chart.shadow.blur']);
  313. }
  314. // Draw the shadow for MSIE
  315. if (ISOLD && prop['chart.shadow']) {
  316. co.fillStyle = prop['chart.shadow.color'];
  317. co.fillRect(this.gutterLeft + prop['chart.shadow.offsetx'], this.gutterTop + prop['chart.shadow.offsety'], this.width, this.height);
  318. }
  319. // Draw the outline
  320. co.fillStyle = prop['chart.background.color'];
  321. co.strokeStyle = prop['chart.strokestyle.outer'];
  322. co.strokeRect(this.gutterLeft, this.gutterTop, this.width, this.height);
  323. co.fillRect(this.gutterLeft, this.gutterTop, this.width, this.height);
  324. // Turn off any shadow
  325. RG.NoShadow(this);
  326. co.strokeStyle = prop['chart.strokestyle.outer'];
  327. co.fillStyle = prop['chart.colors'][0];
  328. var margin = prop['chart.margin'];
  329. var barHeight = (ca.height - this.gutterTop - this.gutterBottom) * (RG.array_sum(this.value) / this.max);
  330. // Draw the actual bar itself
  331. if (typeof(this.value) == 'number') {
  332. co.lineWidth = 1;
  333. co.strokeStyle = prop['chart.strokestyle.inner'];
  334. } else if (typeof(this.value) == 'object') {
  335. co.beginPath();
  336. co.strokeStyle = prop['chart.strokestyle.inner'];
  337. var startPoint = ca.height - this.gutterBottom;
  338. for (var i=0; i<this.value.length; ++i) {
  339. var segmentHeight = ( (this.value[i] - prop['chart.min']) / (this.max - prop['chart.min']) ) * (ca.height - this.gutterBottom - this.gutterTop);
  340. co.fillStyle = prop['chart.colors'][i];
  341. if (prop['chart.border.inner']) {
  342. co.strokeRect(this.gutterLeft + margin, startPoint - segmentHeight, this.width - margin - margin, segmentHeight);
  343. }
  344. co.fillRect(this.gutterLeft + margin, startPoint - segmentHeight, this.width - margin - margin, segmentHeight);
  345. // Store the coords
  346. this.coords.push([this.gutterLeft + margin, startPoint - segmentHeight, this.width - margin - margin, segmentHeight]);
  347. startPoint -= segmentHeight;
  348. }
  349. co.stroke();
  350. co.fill();
  351. }
  352. /**
  353. * Inner tickmarks
  354. */
  355. if (prop['chart.tickmarks.inner']) {
  356. var spacing = (ca.height - this.gutterTop - this.gutterBottom) / prop['chart.numticks.inner'];
  357. co.lineWidth = 1;
  358. co.strokeStyle = prop['chart.strokestyle.outer'];
  359. co.beginPath();
  360. for (var y = this.gutterTop; y<ca.height - this.gutterBottom; y+=spacing) {
  361. co.moveTo(this.gutterLeft, Math.round(y));
  362. co.lineTo(this.gutterLeft + 3, Math.round(y));
  363. co.moveTo(ca.width - this.gutterRight, Math.round(y));
  364. co.lineTo(ca.width - this.gutterRight - 3, Math.round(y));
  365. }
  366. co.stroke();
  367. }
  368. co.beginPath();
  369. co.strokeStyle = prop['chart.strokestyle.inner'];
  370. if (typeof(this.value) == 'number') {
  371. if (prop['chart.border.inner']) {
  372. co.strokeRect(this.gutterLeft + margin, this.gutterTop + this.height - barHeight, this.width - margin - margin, barHeight);
  373. }
  374. co.fillRect(this.gutterLeft + margin, this.gutterTop + this.height - barHeight, this.width - margin - margin, barHeight);
  375. // Store the coords
  376. this.coords.push([this.gutterLeft + margin, this.gutterTop + this.height - barHeight, this.width - margin - margin, barHeight]);
  377. }
  378. /**
  379. * Draw the arrows indicating the level if requested
  380. */
  381. if (prop['chart.arrows']) {
  382. var x = this.gutterLeft - 4;
  383. var y = ca.height - this.gutterBottom - barHeight;
  384. co.lineWidth = 1;
  385. co.fillStyle = 'black';
  386. co.strokeStyle = 'black';
  387. co.beginPath();
  388. co.moveTo(x, y);
  389. co.lineTo(x - 4, y - 2);
  390. co.lineTo(x - 4, y + 2);
  391. co.closePath();
  392. co.stroke();
  393. co.fill();
  394. x += this.width + 8;
  395. co.beginPath();
  396. co.moveTo(x, y);
  397. co.lineTo(x + 4, y - 2);
  398. co.lineTo(x + 4, y + 2);
  399. co.closePath();
  400. co.stroke();
  401. co.fill();
  402. }
  403. /**
  404. * Draw the "in-bar" label
  405. */
  406. if (prop['chart.label.inner']) {
  407. co.fillStyle = 'black';
  408. RG.Text2(this, {'font':prop['chart.text.font'],
  409. 'size':prop['chart.text.size'],
  410. 'x':((ca.width - this.gutterLeft - this.gutterRight) / 2) + this.gutterLeft,'y':this.coords[this.coords.length - 1][1] - 5,'text':RGraph.number_format(this, (typeof(this.value) == 'number' ? this.value : RG.array_sum(this.value)).toFixed(prop['chart.scale.decimals'])),
  411. 'valign':'bottom',
  412. 'halign':'center',
  413. 'bounding':true,
  414. 'boundingFill':'white',
  415. 'tag': 'label.inner'
  416. });
  417. }
  418. }
  419. /**
  420. * The function that draws the tick marks.
  421. */
  422. this.DrawTickMarks = function ()
  423. {
  424. co.strokeStyle = prop['chart.tickmarks.color'];
  425. if (prop['chart.tickmarks']) {
  426. co.beginPath();
  427. for (var i=0; prop['chart.tickmarks.zerostart'] ? i<=prop['chart.numticks'] : i<prop['chart.numticks']; i++) {
  428. var startX = prop['chart.labels.position'] == 'left' ? this.gutterLeft : ca.width - prop['chart.gutter.right'];
  429. var endX = prop['chart.labels.position'] == 'left' ? startX - 4 : startX + 4;
  430. var yPos = (this.height * (i / prop['chart.numticks'])) + this.gutterTop
  431. co.moveTo(startX, Math.round(yPos));
  432. co.lineTo(endX, Math.round(yPos));
  433. }
  434. co.stroke();
  435. }
  436. }
  437. /**
  438. * The function that draws the labels
  439. */
  440. this.DrawLabels = function ()
  441. {
  442. if (!RG.is_null(prop['chart.labels.specific'])) {
  443. return this.DrawSpecificLabels();
  444. }
  445. co.fillStyle = prop['chart.text.color'];
  446. var position = prop['chart.labels.position'];
  447. var xAlignment = position == 'left' ? 'right' : 'left';
  448. var yAlignment = 'center';
  449. var count = prop['chart.labels.count'];
  450. var units_pre = prop['chart.units.pre'];
  451. var units_post = prop['chart.units.post'];
  452. var text_size = prop['chart.text.size'];
  453. var text_font = prop['chart.text.font'];
  454. var decimals = prop['chart.scale.decimals'];
  455. if (prop['chart.tickmarks']) {
  456. for (var i=0; i<count ; ++i) {
  457. RG.Text2(this, {'font':text_font,
  458. 'size':text_size,
  459. 'x':position == 'left' ? (this.gutterLeft - 7) : (ca.width - this.gutterRight + 7),
  460. 'y':(((ca.height - this.gutterTop - this.gutterBottom) / count) * i) + this.gutterTop,
  461. 'text':this.scale2.labels[this.scale2.labels.length - (i+1)],
  462. 'valign':yAlignment,
  463. 'halign':xAlignment,
  464. 'tag': 'scale'
  465. });
  466. }
  467. /**
  468. * Show zero?
  469. */
  470. if (prop['chart.tickmarks.zerostart'] && prop['chart.min'] == 0) {
  471. RG.Text2(this, {'font':text_font,
  472. 'size':text_size,
  473. 'x':position == 'left' ? (this.gutterLeft - 5) : (ca.width - this.gutterRight + 5),
  474. 'y':ca.height - this.gutterBottom,'text':RG.number_format(this, prop['chart.min'].toFixed(decimals), units_pre, units_post),
  475. 'valign':yAlignment,
  476. 'halign':xAlignment,
  477. 'tag': 'scale'
  478. });
  479. }
  480. /**
  481. * chart.ymin is set
  482. */
  483. if (prop['chart.min'] != 0) {
  484. RG.Text2(this, {'font':text_font,
  485. 'size':text_size,
  486. 'x':position == 'left' ? (this.gutterLeft - 5) : (ca.width - this.gutterRight + 5),
  487. 'y':ca.height - this.gutterBottom,
  488. 'text':RG.number_format(this, prop['chart.min'].toFixed(decimals), units_pre, units_post),
  489. 'valign':yAlignment,
  490. 'halign':xAlignment,
  491. 'tag': 'scale'
  492. });
  493. }
  494. }
  495. }
  496. /**
  497. * Draws titles
  498. */
  499. this.DrawTitles = function ()
  500. {
  501. var text_size = prop['chart.text.size'];
  502. var text_font = prop['chart.text.font'];
  503. var title_size = prop['chart.title.size'] ? prop['chart.title.size'] : text_size + 2;
  504. // Draw the title text
  505. if (prop['chart.title'].length > 0) {
  506. co.fillStyle = prop['chart.title.color'];
  507. RG.Text2(this, {'font':prop['chart.title.font'] ? prop['chart.title.font'] : text_font,
  508. 'size':title_size,
  509. 'x':this.gutterLeft + ((ca.width - this.gutterLeft - this.gutterRight) / 2),
  510. 'y':this.gutterTop - 5,
  511. 'text':prop['chart.title'],
  512. 'valign':'bottom',
  513. 'halign':'center',
  514. 'bold': prop['chart.title.bold'],
  515. 'tag': 'title'
  516. });
  517. }
  518. // Draw side title
  519. if (typeof(prop['chart.title.side']) == 'string') {
  520. co.fillStyle = prop['chart.title.side.color'];
  521. RG.Text2(this, {'font':prop['chart.title.side.font'],
  522. 'size':prop['chart.title.side.size'],
  523. 'x':prop['chart.labels.position'] == 'right' ? this.gutterLeft - 10 : (ca.width - this.gutterRight) + 10,
  524. 'y':this.gutterTop + (this.height / 2),
  525. 'text': prop['chart.title.side'],
  526. 'valign':'bottom',
  527. 'halign':'center',
  528. 'angle': prop['chart.labels.position'] == 'right' ? 270 : 90,
  529. 'bold': prop['chart.title.side.bold'],
  530. 'tag': 'title.side'
  531. });
  532. }
  533. }
  534. /**
  535. * Returns the focused bar
  536. *
  537. * @param event e The event object
  538. */
  539. this.getShape =
  540. this.getBar = function (e)
  541. {
  542. var mouseCoords = RG.getMouseXY(e)
  543. for (var i=0; i<this.coords.length; i++) {
  544. var mouseCoords = RG.getMouseXY(e);
  545. var mouseX = mouseCoords[0];
  546. var mouseY = mouseCoords[1];
  547. var left = this.coords[i][0];
  548. var top = this.coords[i][1];
  549. var width = this.coords[i][2];
  550. var height = this.coords[i][3];
  551. var idx = i;
  552. if (mouseX >= left && mouseX <= (left + width) && mouseY >= top && mouseY <= (top + height) ) {
  553. var tooltip = RG.parseTooltipText(prop['chart.tooltips'], i);
  554. return {0: this, 'object': this,
  555. 1: left, 'x': left,
  556. 2: top, 'y': top,
  557. 3: width, 'width': width,
  558. 4: height, 'height': height,
  559. 5: i, 'index': i,
  560. 'tooltip': tooltip };
  561. }
  562. }
  563. }
  564. /**
  565. * This function returns the value that the mouse is positioned at, regardless of
  566. * the actual indicated value.
  567. *
  568. * @param object e The event object
  569. */
  570. this.getValue = function (e)
  571. {
  572. var mouseCoords = RG.getMouseXY(e);
  573. var mouseX = mouseCoords[0];
  574. var mouseY = mouseCoords[1];
  575. var value = (this.height - (mouseY - this.gutterTop)) / this.height;
  576. value *= this.max - prop['chart.min'];
  577. value += prop['chart.min'];
  578. // Bounds checking
  579. if (value > this.max) value = this.max;
  580. if (value < this.min) value = this.min;
  581. return value;
  582. }
  583. /**
  584. * Each object type has its own Highlight() function which highlights the appropriate shape
  585. *
  586. * @param object shape The shape to highlight
  587. */
  588. this.Highlight = function (shape)
  589. {
  590. // Add the new highlight
  591. RG.Highlight.Rect(this, shape);
  592. }
  593. /**
  594. * The getObjectByXY() worker method. Don't call this call:
  595. *
  596. * RGraph.ObjectRegistry.getObjectByXY(e)
  597. *
  598. * @param object e The event object
  599. */
  600. this.getObjectByXY = function (e)
  601. {
  602. var mouseXY = RG.getMouseXY(e);
  603. if (
  604. mouseXY[0] > this.gutterLeft
  605. && mouseXY[0] < (ca.width - this.gutterRight)
  606. && mouseXY[1] >= this.gutterTop
  607. && mouseXY[1] <= (ca.height - this.gutterBottom)
  608. ) {
  609. return this;
  610. }
  611. }
  612. /**
  613. * This function allows the VProgress to be adjustable.
  614. */
  615. this.AllowAdjusting = function () {return;}
  616. /**
  617. * This method handles the adjusting calculation for when the mouse is moved
  618. *
  619. * @param object e The event object
  620. */
  621. this.Adjusting_mousemove = function (e)
  622. {
  623. /**
  624. * Handle adjusting for the HProgress
  625. */
  626. if (prop['chart.adjustable'] && RG.Registry.Get('chart.adjusting') && RG.Registry.Get('chart.adjusting').uid == this.uid) {
  627. var mouseXY = RG.getMouseXY(e);
  628. var value = this.getValue(e);
  629. if (typeof(value) == 'number') {
  630. // Fire the onadjust event
  631. RG.FireCustomEvent(this, 'onadjust');
  632. this.value = Number(value.toFixed(prop['chart.scale.decimals']));
  633. RG.Redraw();
  634. }
  635. }
  636. }
  637. /**
  638. * Draws chart.labels.specific
  639. */
  640. this.DrawSpecificLabels = function ()
  641. {
  642. var labels = prop['chart.labels.specific'];
  643. if (labels) {
  644. var font = prop['chart.text.font'];
  645. var size = prop['chart.text.size'];
  646. var halign = prop['chart.labels.position'] == 'right' ? 'left' : 'right';
  647. var step = this.height / (labels.length - 1);
  648. co.beginPath();
  649. co.fillStyle = prop['chart.text.color'];
  650. for (var i=0; i<labels.length; ++i) {
  651. RG.Text2(this,{'font':font,
  652. 'size':size,
  653. 'x': prop['chart.labels.position'] == 'right' ? ca.width - this.gutterRight + 7 : this.gutterLeft - 7,
  654. 'y':(this.height + this.gutterTop) - (step * i),
  655. 'text':labels[i],
  656. 'valign':'center',
  657. 'halign':halign,
  658. 'tag': 'labels.specific'
  659. });
  660. }
  661. co.fill();
  662. }
  663. }
  664. /**
  665. * This function positions a tooltip when it is displayed
  666. *
  667. * @param obj object The chart object
  668. * @param int x The X coordinate specified for the tooltip
  669. * @param int y The Y coordinate specified for the tooltip
  670. * @param objec tooltip The tooltips DIV element
  671. */
  672. this.positionTooltip = function (obj, x, y, tooltip, idx)
  673. {
  674. var coordX = obj.coords[tooltip.__index__][0];
  675. var coordY = obj.coords[tooltip.__index__][1];
  676. var coordW = obj.coords[tooltip.__index__][2];
  677. var coordH = obj.coords[tooltip.__index__][3];
  678. var canvasXY = RG.getCanvasXY(obj.canvas);
  679. var gutterLeft = obj.gutterLeft;
  680. var gutterTop = obj.gutterTop;
  681. var width = tooltip.offsetWidth;
  682. var height = tooltip.offsetHeight;
  683. // Set the top position
  684. tooltip.style.left = 0;
  685. tooltip.style.top = canvasXY[1] + coordY - height - 7 + 'px';
  686. // By default any overflow is hidden
  687. tooltip.style.overflow = '';
  688. // The arrow
  689. var img = new Image();
  690. img.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAFCAYAAACjKgd3AAAARUlEQVQYV2NkQAN79+797+RkhC4M5+/bd47B2dmZEVkBCgcmgcsgbAaA9GA1BCSBbhAuA/AagmwQPgMIGgIzCD0M0AMMAEFVIAa6UQgcAAAAAElFTkSuQmCC';
  691. img.style.position = 'absolute';
  692. img.id = '__rgraph_tooltip_pointer__';
  693. img.style.top = (tooltip.offsetHeight - 2) + 'px';
  694. tooltip.appendChild(img);
  695. // Reposition the tooltip if at the edges:
  696. // LEFT edge
  697. if ((canvasXY[0] + coordX + (coordW / 2) - (width / 2)) < 10) {
  698. tooltip.style.left = (canvasXY[0] + coordX - (width * 0.1)) + (coordW / 2) + 'px';
  699. img.style.left = ((width * 0.1) - 8.5) + 'px';
  700. // RIGHT edge
  701. } else if ((canvasXY[0] + coordX + (coordW / 2) + (width / 2)) > document.body.offsetWidth) {
  702. tooltip.style.left = canvasXY[0] + coordX - (width * 0.9) + (coordW / 2) + 'px';
  703. img.style.left = ((width * 0.9) - 8.5) + 'px';
  704. // Default positioning - CENTERED
  705. } else {
  706. tooltip.style.left = (canvasXY[0] + coordX + (coordW / 2) - (width * 0.5)) + 'px';
  707. img.style.left = ((width * 0.5) - 8.5) + 'px';
  708. }
  709. }
  710. /**
  711. * This function returns the appropriate Y coordinate for the given Y value
  712. *
  713. * @param int value The Y value you want the coordinate for
  714. * @returm int The coordinate
  715. */
  716. this.getYCoord = function (value)
  717. {
  718. if (value > this.max || value < prop['chart.min']) {
  719. return null;
  720. }
  721. var barHeight = ca.height - prop['chart.gutter.top'] - prop['chart.gutter.bottom'];
  722. var coord = ((value - prop['chart.min']) / (this.max - prop['chart.min'])) * barHeight;
  723. coord = ca.height - coord - prop['chart.gutter.bottom'];
  724. return coord;
  725. }
  726. /**
  727. * This returns true/false as to whether the cursor is over the chart area.
  728. * The cursor does not necessarily have to be over the bar itself.
  729. */
  730. this.overChartArea = function (e)
  731. {
  732. var mouseXY = RGraph.getMouseXY(e);
  733. var mouseX = mouseXY[0];
  734. var mouseY = mouseXY[1];
  735. if ( mouseX >= this.gutterLeft
  736. && mouseX <= (ca.width - this.gutterRight)
  737. && mouseY >= this.gutterTop
  738. && mouseY <= (ca.height - this.gutterBottom)
  739. ) {
  740. return true;
  741. }
  742. return false;
  743. }
  744. /**
  745. *
  746. */
  747. this.parseColors = function ()
  748. {
  749. var colors = prop['chart.colors'];
  750. for (var i=0; i<colors.length; ++i) {
  751. colors[i] = this.parseSingleColorForGradient(colors[i]);
  752. }
  753. }
  754. /**
  755. * This parses a single color value
  756. */
  757. this.parseSingleColorForGradient = function (color)
  758. {
  759. if (!color || typeof(color) != 'string') {
  760. return color;
  761. }
  762. if (color.match(/^gradient\((.*)\)$/i)) {
  763. var parts = RegExp.$1.split(':');
  764. // Create the gradient
  765. var grad = co.createLinearGradient(0, ca.height - prop['chart.gutter.bottom'], 0, prop['chart.gutter.top']);
  766. var diff = 1 / (parts.length - 1);
  767. grad.addColorStop(0, RG.trim(parts[0]));
  768. for (var j=1; j<parts.length; ++j) {
  769. grad.addColorStop(j * diff, RG.trim(parts[j]));
  770. }
  771. return grad ? grad : color;
  772. }
  773. return grad ? grad : color;
  774. }
  775. /**
  776. * Draws the bevel effect
  777. */
  778. this.DrawBevel = function ()
  779. {
  780. // In case of multiple segments - this adds up all the lengths
  781. for (var i=0,len=0; i<this.coords.length; ++i) len += this.coords[i][3];
  782. co.save();
  783. // Draw a path to clip to
  784. co.beginPath();
  785. co.rect(this.coords[0][0], this.coords[this.coords.length - 1][1] - 1, this.coords[0][2], len);
  786. co.clip();
  787. // Now draw the rect with a shadow
  788. co.beginPath();
  789. co.shadowColor = 'black';
  790. co.shadowOffsetX = 0;
  791. co.shadowOffsetY = 0;
  792. co.shadowBlur = 15;
  793. co.lineWidth = 2;
  794. co.rect(this.coords[0][0] - 1, this.coords[this.coords.length - 1][1] - 1, this.coords[0][2] + 2, len + 2);
  795. co.stroke();
  796. co.restore();
  797. }
  798. /**
  799. * This function handles highlighting an entire data-series for the interactive
  800. * key
  801. *
  802. * @param int index The index of the data series to be highlighted
  803. */
  804. this.interactiveKeyHighlight = function (index)
  805. {
  806. var coords = this.coords[index];
  807. co.beginPath();
  808. co.strokeStyle = prop['chart.key.interactive.highlight.chart.stroke'];
  809. co.lineWidth = 2;
  810. co.fillStyle = prop['chart.key.interactive.highlight.chart.fill'];
  811. co.rect(coords[0], coords[1], coords[2], coords[3]);
  812. co.fill();
  813. co.stroke();
  814. // Reset the linewidth
  815. co.lineWidth = 1;
  816. }
  817. /**
  818. * The chart is now always registered
  819. */
  820. RG.Register(this);
  821. }