RGraph.hbar.js 66 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662
  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 horizontal bar chart constructor. The horizontal bar is a minor variant
  14. * on the bar chart. If you have big labels, this may be useful as there is usually
  15. * more space available for them.
  16. *
  17. * @param object canvas The canvas object
  18. * @param array data The chart data
  19. */
  20. RGraph.HBar = function (id, data)
  21. {
  22. var tmp = RGraph.getCanvasTag(id);
  23. // Get the canvas and context objects
  24. this.id = tmp[0];
  25. this.canvas = tmp[1];
  26. this.context = this.canvas.getContext ? this.canvas.getContext("2d", {alpha: (typeof id === 'object' && id.alpha === false) ? false : true}) : null;
  27. this.canvas.__object__ = this;
  28. this.data = data;
  29. this.type = 'hbar';
  30. this.isRGraph = true;
  31. this.uid = RGraph.CreateUID();
  32. this.canvas.uid = this.canvas.uid ? this.canvas.uid : RGraph.CreateUID();
  33. this.colorsParsed = false;
  34. this.coords = [];
  35. this.coords2 = [];
  36. this.coordsText = [];
  37. this.original_colors = [];
  38. this.firstDraw = true; // After the first draw this will be false
  39. /**
  40. * Compatibility with older browsers
  41. */
  42. //RGraph.OldBrowserCompat(this.context);
  43. this.max = 0;
  44. this.stackedOrGrouped = false;
  45. // Default properties
  46. this.properties =
  47. {
  48. 'chart.gutter.left': 75,
  49. 'chart.gutter.right': 25,
  50. 'chart.gutter.top': 25,
  51. 'chart.gutter.bottom': 25,
  52. 'chart.background.grid': true,
  53. 'chart.background.grid.color': '#ddd',
  54. 'chart.background.grid.width': 1,
  55. 'chart.background.grid.hsize': 25,
  56. 'chart.background.grid.vsize': 25,
  57. 'chart.background.barcolor1': 'rgba(0,0,0,0)',
  58. 'chart.background.barcolor2': 'rgba(0,0,0,0)',
  59. 'chart.background.grid.hlines': true,
  60. 'chart.background.grid.vlines': true,
  61. 'chart.background.grid.border': true,
  62. 'chart.background.grid.autofit':true,
  63. 'chart.background.grid.autofit.numhlines': 14,
  64. 'chart.background.grid.autofit.numvlines': 20,
  65. 'chart.background.grid.dashed': false,
  66. 'chart.background.grid.dotted': false,
  67. 'chart.background.color': null,
  68. 'chart.linewidth': 1,
  69. 'chart.title': '',
  70. 'chart.title.background': null,
  71. 'chart.title.xaxis': '',
  72. 'chart.title.xaxis.bold': true,
  73. 'chart.title.xaxis.size': null,
  74. 'chart.title.xaxis.font': null,
  75. 'chart.title.yaxis': '',
  76. 'chart.title.yaxis.bold': true,
  77. 'chart.title.yaxis.size': null,
  78. 'chart.title.yaxis.font': null,
  79. 'chart.title.yaxis.color': null,
  80. 'chart.title.xaxis.pos': null,
  81. 'chart.title.yaxis.pos': 0.8,
  82. 'chart.title.yaxis.x': null,
  83. 'chart.title.yaxis.y': null,
  84. 'chart.title.xaxis.x': null,
  85. 'chart.title.xaxis.y': null,
  86. 'chart.title.hpos': null,
  87. 'chart.title.vpos': null,
  88. 'chart.title.bold': true,
  89. 'chart.title.font': null,
  90. 'chart.title.x': null,
  91. 'chart.title.y': null,
  92. 'chart.title.halign': null,
  93. 'chart.title.valign': null,
  94. 'chart.text.size': 10,
  95. 'chart.text.color': 'black',
  96. 'chart.text.font': 'Arial',
  97. 'chart.colors': ['Gradient(white:red)', 'Gradient(white:blue)', 'Gradient(white:green)', 'Gradient(white:pink)', 'Gradient(white:yellow)', 'Gradient(white:cyan)', 'Gradient(white:navy)', 'Gradient(white:gray)', 'Gradient(white:black)'],
  98. 'chart.colors.sequential': false,
  99. 'chart.xlabels.specific': null,
  100. 'chart.labels': [],
  101. 'chart.labels.above': false,
  102. 'chart.labels.above.decimals': 0,
  103. 'chart.labels.above.specific': null,
  104. 'chart.xlabels': true,
  105. 'chart.xlabels.count': 5,
  106. 'chart.contextmenu': null,
  107. 'chart.key': null,
  108. 'chart.key.background': 'white',
  109. 'chart.key.position': 'graph',
  110. 'chart.key.halign': 'right',
  111. 'chart.key.shadow': false,
  112. 'chart.key.shadow.color': '#666',
  113. 'chart.key.shadow.blur': 3,
  114. 'chart.key.shadow.offsetx': 2,
  115. 'chart.key.shadow.offsety': 2,
  116. 'chart.key.position.gutter.boxed': false,
  117. 'chart.key.position.x': null,
  118. 'chart.key.position.y': null,
  119. 'chart.key.color.shape': 'square',
  120. 'chart.key.rounded': true,
  121. 'chart.key.linewidth': 1,
  122. 'chart.key.colors': null,
  123. 'chart.key.interactive': false,
  124. 'chart.key.interactive.highlight.chart.stroke': 'black',
  125. 'chart.key.interactive.highlight.chart.fill':'rgba(255,255,255,0.7)',
  126. 'chart.key.interactive.highlight.label':'rgba(255,0,0,0.2)',
  127. 'chart.key.text.color': 'black',
  128. 'chart.units.pre': '',
  129. 'chart.units.post': '',
  130. 'chart.units.ingraph': false,
  131. 'chart.strokestyle': 'rgba(0,0,0,0)',
  132. 'chart.xmin': 0,
  133. 'chart.xmax': 0,
  134. 'chart.axis.color': 'black',
  135. 'chart.shadow': false,
  136. 'chart.shadow.color': '#666',
  137. 'chart.shadow.blur': 3,
  138. 'chart.shadow.offsetx': 3,
  139. 'chart.shadow.offsety': 3,
  140. 'chart.vmargin': 2,
  141. 'chart.vmargin.grouped': 2,
  142. 'chart.grouping': 'grouped',
  143. 'chart.tooltips': null,
  144. 'chart.tooltips.event': 'onclick',
  145. 'chart.tooltips.effect': 'fade',
  146. 'chart.tooltips.css.class': 'RGraph_tooltip',
  147. 'chart.tooltips.highlight': true,
  148. 'chart.highlight.fill': 'rgba(255,255,255,0.7)',
  149. 'chart.highlight.stroke': 'rgba(0,0,0,0)',
  150. 'chart.annotatable': false,
  151. 'chart.annotate.color': 'black',
  152. 'chart.zoom.factor': 1.5,
  153. 'chart.zoom.fade.in': true,
  154. 'chart.zoom.fade.out': true,
  155. 'chart.zoom.hdir': 'right',
  156. 'chart.zoom.vdir': 'down',
  157. 'chart.zoom.frames': 25,
  158. 'chart.zoom.delay': 16.666,
  159. 'chart.zoom.shadow': true,
  160. 'chart.zoom.background': true,
  161. 'chart.zoom.action': 'zoom',
  162. 'chart.resizable': false,
  163. 'chart.resize.handle.adjust': [0,0],
  164. 'chart.resize.handle.background': null,
  165. 'chart.scale.point': '.',
  166. 'chart.scale.thousand': ',',
  167. 'chart.scale.decimals': null,
  168. 'chart.noredraw': false,
  169. 'chart.events.click': null,
  170. 'chart.events.mousemove': null,
  171. 'chart.noxaxis': false,
  172. 'chart.noyaxis': false,
  173. 'chart.noaxes': false,
  174. 'chart.noxtickmarks': false,
  175. 'chart.noytickmarks': false,
  176. 'chart.numyticks': data.length,
  177. 'chart.numxticks': 10
  178. }
  179. // Check for support
  180. if (!this.canvas) {
  181. alert('[HBAR] No canvas support');
  182. return;
  183. }
  184. for (i=0,len=this.data.length; i<len; ++i) {
  185. if (typeof this.data[i] == 'object') {
  186. this.stackedOrGrouped = true;
  187. }
  188. }
  189. /**
  190. * Create the dollar objects so that functions can be added to them
  191. */
  192. var linear_data = RGraph.arrayLinearize(data);
  193. for (var i=0,len=linear_data.length; i<len; ++i) {
  194. this['$' + i] = {};
  195. }
  196. /**
  197. * Create the linear data array
  198. */
  199. this.data_arr = RGraph.array_linearize(this.data);
  200. /**
  201. * Translate half a pixel for antialiasing purposes - but only if it hasn't beeen
  202. * done already
  203. */
  204. if (!this.canvas.__rgraph_aa_translated__) {
  205. this.context.translate(0.5,0.5);
  206. this.canvas.__rgraph_aa_translated__ = true;
  207. }
  208. // Short variable names
  209. var RG = RGraph;
  210. var ca = this.canvas;
  211. var co = ca.getContext('2d');
  212. var prop = this.properties;
  213. var jq = jQuery;
  214. var pa = RG.Path;
  215. var win = window;
  216. var doc = document;
  217. var ma = Math;
  218. /**
  219. * "Decorate" the object with the generic effects if the effects library has been included
  220. */
  221. if (RG.Effects && typeof RG.Effects.decorate === 'function') {
  222. RG.Effects.decorate(this);
  223. }
  224. /**
  225. * A setter
  226. *
  227. * @param name string The name of the property to set
  228. * @param value mixed The value of the property
  229. */
  230. this.set =
  231. this.Set = function (name, value)
  232. {
  233. name = name.toLowerCase();
  234. /**
  235. * This should be done first - prepend the propertyy name with "chart." if necessary
  236. */
  237. if (name.substr(0,6) != 'chart.') {
  238. name = 'chart.' + name;
  239. }
  240. if (name == 'chart.labels.abovebar') {
  241. name = 'chart.labels.above';
  242. }
  243. prop[name] = value;
  244. return this;
  245. };
  246. /**
  247. * A getter
  248. *
  249. * @param name string The name of the property to get
  250. */
  251. this.get =
  252. this.Get = function (name)
  253. {
  254. /**
  255. * This should be done first - prepend the property name with "chart." if necessary
  256. */
  257. if (name.substr(0,6) != 'chart.') {
  258. name = 'chart.' + name;
  259. }
  260. if (name == 'chart.labels.abovebar') {
  261. name = 'chart.labels.above';
  262. }
  263. return prop[name];
  264. };
  265. /**
  266. * The function you call to draw the bar chart
  267. */
  268. this.draw =
  269. this.Draw = function ()
  270. {
  271. /**
  272. * Fire the onbeforedraw event
  273. */
  274. RG.FireCustomEvent(this, 'onbeforedraw');
  275. /**
  276. * Parse the colors. This allows for simple gradient syntax
  277. */
  278. if (!this.colorsParsed) {
  279. this.parseColors();
  280. // Don't want to do this again
  281. this.colorsParsed = true;
  282. }
  283. /**
  284. * This is new in May 2011 and facilitates indiviual gutter settings,
  285. * eg chart.gutter.left
  286. */
  287. this.gutterLeft = prop['chart.gutter.left'];
  288. this.gutterRight = prop['chart.gutter.right'];
  289. this.gutterTop = prop['chart.gutter.top'];
  290. this.gutterBottom = prop['chart.gutter.bottom'];
  291. /**
  292. * Stop the coords array from growing uncontrollably
  293. */
  294. this.coords = [];
  295. this.coords2 = [];
  296. this.coordsText = [];
  297. this.max = 0;
  298. /**
  299. * Check for chart.xmin in stacked charts
  300. */
  301. if (prop['chart.xmin'] > 0 && prop['chart.grouping'] == 'stacked') {
  302. alert('[HBAR] Using chart.xmin is not supported with stacked charts, resetting chart.xmin to zero');
  303. this.Set('chart.xmin', 0);
  304. }
  305. /**
  306. * Work out a few things. They need to be here because they depend on things you can change before you
  307. * call Draw() but after you instantiate the object
  308. */
  309. this.graphwidth = ca.width - this.gutterLeft - this.gutterRight;
  310. this.graphheight = ca.height - this.gutterTop - this.gutterBottom;
  311. this.halfgrapharea = this.grapharea / 2;
  312. this.halfTextHeight = prop['chart.text.size'] / 2;
  313. // Progressively Draw the chart
  314. RG.Background.draw(this);
  315. this.Drawbars();
  316. this.DrawAxes();
  317. this.DrawLabels();
  318. // Draw the key if necessary
  319. if (prop['chart.key'] && prop['chart.key'].length) {
  320. RG.DrawKey(this, prop['chart.key'], prop['chart.colors']);
  321. }
  322. /**
  323. * Setup the context menu if required
  324. */
  325. if (prop['chart.contextmenu']) {
  326. RG.ShowContext(this);
  327. }
  328. /**
  329. * Draw "in graph" labels
  330. */
  331. RG.DrawInGraphLabels(this);
  332. /**
  333. * This function enables resizing
  334. */
  335. if (prop['chart.resizable']) {
  336. RG.AllowResizing(this);
  337. }
  338. /**
  339. * This installs the event listeners
  340. */
  341. RG.InstallEventListeners(this);
  342. /**
  343. * Fire the onfirstdraw event
  344. */
  345. if (this.firstDraw) {
  346. RG.fireCustomEvent(this, 'onfirstdraw');
  347. this.firstDrawFunc();
  348. this.firstDraw = false;
  349. }
  350. /**
  351. * Fire the RGraph ondraw event
  352. */
  353. RG.FireCustomEvent(this, 'ondraw');
  354. return this;
  355. };
  356. /**
  357. * This draws the axes
  358. */
  359. this.drawAxes =
  360. this.DrawAxes = function ()
  361. {
  362. var halfway = ma.round((this.graphwidth / 2) + this.gutterLeft);
  363. co.beginPath();
  364. co.lineWidth = prop['chart.axis.linewidth'] ? prop['chart.axis.linewidth'] + 0.001 : 1.001;
  365. co.strokeStyle = prop['chart.axis.color'];
  366. // Draw the Y axis
  367. if (prop['chart.noyaxis'] == false && prop['chart.noaxes'] == false) {
  368. if (prop['chart.yaxispos'] == 'center') {
  369. co.moveTo(halfway, this.gutterTop);
  370. co.lineTo(halfway, ca.height - this.gutterBottom);
  371. } else if (prop['chart.yaxispos'] == 'right') {
  372. co.moveTo(ca.width - this.gutterRight, this.gutterTop);
  373. co.lineTo(ca.width - this.gutterRight, ca.height - this.gutterBottom);
  374. } else {
  375. co.moveTo(this.gutterLeft, this.gutterTop);
  376. co.lineTo(this.gutterLeft, ca.height - this.gutterBottom);
  377. }
  378. }
  379. // Draw the X axis
  380. if (prop['chart.noxaxis'] == false && prop['chart.noaxes'] == false) {
  381. co.moveTo(this.gutterLeft +0.001, ca.height - this.gutterBottom + 0.001);
  382. co.lineTo(ca.width - this.gutterRight + 0.001, ca.height - this.gutterBottom + 0.001);
  383. }
  384. // Draw the Y tickmarks
  385. if ( prop['chart.noytickmarks'] == false
  386. && prop['chart.noyaxis'] == false
  387. && prop['chart.numyticks'] > 0
  388. && prop['chart.noaxes'] == false
  389. ) {
  390. var yTickGap = (ca.height - this.gutterTop - this.gutterBottom) / (prop['chart.numyticks'] > 0 ? prop['chart.numyticks'] : this.data.length);
  391. for (y=this.gutterTop; y<(ca.height - this.gutterBottom - 1); y+=yTickGap) {
  392. if (prop['chart.yaxispos'] == 'center') {
  393. co.moveTo(halfway + 3, ma.round(y));
  394. co.lineTo(halfway - 3, ma.round(y));
  395. } else if (prop['chart.yaxispos'] == 'right') {
  396. co.moveTo(ca.width - this.gutterRight, ma.round(y));
  397. co.lineTo(ca.width - this.gutterRight + 3, ma.round(y));
  398. } else {
  399. co.moveTo(this.gutterLeft, ma.round(y));
  400. co.lineTo( this.gutterLeft - 3, ma.round(y));
  401. }
  402. }
  403. // If the X axis isn't being shown draw the end tick
  404. if (prop['chart.noxaxis'] == true) {
  405. if (prop['chart.yaxispos'] == 'center') {
  406. co.moveTo(halfway + 3, ma.round(y));
  407. co.lineTo(halfway - 3, ma.round(y));
  408. } else if (prop['chart.yaxispos'] == 'right') {
  409. co.moveTo(ca.width - this.gutterRight, ma.round(y));
  410. co.lineTo(ca.width - this.gutterRight + 3, ma.round(y));
  411. } else {
  412. co.moveTo(this.gutterLeft, ma.round(y));
  413. co.lineTo( this.gutterLeft - 3, ma.round(y));
  414. }
  415. }
  416. }
  417. // Draw the X tickmarks
  418. if ( prop['chart.noxtickmarks'] == false
  419. && prop['chart.noxaxis'] == false
  420. && prop['chart.numxticks'] > 0
  421. && prop['chart.noaxes'] == false) {
  422. xTickGap = (ca.width - this.gutterLeft - this.gutterRight ) / prop['chart.numxticks'];
  423. yStart = ca.height - this.gutterBottom;
  424. yEnd = (ca.height - this.gutterBottom) + 3;
  425. var i = prop['chart.yaxispos'] === 'center' ? 5 : 10; while(i--) {
  426. var gap = (ca.width - this.gutterRight - this.gutterLeft) / 10
  427. var x = ca.width - this.gutterRight - (i * gap);
  428. if (prop['chart.yaxispos'] === 'right') {
  429. x -= gap;
  430. }
  431. co.moveTo(ma.round(x), yStart);
  432. co.lineTo(ma.round(x), yEnd);
  433. }
  434. if (prop['chart.yaxispos'] === 'center') {
  435. var i = 5; while (i--) {
  436. var x = this.gutterLeft + (gap * i);
  437. co.moveTo(ma.round(x), yStart);
  438. co.lineTo(ma.round(x), yEnd);
  439. }
  440. }
  441. // If the Y axis isn't being shown draw the end tick
  442. if (prop['chart.noyaxis'] == true) {
  443. co.moveTo(this.gutterLeft, ma.round(yStart));
  444. co.lineTo( this.gutterLeft, ma.round(yEnd));
  445. }
  446. }
  447. co.stroke();
  448. /**
  449. * Reset the linewidth
  450. */
  451. co.lineWidth = 1;
  452. };
  453. /**
  454. * This draws the labels for the graph
  455. */
  456. this.drawLabels =
  457. this.DrawLabels = function ()
  458. {
  459. var units_pre = prop['chart.units.pre'];
  460. var units_post = prop['chart.units.post'];
  461. var text_size = prop['chart.text.size'];
  462. var font = prop['chart.text.font'];
  463. /**
  464. * Set the units to blank if they're to be used for ingraph labels only
  465. */
  466. if (prop['chart.units.ingraph']) {
  467. units_pre = '';
  468. units_post = '';
  469. }
  470. /**
  471. * Draw the X axis labels
  472. */
  473. if (prop['chart.xlabels']) {
  474. /**
  475. * Specific X labels
  476. */
  477. if (RG.isArray(prop['chart.xlabels.specific'])) {
  478. if (prop['chart.yaxispos'] == 'center') {
  479. var halfGraphWidth = this.graphwidth / 2;
  480. var labels = prop['chart.xlabels.specific'];
  481. var interval = (this.graphwidth / 2) / (labels.length - 1);
  482. co.fillStyle = prop['chart.text.color'];
  483. for (var i=0; i<labels.length; i+=1) {
  484. RG.Text2(this, {'font':font,
  485. 'size':text_size,
  486. 'x':this.gutterLeft + halfGraphWidth + (interval * i),
  487. 'y':ca.height - this.gutterBottom,
  488. 'text':labels[i],
  489. 'valign':'top',
  490. 'halign':'center',
  491. 'tag': 'scale'});
  492. }
  493. for (var i=(labels.length - 1); i>0; i-=1) {
  494. RG.Text2(this, {'font':font,
  495. 'size':text_size,
  496. 'x':this.gutterLeft + (interval * (labels.length - i - 1)),
  497. 'y':ca.height - this.gutterBottom,
  498. 'text':labels[i],
  499. 'valign':'top',
  500. 'halign':'center',
  501. 'tag': 'scale'});
  502. }
  503. } else if (prop['chart.yaxispos'] == 'right') {
  504. var labels = prop['chart.xlabels.specific'];
  505. var interval = this.graphwidth / (labels.length - 1);
  506. co.fillStyle = prop['chart.text.color'];
  507. for (var i=0; i<labels.length; i+=1) {
  508. RG.Text2(this, {'font':font,
  509. 'size':text_size,
  510. 'x':this.gutterLeft + (interval * i),
  511. 'y':ca.height - this.gutterBottom,
  512. 'text':labels[labels.length - i - 1],
  513. 'valign':'top',
  514. 'halign':'center',
  515. 'tag': 'scale'});
  516. }
  517. } else {
  518. var labels = prop['chart.xlabels.specific'];
  519. var interval = this.graphwidth / (labels.length - 1);
  520. co.fillStyle = prop['chart.text.color'];
  521. for (var i=0; i<labels.length; i+=1) {
  522. RG.Text2(this, {'font':font,
  523. 'size':text_size,
  524. 'x':this.gutterLeft + (interval * i),
  525. 'y':ca.height - this.gutterBottom,
  526. 'text':labels[i],
  527. 'valign':'top',
  528. 'halign':'center',
  529. 'tag': 'scale'});
  530. }
  531. }
  532. /**
  533. * Draw an X scale
  534. */
  535. } else {
  536. var gap = 7;
  537. co.beginPath();
  538. co.fillStyle = prop['chart.text.color'];
  539. if (prop['chart.yaxispos'] == 'center') {
  540. for (var i=0; i<this.scale2.labels.length; ++i) {
  541. RG.Text2(this, {'font':font,
  542. 'size':text_size,
  543. 'x':this.gutterLeft + (this.graphwidth / 2) - ((this.graphwidth / 2) * ((i+1)/this.scale2.labels.length)),
  544. 'y':this.gutterTop + this.halfTextHeight + this.graphheight + gap,
  545. 'text':'-' + this.scale2.labels[i],
  546. 'valign':'center',
  547. 'halign':'center',
  548. 'tag': 'scale'});
  549. }
  550. for (var i=0; i<this.scale2.labels.length; ++i) {
  551. RG.Text2(this, {'font':font,
  552. 'size':text_size,
  553. 'x':this.gutterLeft + ((this.graphwidth / 2) * ((i+1)/this.scale2.labels.length)) + (this.graphwidth / 2),
  554. 'y':this.gutterTop + this.halfTextHeight + this.graphheight + gap,
  555. 'text':this.scale2.labels[i],
  556. 'valign':'center',
  557. 'halign':'center',
  558. 'tag': 'scale'});
  559. }
  560. }else if (prop['chart.yaxispos'] == 'right') {
  561. for (var i=0,len=this.scale2.labels.length; i<len; ++i) {
  562. RG.Text2(this, {'font':font,
  563. 'size':text_size,
  564. 'x':this.gutterLeft + (i * (this.graphwidth / len)),
  565. 'y':this.gutterTop + this.halfTextHeight + this.graphheight + gap,
  566. 'text':'-' + this.scale2.labels[len - 1 - i],
  567. 'valign':'center',
  568. 'halign':'center',
  569. 'tag': 'scale'
  570. });
  571. }
  572. } else {
  573. for (var i=0,len=this.scale2.labels.length; i<len; ++i) {
  574. RG.Text2(this, {'font':font,
  575. 'size':text_size,
  576. 'x':this.gutterLeft + (this.graphwidth * ((i+1)/len)),
  577. 'y':this.gutterTop + this.halfTextHeight + this.graphheight + gap,
  578. 'text':this.scale2.labels[i],
  579. 'valign':'center',
  580. 'halign':'center',
  581. 'tag': 'scale'
  582. });
  583. }
  584. }
  585. /**
  586. * If xmin is not zero - draw that
  587. */
  588. if (prop['chart.xmin'] > 0 || prop['chart.noyaxis'] == true) {
  589. var x = prop['chart.yaxispos'] == 'center' ? this.gutterLeft + (this.graphwidth / 2): this.gutterLeft;
  590. /**
  591. * Y axis on the right
  592. */
  593. if (prop['chart.yaxispos'] === 'right') {
  594. var x = ca.width - this.gutterRight;
  595. }
  596. RG.Text2(this, {'font':font,
  597. 'size':text_size,
  598. 'x':x,
  599. 'y':this.gutterTop + this.halfTextHeight + this.graphheight + gap,
  600. 'text':RG.number_format(this, prop['chart.xmin'].toFixed(prop['chart.scale.decimals']), units_pre, units_post),
  601. 'valign':'center',
  602. 'halign':'center',
  603. 'tag': 'scale'
  604. });
  605. }
  606. co.fill();
  607. co.stroke();
  608. }
  609. }
  610. /**
  611. * The Y axis labels
  612. */
  613. if (typeof(prop['chart.labels']) == 'object') {
  614. var xOffset = 5;
  615. var font = prop['chart.text.font'];
  616. // Draw the X axis labels
  617. co.fillStyle = prop['chart.text.color'];
  618. // How high is each bar
  619. var barHeight = (ca.height - this.gutterTop - this.gutterBottom ) / prop['chart.labels'].length;
  620. // Reset the yTickGap
  621. yTickGap = (ca.height - this.gutterTop - this.gutterBottom) / prop['chart.labels'].length
  622. /**
  623. * If the Y axis is on the right set the alignment and the X position, otherwise on the left
  624. */
  625. if (prop['chart.yaxispos'] === 'right') {
  626. var x = ca.width - this.gutterRight + xOffset;
  627. var halign = 'left'
  628. } else {
  629. var x = this.gutterLeft - xOffset;
  630. var halign = 'right'
  631. }
  632. // Draw the X tickmarks
  633. var i=0;
  634. for (y=this.gutterTop + (yTickGap / 2); y<=ca.height - this.gutterBottom; y+=yTickGap) {
  635. RG.Text2(this, {'font': font,
  636. 'size': prop['chart.text.size'],
  637. 'x': x,
  638. 'y': y,
  639. 'text': String(prop['chart.labels'][i++]),
  640. 'halign': halign,
  641. 'valign': 'center',
  642. 'tag': 'labels'
  643. });
  644. }
  645. }
  646. };
  647. /**
  648. * This function draws the bars
  649. */
  650. this.drawbars =
  651. this.Drawbars = function ()
  652. {
  653. co.lineWidth = prop['chart.linewidth'];
  654. co.strokeStyle = prop['chart.strokestyle'];
  655. co.fillStyle = prop['chart.colors'][0];
  656. var prevX = 0;
  657. var prevY = 0;
  658. /**
  659. * Work out the max value
  660. */
  661. if (prop['chart.xmax']) {
  662. this.scale2 = RG.getScale2(this, {'max':prop['chart.xmax'],
  663. 'min':prop['chart.xmin'],
  664. 'scale.decimals':Number(prop['chart.scale.decimals']),
  665. 'scale.point':prop['chart.scale.point'],
  666. 'scale.thousand':prop['chart.scale.thousand'],
  667. 'scale.round':prop['chart.scale.round'],
  668. 'units.pre':prop['chart.units.pre'],
  669. 'units.post':prop['chart.units.post'],
  670. 'ylabels.count':prop['chart.xlabels.count'],
  671. 'strict':true
  672. });
  673. this.max = this.scale2.max;
  674. } else {
  675. var grouping = prop['chart.grouping'];
  676. for (i=0; i<this.data.length; ++i) {
  677. if (typeof(this.data[i]) == 'object') {
  678. var value = grouping == 'grouped' ? Number(RG.array_max(this.data[i], true)) : Number(RG.array_sum(this.data[i])) ;
  679. } else {
  680. var value = Number(Math.abs(this.data[i]));
  681. }
  682. this.max = ma.max(Math.abs(this.max), Math.abs(value));
  683. }
  684. this.scale2 = RG.getScale2(this, {'max':this.max,
  685. 'min':prop['chart.xmin'],
  686. 'scale.decimals':Number(prop['chart.scale.decimals']),
  687. 'scale.point':prop['chart.scale.point'],
  688. 'scale.thousand':prop['chart.scale.thousand'],
  689. 'scale.round':prop['chart.scale.round'],
  690. 'units.pre':prop['chart.units.pre'],
  691. 'units.post':prop['chart.units.post'],
  692. 'ylabels.count':prop['chart.xlabels.count']
  693. });
  694. this.max = this.scale2.max;
  695. this.min = this.scale2.min;
  696. }
  697. if (prop['chart.scale.decimals'] == null && Number(this.max) == 1) {
  698. this.Set('chart.scale.decimals', 1);
  699. }
  700. /**
  701. * This is here to facilitate sequential colors
  702. */
  703. var colorIdx = 0;
  704. /**
  705. * The bars are drawn HERE
  706. */
  707. var graphwidth = (ca.width - this.gutterLeft - this.gutterRight);
  708. var halfwidth = graphwidth / 2;
  709. for (i=0,len=this.data.length; i<len; ++i) {
  710. // Work out the width and height
  711. var width = ma.abs((this.data[i] / this.max) * graphwidth);
  712. var height = this.graphheight / this.data.length;
  713. var orig_height = height;
  714. var x = this.gutterLeft;
  715. var y = this.gutterTop + (i * height);
  716. var vmargin = prop['chart.vmargin'];
  717. // Account for the Y axis being on the right hand side
  718. if (prop['chart.yaxispos'] === 'right') {
  719. x = ca.width - this.gutterRight - ma.abs(width);
  720. }
  721. // Account for negative lengths - Some browsers (eg Chrome) don't like a negative value
  722. if (width < 0) {
  723. x -= width;
  724. width = ma.abs(width);
  725. }
  726. /**
  727. * Turn on the shadow if need be
  728. */
  729. if (prop['chart.shadow']) {
  730. co.shadowColor = prop['chart.shadow.color'];
  731. co.shadowBlur = prop['chart.shadow.blur'];
  732. co.shadowOffsetX = prop['chart.shadow.offsetx'];
  733. co.shadowOffsetY = prop['chart.shadow.offsety'];
  734. }
  735. /**
  736. * Draw the bar
  737. */
  738. co.beginPath();
  739. if (typeof(this.data[i]) == 'number') {
  740. var barHeight = height - (2 * vmargin);
  741. var barWidth = ((this.data[i] - prop['chart.xmin']) / (this.max - prop['chart.xmin'])) * this.graphwidth;
  742. var barX = this.gutterLeft;
  743. // Account for Y axis pos
  744. if (prop['chart.yaxispos'] == 'center') {
  745. barWidth /= 2;
  746. barX += halfwidth;
  747. if (this.data[i] < 0) {
  748. barWidth = (Math.abs(this.data[i]) - prop['chart.xmin']) / (this.max - prop['chart.xmin']);
  749. barWidth = barWidth * (this.graphwidth / 2);
  750. barX = ((this.graphwidth / 2) + this.gutterLeft) - barWidth;
  751. }
  752. } else if (prop['chart.yaxispos'] == 'right') {
  753. barWidth = Math.abs(barWidth);
  754. barX = ca.width - this.gutterRight - barWidth;
  755. }
  756. // Set the fill color
  757. co.strokeStyle = prop['chart.strokestyle'];
  758. co.fillStyle = prop['chart.colors'][0];
  759. // Sequential colors
  760. if (prop['chart.colors.sequential']) {
  761. co.fillStyle = prop['chart.colors'][colorIdx++];
  762. }
  763. co.strokeRect(barX, this.gutterTop + (i * height) + prop['chart.vmargin'], barWidth, barHeight);
  764. co.fillRect(barX, this.gutterTop + (i * height) + prop['chart.vmargin'], barWidth, barHeight);
  765. this.coords.push([barX,
  766. y + vmargin,
  767. barWidth,
  768. height - (2 * vmargin),
  769. co.fillStyle,
  770. this.data[i],
  771. true]);
  772. /**
  773. * Stacked bar chart
  774. */
  775. } else if (typeof(this.data[i]) == 'object' && prop['chart.grouping'] == 'stacked') {
  776. if (prop['chart.yaxispos'] == 'center') {
  777. alert('[HBAR] You can\'t have a stacked chart with the Y axis in the center, change it to grouped');
  778. } else if (prop['chart.yaxispos'] == 'right') {
  779. var x = ca.width - this.gutterRight
  780. }
  781. var barHeight = height - (2 * vmargin);
  782. if (typeof this.coords2[i] == 'undefined') {
  783. this.coords2[i] = [];
  784. }
  785. for (j=0; j<this.data[i].length; ++j) {
  786. // Set the fill/stroke colors
  787. co.strokeStyle = prop['chart.strokestyle'];
  788. co.fillStyle = prop['chart.colors'][j];
  789. // Sequential colors
  790. if (prop['chart.colors.sequential']) {
  791. co.fillStyle = prop['chart.colors'][colorIdx++];
  792. }
  793. var width = (((this.data[i][j]) / (this.max))) * this.graphwidth;
  794. var totalWidth = (RG.arraySum(this.data[i]) / this.max) * this.graphwidth;
  795. if (prop['chart.yaxispos'] === 'right') {
  796. x -= width;
  797. }
  798. co.strokeRect(x, this.gutterTop + prop['chart.vmargin'] + (this.graphheight / this.data.length) * i, width, height - (2 * vmargin) );
  799. co.fillRect(x, this.gutterTop + prop['chart.vmargin'] + (this.graphheight / this.data.length) * i, width, height - (2 * vmargin) );
  800. /**
  801. * Store the coords for tooltips
  802. */
  803. // The last property of this array is a boolean which tells you whether the value is the last or not
  804. this.coords.push([x,
  805. y + vmargin,
  806. width,
  807. height - (2 * vmargin),
  808. co.fillStyle,
  809. RG.array_sum(this.data[i]),
  810. j == (this.data[i].length - 1)
  811. ]);
  812. this.coords2[i].push([x,
  813. y + vmargin,
  814. width,
  815. height - (2 * vmargin),
  816. co.fillStyle,
  817. RG.array_sum(this.data[i]),
  818. j == (this.data[i].length - 1)
  819. ]);
  820. if (prop['chart.yaxispos'] !== 'right') {
  821. x += width;
  822. }
  823. }
  824. /**
  825. * A grouped bar chart
  826. */
  827. } else if (typeof(this.data[i]) == 'object' && prop['chart.grouping'] == 'grouped') {
  828. var vmarginGrouped = prop['chart.vmargin.grouped'];
  829. var individualBarHeight = ((height - (2 * vmargin) - ((this.data[i].length - 1) * vmarginGrouped)) / this.data[i].length)
  830. if (typeof this.coords2[i] == 'undefined') {
  831. this.coords2[i] = [];
  832. }
  833. for (j=0; j<this.data[i].length; ++j) {
  834. /**
  835. * Turn on the shadow if need be
  836. */
  837. if (prop['chart.shadow']) {
  838. RG.setShadow(this, prop['chart.shadow.color'], prop['chart.shadow.offsetx'], prop['chart.shadow.offsety'], prop['chart.shadow.blur']);
  839. }
  840. // Set the fill/stroke colors
  841. co.strokeStyle = prop['chart.strokestyle'];
  842. co.fillStyle = prop['chart.colors'][j];
  843. // Sequential colors
  844. if (prop['chart.colors.sequential']) {
  845. co.fillStyle = prop['chart.colors'][colorIdx++];
  846. }
  847. var startY = this.gutterTop + (height * i) + (individualBarHeight * j) + vmargin + (vmarginGrouped * j);
  848. var width = ((this.data[i][j] - prop['chart.xmin']) / (this.max - prop['chart.xmin'])) * (ca.width - this.gutterLeft - this.gutterRight );
  849. var startX = this.gutterLeft;
  850. // Account for the Y axis being in the middle
  851. if (prop['chart.yaxispos'] == 'center') {
  852. width /= 2;
  853. startX += halfwidth;
  854. // Account for the Y axis being on the right
  855. } else if (prop['chart.yaxispos'] == 'right') {
  856. width = ma.abs(width);
  857. startX = ca.width - this.gutterRight - ma.abs(width);;
  858. }
  859. if (width < 0) {
  860. startX += width;
  861. width *= -1;
  862. }
  863. co.strokeRect(startX, startY, width, individualBarHeight);
  864. co.fillRect(startX, startY, width, individualBarHeight);
  865. this.coords.push([startX,
  866. startY,
  867. width,
  868. individualBarHeight,
  869. co.fillStyle,
  870. this.data[i][j],
  871. true]);
  872. this.coords2[i].push([startX,
  873. startY,
  874. width,
  875. individualBarHeight,
  876. co.fillStyle,
  877. this.data[i][j],
  878. true]);
  879. }
  880. startY += vmargin;
  881. }
  882. co.closePath();
  883. }
  884. co.stroke();
  885. co.fill();
  886. /**
  887. * Now the bars are stroke()ed, turn off the shadow
  888. */
  889. RG.NoShadow(this);
  890. this.RedrawBars();
  891. };
  892. /**
  893. * This function goes over the bars after they been drawn, so that upwards shadows are underneath the bars
  894. */
  895. this.redrawBars =
  896. this.RedrawBars = function ()
  897. {
  898. if (prop['chart.noredraw']) {
  899. return;
  900. }
  901. var coords = this.coords;
  902. var font = prop['chart.text.font'];
  903. var size = prop['chart.text.size'];
  904. var color = prop['chart.text.color'];
  905. RG.noShadow(this);
  906. co.strokeStyle = prop['chart.strokestyle'];
  907. for (var i=0; i<coords.length; ++i) {
  908. if (prop['chart.shadow']) {
  909. co.beginPath();
  910. co.strokeStyle = prop['chart.strokestyle'];
  911. co.fillStyle = coords[i][4];
  912. co.lineWidth = prop['chart.linewidth'];
  913. co.rect(coords[i][0], coords[i][1], coords[i][2], coords[i][3]);
  914. co.stroke();
  915. co.fill();
  916. }
  917. /**
  918. * Draw labels "above" the bar
  919. */
  920. var halign = 'left';
  921. if (prop['chart.labels.above'] && coords[i][6]) {
  922. co.fillStyle = prop['chart.text.color'];
  923. co.strokeStyle = 'black';
  924. RG.noShadow(this);
  925. var border = (coords[i][0] + coords[i][2] + 7 + co.measureText(prop['chart.units.pre'] + this.coords[i][5] + prop['chart.units.post']).width) > ca.width ? true : false;
  926. /**
  927. * Default to the value - then check for specific labels
  928. */
  929. var text = RG.numberFormat(this, (this.coords[i][5]).toFixed(prop['chart.labels.above.decimals']), prop['chart.units.pre'], prop['chart.units.post']);
  930. if (typeof prop['chart.labels.above.specific'] == 'object' && prop['chart.labels.above.specific'] && prop['chart.labels.above.specific'][i]) {
  931. text = prop['chart.labels.above.specific'][i];
  932. }
  933. var x = coords[i][0] + coords[i][2] + 5;
  934. var y = coords[i][1] + (coords[i][3] / 2);
  935. if (prop['chart.yaxispos'] === 'right') {
  936. x = coords[i][0] - 5;
  937. halign = 'right';
  938. } else if (prop['chart.yaxispos'] === 'center' && this.data_arr[i] < 0) {
  939. x = coords[i][0] - 5;
  940. halign = 'right';
  941. }
  942. RG.Text2(this, {'font':font,
  943. 'size':size,
  944. 'x':x,
  945. 'y':y,
  946. 'text': text,
  947. 'valign':'center',
  948. 'halign': halign,
  949. 'tag': 'labels.above'
  950. });
  951. }
  952. }
  953. };
  954. /**
  955. * This function can be used to get the appropriate bar information (if any)
  956. *
  957. * @param e Event object
  958. * @return Appriate bar information (if any)
  959. */
  960. this.getShape =
  961. this.getBar = function (e)
  962. {
  963. var mouseCoords = RG.getMouseXY(e);
  964. /**
  965. * Loop through the bars determining if the mouse is over a bar
  966. */
  967. for (var i=0,len=this.coords.length; i<len; i++) {
  968. var mouseX = mouseCoords[0]; // In relation to the canvas
  969. var mouseY = mouseCoords[1]; // In relation to the canvas
  970. var left = this.coords[i][0];
  971. var top = this.coords[i][1];
  972. var width = this.coords[i][2];
  973. var height = this.coords[i][3];
  974. var idx = i;
  975. if (mouseX >= left && mouseX <= (left + width) && mouseY >= top && mouseY <= (top + height) ) {
  976. var tooltip = RG.parseTooltipText(prop['chart.tooltips'], i);
  977. return {
  978. 0: this, 'object': this,
  979. 1: left, 'x': left,
  980. 2: top, 'y': top,
  981. 3: width, 'width': width,
  982. 4: height, 'height': height,
  983. 5: idx, 'index': idx,
  984. 'tooltip': tooltip
  985. };
  986. }
  987. }
  988. };
  989. /**
  990. * When you click on the chart, this method can return the X value at that point. It works for any point on the
  991. * chart (that is inside the gutters) - not just points within the Bars.
  992. *
  993. * @param object e The event object
  994. */
  995. this.getValue = function (arg)
  996. {
  997. if (arg.length == 2) {
  998. var mouseX = arg[0];
  999. var mouseY = arg[1];
  1000. } else {
  1001. var mouseCoords = RG.getMouseXY(arg);
  1002. var mouseX = mouseCoords[0];
  1003. var mouseY = mouseCoords[1];
  1004. }
  1005. if ( mouseY < this.gutterTop
  1006. || mouseY > (ca.height - this.gutterBottom)
  1007. || mouseX < this.gutterLeft
  1008. || mouseX > (ca.width - this.gutterRight)
  1009. ) {
  1010. return null;
  1011. }
  1012. if (prop['chart.yaxispos'] == 'center') {
  1013. var value = ((mouseX - this.gutterLeft) / (this.graphwidth / 2)) * (this.max - prop['chart.xmin']);
  1014. value = value - this.max
  1015. // Special case if xmin is defined
  1016. if (prop['chart.xmin'] > 0) {
  1017. value = ((mouseX - this.gutterLeft - (this.graphwidth / 2)) / (this.graphwidth / 2)) * (this.max - prop['chart.xmin']);
  1018. value += prop['chart.xmin'];
  1019. if (mouseX < (this.gutterLeft + (this.graphwidth / 2))) {
  1020. value -= (2 * prop['chart.xmin']);
  1021. }
  1022. }
  1023. } else {
  1024. var value = ((mouseX - this.gutterLeft) / this.graphwidth) * (this.max - prop['chart.xmin']);
  1025. value += prop['chart.xmin'];
  1026. }
  1027. return value;
  1028. };
  1029. /**
  1030. * Each object type has its own Highlight() function which highlights the appropriate shape
  1031. *
  1032. * @param object shape The shape to highlight
  1033. */
  1034. this.highlight =
  1035. this.Highlight = function (shape)
  1036. {
  1037. // Add the new highlight
  1038. RG.Highlight.Rect(this, shape);
  1039. };
  1040. /**
  1041. * The getObjectByXY() worker method. Don't call this call:
  1042. *
  1043. * RG.ObjectRegistry.getObjectByXY(e)
  1044. *
  1045. * @param object e The event object
  1046. */
  1047. this.getObjectByXY = function (e)
  1048. {
  1049. var mouseXY = RG.getMouseXY(e);
  1050. if (
  1051. mouseXY[0] > this.gutterLeft
  1052. && mouseXY[0] < (ca.width - this.gutterRight)
  1053. && mouseXY[1] > this.gutterTop
  1054. && mouseXY[1] < (ca.height - this.gutterBottom)
  1055. ) {
  1056. return this;
  1057. }
  1058. };
  1059. /**
  1060. * This function positions a tooltip when it is displayed
  1061. *
  1062. * @param obj object The chart object
  1063. * @param int x The X coordinate specified for the tooltip
  1064. * @param int y The Y coordinate specified for the tooltip
  1065. * @param objec tooltip The tooltips DIV element
  1066. */
  1067. this.positionTooltip = function (obj, x, y, tooltip, idx)
  1068. {
  1069. var coordX = obj.coords[tooltip.__index__][0];
  1070. var coordY = obj.coords[tooltip.__index__][1];
  1071. var coordW = obj.coords[tooltip.__index__][2];
  1072. var coordH = obj.coords[tooltip.__index__][3];
  1073. var canvasXY = RG.getCanvasXY(obj.canvas);
  1074. var gutterLeft = obj.gutterLeft;
  1075. var gutterTop = obj.gutterTop;
  1076. var width = tooltip.offsetWidth;
  1077. var height = tooltip.offsetHeight;
  1078. // Set the top position
  1079. tooltip.style.left = 0;
  1080. tooltip.style.top = canvasXY[1] + coordY + (coordH / 2) - height + 'px';
  1081. // By default any overflow is hidden
  1082. tooltip.style.overflow = '';
  1083. // The arrow
  1084. var img = new Image();
  1085. img.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAFCAYAAACjKgd3AAAARUlEQVQYV2NkQAN79+797+RkhC4M5+/bd47B2dmZEVkBCgcmgcsgbAaA9GA1BCSBbhAuA/AagmwQPgMIGgIzCD0M0AMMAEFVIAa6UQgcAAAAAElFTkSuQmCC';
  1086. img.style.position = 'absolute';
  1087. img.id = '__rgraph_tooltip_pointer__';
  1088. img.style.top = (tooltip.offsetHeight - 2) + 'px';
  1089. tooltip.appendChild(img);
  1090. // Reposition the tooltip if at the edges:
  1091. // LEFT edge
  1092. if ((canvasXY[0] + coordX + (coordW / 2) - (width / 2)) < 10) {
  1093. tooltip.style.left = (canvasXY[0] + coordX - (width * 0.1)) + (coordW / 2) + 'px';
  1094. img.style.left = ((width * 0.1) - 8.5) + 'px';
  1095. // RIGHT edge
  1096. } else if ((canvasXY[0] + (coordW / 2) + coordX + (width / 2)) > doc.body.offsetWidth) {
  1097. tooltip.style.left = canvasXY[0] + coordX - (width * 0.9) + (coordW / 2) + 'px';
  1098. img.style.left = ((width * 0.9) - 8.5) + 'px';
  1099. // Default positioning - CENTERED
  1100. } else {
  1101. tooltip.style.left = (canvasXY[0] + coordX + (coordW / 2) - (width * 0.5)) + 'px';
  1102. img.style.left = ((width * 0.5) - 8.5) + 'px';
  1103. }
  1104. };
  1105. /**
  1106. * Returns the appropriate Y coord for the given value
  1107. *
  1108. * @param number value The value to get the coord for
  1109. */
  1110. this.getXCoord = function (value)
  1111. {
  1112. if (prop['chart.yaxispos'] == 'center') {
  1113. // Range checking
  1114. if (value > this.max || value < (-1 * this.max)) {
  1115. return null;
  1116. }
  1117. var width = (ca.width - prop['chart.gutter.left'] - prop['chart.gutter.right']) / 2;
  1118. var coord = (((value - prop['chart.xmin']) / (this.max - prop['chart.xmin'])) * width) + width;
  1119. coord = prop['chart.gutter.left'] + coord;
  1120. } else {
  1121. // Range checking
  1122. if (value > this.max || value < 0) {
  1123. return null;
  1124. }
  1125. var width = ca.width - prop['chart.gutter.left'] - prop['chart.gutter.right'];
  1126. var coord = ((value - prop['chart.xmin']) / (this.max - prop['chart.xmin'])) * width;
  1127. coord = prop['chart.gutter.left'] + coord;
  1128. }
  1129. return coord;
  1130. };
  1131. /**
  1132. *
  1133. */
  1134. this.parseColors = function ()
  1135. {
  1136. // Save the original colors so that they can be restored when the canvas is reset
  1137. if (this.original_colors.length === 0) {
  1138. //this.original_colors['chart.'] = RG.array_clone(prop['chart.']);
  1139. this.original_colors['chart.colors'] = RG.array_clone(prop['chart.colors']);
  1140. this.original_colors['chart.background.grid.color'] = RG.array_clone(prop['chart.background.grid.color']);
  1141. this.original_colors['chart.background.color'] = RG.array_clone(prop['chart.background.color']);
  1142. this.original_colors['chart.background.barcolor1'] = RG.array_clone(prop['chart.background.barcolor1']);
  1143. this.original_colors['chart.background.barcolor2'] = RG.array_clone(prop['chart.background.barcolor2']);
  1144. this.original_colors['chart.text.color'] = RG.array_clone(prop['chart.text.color']);
  1145. this.original_colors['chart.labels.colors'] = RG.array_clone(prop['chart.labels.colors']);
  1146. this.original_colors['chart.strokestyle'] = RG.array_clone(prop['chart.strokestyle']);
  1147. this.original_colors['chart.axis.color'] = RG.array_clone(prop['chart.axis.color']);
  1148. this.original_colors['chart.highlight.fill'] = RG.array_clone(prop['chart.highlight.fill']);
  1149. this.original_colors['chart.highlight.stroke'] = RG.array_clone(prop['chart.highlight.stroke']);
  1150. }
  1151. var colors = prop['chart.colors'];
  1152. for (var i=0; i<colors.length; ++i) {
  1153. colors[i] = this.parseSingleColorForGradient(colors[i]);
  1154. }
  1155. prop['chart.background.grid.color'] = this.parseSingleColorForGradient(prop['chart.background.grid.color']);
  1156. prop['chart.background.color'] = this.parseSingleColorForGradient(prop['chart.background.color']);
  1157. prop['chart.background.barcolor1'] = this.parseSingleColorForGradient(prop['chart.background.barcolor1']);
  1158. prop['chart.background.barcolor2'] = this.parseSingleColorForGradient(prop['chart.background.barcolor2']);
  1159. prop['chart.text.color'] = this.parseSingleColorForGradient(prop['chart.text.color']);
  1160. prop['chart.labels.colors'] = this.parseSingleColorForGradient(prop['chart.labels.colors']);
  1161. prop['chart.strokestyle'] = this.parseSingleColorForGradient(prop['chart.strokestyle']);
  1162. prop['chart.axis.color'] = this.parseSingleColorForGradient(prop['chart.axis.color']);
  1163. prop['chart.highlight.fill'] = this.parseSingleColorForGradient(prop['chart.highlight.fill']);
  1164. prop['chart.highlight.stroke'] = this.parseSingleColorForGradient(prop['chart.highlight.stroke']);
  1165. };
  1166. /**
  1167. * This parses a single color value
  1168. */
  1169. this.parseSingleColorForGradient = function (color)
  1170. {
  1171. if (!color || typeof(color) != 'string') {
  1172. return color;
  1173. }
  1174. if (color.match(/^gradient\((.*)\)$/i)) {
  1175. var parts = RegExp.$1.split(':');
  1176. if (prop['chart.yaxispos'] === 'right') {
  1177. parts = RG.arrayReverse(parts);
  1178. }
  1179. // Create the gradient
  1180. var grad = co.createLinearGradient(prop['chart.gutter.left'],0,ca.width - prop['chart.gutter.right'],0);
  1181. var diff = 1 / (parts.length - 1);
  1182. grad.addColorStop(0, RG.trim(parts[0]));
  1183. for (var j=1; j<parts.length; ++j) {
  1184. grad.addColorStop(j * diff, RG.trim(parts[j]));
  1185. }
  1186. }
  1187. return grad ? grad : color;
  1188. };
  1189. /**
  1190. * This function handles highlighting an entire data-series for the interactive
  1191. * key
  1192. *
  1193. * @param int index The index of the data series to be highlighted
  1194. */
  1195. this.interactiveKeyHighlight = function (index)
  1196. {
  1197. var obj = this;
  1198. this.coords2.forEach(function (value, idx, arr)
  1199. {
  1200. var shape = obj.coords2[idx][index]
  1201. var pre_linewidth = co.lineWidth;
  1202. co.lineWidth = 2;
  1203. co.fillStyle = prop['chart.key.interactive.highlight.chart.fill'];
  1204. co.strokeStyle = prop['chart.key.interactive.highlight.chart.stroke'];
  1205. co.fillRect(shape[0], shape[1], shape[2], shape[3]);
  1206. co.strokeRect(shape[0], shape[1], shape[2], shape[3]);
  1207. // Reset the lineWidth
  1208. co.lineWidth = pre_linewidth;
  1209. });
  1210. };
  1211. /**
  1212. * Using a function to add events makes it easier to facilitate method chaining
  1213. *
  1214. * @param string type The type of even to add
  1215. * @param function func
  1216. */
  1217. this.on = function (type, func)
  1218. {
  1219. if (type.substr(0,2) !== 'on') {
  1220. type = 'on' + type;
  1221. }
  1222. this[type] = func;
  1223. return this;
  1224. };
  1225. /**
  1226. * This function runs once only
  1227. * (put at the end of the file (before any effects))
  1228. */
  1229. this.firstDrawFunc = function ()
  1230. {
  1231. };
  1232. /**
  1233. * Grow
  1234. *
  1235. * The HBar chart Grow effect gradually increases the values of the bars
  1236. *
  1237. * @param object OPTIONAL Options for the effect. You can pass frames here
  1238. * @param function OPTIONAL A callback function
  1239. */
  1240. this.grow = function ()
  1241. {
  1242. var obj = this;
  1243. var opt = arguments[0] || {};
  1244. var frames = opt.frames || 30;
  1245. var frame = 0;
  1246. var callback = arguments[1] || function () {};
  1247. // Save the data
  1248. obj.original_data = RG.array_clone(obj.data);
  1249. // Stop the scale from changing by setting chart.ymax (if it's not already set)
  1250. if (obj.Get('chart.xmax') == 0) {
  1251. var xmax = 0;
  1252. for (var i=0; i<obj.data.length; ++i) {
  1253. if (RG.is_array(obj.data[i]) && obj.Get('chart.grouping') == 'stacked') {
  1254. xmax = Math.max(xmax, RG.array_sum(obj.data[i]));
  1255. } else if (RG.is_array(obj.data[i]) && obj.Get('chart.grouping') == 'grouped') {
  1256. xmax = ma.max(xmax, RG.array_max(obj.data[i]));
  1257. } else {
  1258. xmax = ma.max(xmax, RG.array_max(obj.data[i]));
  1259. }
  1260. }
  1261. var scale2 = RG.getScale2(obj, {'max':xmax});
  1262. obj.Set('chart.xmax', scale2.max);
  1263. }
  1264. function iterator ()
  1265. {
  1266. // Alter the Bar chart data depending on the frame
  1267. for (var j=0,len=obj.original_data.length; j<len; ++j) {
  1268. // This stops the animation from being completely linear
  1269. var easingFactor = RG.Effects.getEasingMultiplier(frames, frame);
  1270. if (typeof obj.data[j] === 'object') {
  1271. for (var k=0,len2=obj.data[j].length; k<len2; ++k) {
  1272. obj.data[j][k] = obj.original_data[j][k] * easingFactor;
  1273. }
  1274. } else {
  1275. obj.data[j] = obj.original_data[j] * easingFactor;
  1276. }
  1277. }
  1278. RG.redrawCanvas(obj.canvas);
  1279. if (frame < frames) {
  1280. frame += 1;
  1281. RG.Effects.updateCanvas(iterator);
  1282. } else {
  1283. callback(obj);
  1284. }
  1285. }
  1286. iterator();
  1287. return this;
  1288. };
  1289. /**
  1290. * Charts are now always registered
  1291. */
  1292. RG.Register(this);
  1293. };