RGraph.vprogress.js 42 KB

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