You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2669 lines
92 KiB

  1. /**
  2. * o------------------------------------------------------------------------------o
  3. * | This file is part of the RGraph package - you can learn more at: |
  4. * | |
  5. * | http://www.rgraph.net |
  6. * | |
  7. * | This package is licensed under the RGraph license. For all kinds of business |
  8. * | purposes there is a small one-time licensing fee to pay and for non |
  9. * | commercial purposes it is free to use. You can read the full license here: |
  10. * | |
  11. * | http://www.rgraph.net/LICENSE.txt |
  12. * o------------------------------------------------------------------------------o
  13. */
  14. /**
  15. * Initialise the various objects
  16. */
  17. if (typeof(RGraph) == 'undefined') RGraph = {isRGraph:true,type:'common'};
  18. RGraph.Registry = {};
  19. RGraph.Registry.store = [];
  20. RGraph.Registry.store['chart.event.handlers'] = [];
  21. RGraph.Registry.store['__rgraph_event_listeners__'] = []; // Used in the new system for tooltips
  22. RGraph.background = {};
  23. RGraph.objects = [];
  24. RGraph.Resizing = {};
  25. RGraph.events = [];
  26. RGraph.cursor = [];
  27. RGraph.DOM2Events = {};
  28. RGraph.ObjectRegistry = {};
  29. RGraph.ObjectRegistry.objects = {};
  30. RGraph.ObjectRegistry.objects.byUID = [];
  31. RGraph.ObjectRegistry.objects.byCanvasID = [];
  32. /**
  33. * Some "constants"
  34. */
  35. HALFPI = (Math.PI / 2);
  36. PI = Math.PI;
  37. TWOPI = PI * 2;
  38. /**
  39. * Returns five values which are used as a nice scale
  40. *
  41. * @param max int The maximum value of the graph
  42. * @param obj object The graph object
  43. * @return array An appropriate scale
  44. */
  45. RGraph.getScale = function (max, obj)
  46. {
  47. /**
  48. * Special case for 0
  49. */
  50. if (max == 0) {
  51. return ['0.2', '0.4', '0.6', '0.8', '1.0'];
  52. }
  53. var original_max = max;
  54. /**
  55. * Manually do decimals
  56. */
  57. if (max <= 1) {
  58. if (max > 0.5) {
  59. return [0.2,0.4,0.6,0.8, Number(1).toFixed(1)];
  60. } else if (max >= 0.1) {
  61. return obj.Get('chart.scale.round') ? [0.2,0.4,0.6,0.8,1] : [0.1,0.2,0.3,0.4,0.5];
  62. } else {
  63. var tmp = max;
  64. var exp = 0;
  65. while (tmp < 1.01) {
  66. exp += 1;
  67. tmp *= 10;
  68. }
  69. var ret = ['2e-' + exp, '4e-' + exp, '6e-' + exp, '8e-' + exp, '10e-' + exp];
  70. if (max <= ('5e-' + exp)) {
  71. ret = ['1e-' + exp, '2e-' + exp, '3e-' + exp, '4e-' + exp, '5e-' + exp];
  72. }
  73. return ret;
  74. }
  75. }
  76. // Take off any decimals
  77. if (String(max).indexOf('.') > 0) {
  78. max = String(max).replace(/\.\d+$/, '');
  79. }
  80. var interval = Math.pow(10, Number(String(Number(max)).length - 1));
  81. var topValue = interval;
  82. while (topValue < max) {
  83. topValue += (interval / 2);
  84. }
  85. // Handles cases where the max is (for example) 50.5
  86. if (Number(original_max) > Number(topValue)) {
  87. topValue += (interval / 2);
  88. }
  89. // Custom if the max is greater than 5 and less than 10
  90. if (max < 10) {
  91. topValue = (Number(original_max) <= 5 ? 5 : 10);
  92. }
  93. /**
  94. * Added 02/11/2010 to create "nicer" scales
  95. */
  96. if (obj && typeof(obj.Get('chart.scale.round')) == 'boolean' && obj.Get('chart.scale.round')) {
  97. topValue = 10 * interval;
  98. }
  99. return [topValue * 0.2, topValue * 0.4, topValue * 0.6, topValue * 0.8, topValue];
  100. }
  101. /**
  102. * Returns the maximum numeric value which is in an array
  103. *
  104. * @param array arr The array (can also be a number, in which case it's returned as-is)
  105. * @param int Whether to ignore signs (ie negative/positive)
  106. * @return int The maximum value in the array
  107. */
  108. RGraph.array_max = function (arr)
  109. {
  110. var max = null;
  111. if (typeof(arr) == 'number') {
  112. return arr;
  113. }
  114. for (var i=0; i<arr.length; ++i) {
  115. if (typeof(arr[i]) == 'number') {
  116. var val = arguments[1] ? Math.abs(arr[i]) : arr[i];
  117. if (typeof(max) == 'number') {
  118. max = Math.max(max, val);
  119. } else {
  120. max = val;
  121. }
  122. }
  123. }
  124. return max;
  125. }
  126. /**
  127. * Returns the maximum value which is in an array
  128. *
  129. * @param array arr The array
  130. * @param int len The length to pad the array to
  131. * @param mixed The value to use to pad the array (optional)
  132. */
  133. RGraph.array_pad = function (arr, len)
  134. {
  135. if (arr.length < len) {
  136. var val = arguments[2] ? arguments[2] : null;
  137. for (var i=arr.length; i<len; ++i) {
  138. arr[i] = val;
  139. }
  140. }
  141. return arr;
  142. }
  143. /**
  144. * An array sum function
  145. *
  146. * @param array arr The array to calculate the total of
  147. * @return int The summed total of the arrays elements
  148. */
  149. RGraph.array_sum = function (arr)
  150. {
  151. // Allow integers
  152. if (typeof(arr) == 'number') {
  153. return arr;
  154. }
  155. var i, sum;
  156. var len = arr.length;
  157. for(i=0,sum=0;i<len;sum+=arr[i++]);
  158. return sum;
  159. }
  160. /**
  161. * Takes any number of arguments and adds them to one big linear array
  162. * which is then returned
  163. *
  164. * @param ... mixed The data to linearise. You can strings, booleans, numbers or arrays
  165. */
  166. RGraph.array_linearize = function ()
  167. {
  168. var arr = [];
  169. for (var i=0; i<arguments.length; ++i) {
  170. if (typeof(arguments[i]) == 'object' && arguments[i]) {
  171. for (var j=0; j<arguments[i].length; ++j) {
  172. var sub = RGraph.array_linearize(arguments[i][j]);
  173. for (var k=0; k<sub.length; ++k) {
  174. arr.push(sub[k]);
  175. }
  176. }
  177. } else {
  178. arr.push(arguments[i]);
  179. }
  180. }
  181. return arr;
  182. }
  183. /**
  184. * This is a useful function which is basically a shortcut for drawing left, right, top and bottom alligned text.
  185. *
  186. * @param object context The context
  187. * @param string font The font
  188. * @param int size The size of the text
  189. * @param int x The X coordinate
  190. * @param int y The Y coordinate
  191. * @param string text The text to draw
  192. * @parm string The vertical alignment. Can be null. "center" gives center aligned text, "top" gives top aligned text.
  193. * Anything else produces bottom aligned text. Default is bottom.
  194. * @param string The horizontal alignment. Can be null. "center" gives center aligned text, "right" gives right aligned text.
  195. * Anything else produces left aligned text. Default is left.
  196. * @param bool Whether to show a bounding box around the text. Defaults not to
  197. * @param int The angle that the text should be rotate at (IN DEGREES)
  198. * @param string Background color for the text
  199. * @param bool Whether the text is bold or not
  200. * @param bool Whether the bounding box has a placement indicator
  201. */
  202. RGraph.Text = function (context, font, size, x, y, text)
  203. {
  204. // Handle undefined - change it to an empty string
  205. if ((typeof(text) != 'string' && typeof(text) != 'number') || text == 'undefined') {
  206. return;
  207. }
  208. /**
  209. * Adjust the vertical Y coord if it should be vertically centered
  210. */
  211. if (typeof(text) == 'string') {
  212. if (text.indexOf('\r\n') > 0 && arguments[6] == 'center') {
  213. var line_count = text.split('\r\n').length;
  214. y = y - ((line_count - 1.5) * size);
  215. } else if (text.indexOf('\r\n') > 0 && arguments[6] == 'bottom') {
  216. var line_count = text.split('\r\n').length;
  217. y = y - ((line_count - 0.5) * size);
  218. }
  219. }
  220. /**
  221. * This calls the text function recursively to accommodate multi-line text
  222. */
  223. if (typeof(text) == 'string' && text.match(/\r\n/)) {
  224. var arr = text.split('\r\n');
  225. text = arr[0];
  226. arr = RGraph.array_shift(arr);
  227. var nextline = arr.join('\r\n')
  228. RGraph.Text(context,
  229. font,
  230. size,
  231. arguments[9] == -90 ? (x + (size * 1.5)) : x,
  232. y + (size * 1.5),
  233. nextline,
  234. arguments[6] ? arguments[6] : null,
  235. arguments[7],
  236. arguments[8],
  237. arguments[9],
  238. arguments[10],
  239. arguments[11],
  240. arguments[12]);
  241. }
  242. // Accommodate MSIE
  243. if (RGraph.isOld()) {
  244. y += 2;
  245. }
  246. context.font = (arguments[11] ? 'Bold ': '') + size + 'pt ' + font;
  247. var i;
  248. var origX = x;
  249. var origY = y;
  250. var originalFillStyle = context.fillStyle;
  251. var originalLineWidth = context.lineWidth;
  252. // Need these now the angle can be specified, ie defaults for the former two args
  253. if (typeof(arguments[6]) == null) arguments[6] = 'bottom'; // Vertical alignment. Default to bottom/baseline
  254. if (typeof(arguments[7]) == null) arguments[7] = 'left'; // Horizontal alignment. Default to left
  255. if (typeof(arguments[8]) == null) arguments[8] = null; // Show a bounding box. Useful for positioning during development. Defaults to false
  256. if (typeof(arguments[9]) == null) arguments[9] = 0; // Angle (IN DEGREES) that the text should be drawn at. 0 is middle right, and it goes clockwise
  257. if (typeof(arguments[12]) == null) arguments[12] = true; // Whether the bounding box has the placement indicator
  258. // The alignment is recorded here for purposes of Opera compatibility
  259. if (navigator.userAgent.indexOf('Opera') != -1) {
  260. context.canvas.__rgraph_valign__ = arguments[6];
  261. context.canvas.__rgraph_halign__ = arguments[7];
  262. }
  263. // First, translate to x/y coords
  264. context.save();
  265. context.canvas.__rgraph_originalx__ = x;
  266. context.canvas.__rgraph_originaly__ = y;
  267. context.translate(x, y);
  268. x = 0;
  269. y = 0;
  270. // Rotate the canvas if need be
  271. if (arguments[9]) {
  272. context.rotate(arguments[9] / 57.3);
  273. }
  274. // Vertical alignment - defaults to bottom
  275. if (arguments[6]) {
  276. var vAlign = arguments[6];
  277. if (vAlign == 'center') {
  278. context.translate(0, size / 2);
  279. } else if (vAlign == 'top') {
  280. context.translate(0, size);
  281. }
  282. }
  283. // Hoeizontal alignment - defaults to left
  284. if (arguments[7]) {
  285. var hAlign = arguments[7];
  286. var width = context.measureText(text).width;
  287. if (hAlign) {
  288. if (hAlign == 'center') {
  289. context.translate(-1 * (width / 2), 0)
  290. } else if (hAlign == 'right') {
  291. context.translate(-1 * width, 0)
  292. }
  293. }
  294. }
  295. context.fillStyle = originalFillStyle;
  296. /**
  297. * Draw a bounding box if requested
  298. */
  299. context.save();
  300. context.fillText(text,0,0);
  301. context.lineWidth = 1;
  302. if (arguments[8]) {
  303. var width = context.measureText(text).width;
  304. var ieOffset = RGraph.isIE8() ? 2 : 0;
  305. context.translate(x, y);
  306. context.strokeRect(AA(context.canvas.__object__, - 3), AA(context.canvas.__object__, 0 - 3 - size - ieOffset), width + 6, 0 + size + 6);
  307. /**
  308. * If requested, draw a background for the text
  309. */
  310. if (arguments[10]) {
  311. var offset = 3;
  312. var ieOffset = RGraph.isIE8() ? 2 : 0;
  313. var width = context.measureText(text).width
  314. //context.strokeStyle = 'gray';
  315. context.fillStyle = arguments[10];
  316. context.fillRect(AA(context.canvas.__object__, x - offset),
  317. AA(context.canvas.__object__, y - size - offset - ieOffset),
  318. width + (2 * offset),
  319. size + (2 * offset));
  320. //context.strokeRect(x - offset, y - size - offset - ieOffset, width + (2 * offset), size + (2 * offset));
  321. }
  322. /**
  323. * Do the actual drawing of the text
  324. */
  325. context.fillStyle = originalFillStyle;
  326. //if (arguments[6] = 'center') {
  327. // context.ttextBaseline = 'middle';
  328. //}
  329. context.fillText(text,0,0);
  330. if (arguments[12]) {
  331. context.fillRect(
  332. arguments[7] == 'left' ? 0 : (arguments[7] == 'center' ? width / 2 : width ) - 2,
  333. arguments[6] == 'bottom' ? 0 : (arguments[6] == 'center' ? (0 - size) / 2 : 0 - size) - 2,
  334. 4,
  335. 4
  336. );
  337. }
  338. }
  339. context.restore();
  340. // Reset the lineWidth
  341. context.lineWidth = originalLineWidth;
  342. context.restore();
  343. }
  344. /**
  345. * Clears the canvas by setting the width. You can specify a colour if you wish.
  346. *
  347. * @param object canvas The canvas to clear
  348. */
  349. RGraph.Clear = function (canvas)
  350. {
  351. if (!canvas) {
  352. return;
  353. }
  354. RGraph.FireCustomEvent(canvas.__object__, 'onbeforeclear');
  355. var context = canvas.getContext('2d');
  356. var color = arguments[1];
  357. if (RGraph.isIE8() && !color) {
  358. color = 'white';
  359. }
  360. /**
  361. * Can now clear the canvas back to fully transparent
  362. */
  363. if (!color || (color && color == 'transparent')) {
  364. context.clearRect(0,0,canvas.width, canvas.height);
  365. // Reset the globalCompositeOperation
  366. context.globalCompositeOperation = 'source-over';
  367. } else {
  368. context.fillStyle = color;
  369. context = canvas.getContext('2d');
  370. context.beginPath();
  371. if (RGraph.isIE8()) {
  372. context.fillRect(0,0,canvas.width,canvas.height);
  373. } else {
  374. context.fillRect(-10,-10,canvas.width + 20,canvas.height + 20);
  375. }
  376. context.fill();
  377. }
  378. if (RGraph.ClearAnnotations) {
  379. //RGraph.ClearAnnotations(canvas.id);
  380. }
  381. /**
  382. * This removes any background image that may be present
  383. */
  384. if (RGraph.Registry.Get('chart.background.image.' + canvas.id)) {
  385. var img = RGraph.Registry.Get('chart.background.image.' + canvas.id);
  386. img.style.position = 'absolute';
  387. img.style.left = '-10000px';
  388. img.style.top = '-10000px';
  389. }
  390. /**
  391. * This hides the tooltip that is showing IF it has the same canvas ID as
  392. * that which is being cleared
  393. */
  394. if (RGraph.Registry.Get('chart.tooltip')) {
  395. RGraph.HideTooltip();
  396. //RGraph.Redraw();
  397. }
  398. /**
  399. * Set the cursor to default
  400. */
  401. canvas.style.cursor = 'default';
  402. RGraph.FireCustomEvent(canvas.__object__, 'onclear');
  403. }
  404. /**
  405. * Draws the title of the graph
  406. *
  407. * @param object canvas The canvas object
  408. * @param string text The title to write
  409. * @param integer gutter The size of the gutter
  410. * @param integer The center X point (optional - if not given it will be generated from the canvas width)
  411. * @param integer Size of the text. If not given it will be 14
  412. */
  413. RGraph.DrawTitle = function (obj, text, gutterTop)
  414. {
  415. var canvas = obj.canvas;
  416. var context = obj.context;
  417. var gutterLeft = obj.Get('chart.gutter.left');
  418. var gutterRight = obj.Get('chart.gutter.right');
  419. var gutterBottom = obj.Get('chart.gutter.bottom');
  420. var size = arguments[4] ? arguments[4] : 12;
  421. var bold = obj.Get('chart.title.bold');
  422. var centerx = (arguments[3] ? arguments[3] : ((obj.canvas.width - gutterLeft - gutterRight) / 2) + gutterLeft);
  423. var keypos = obj.Get('chart.key.position');
  424. var vpos = obj.Get('chart.title.vpos');
  425. var hpos = obj.Get('chart.title.hpos');
  426. var bgcolor = obj.Get('chart.title.background');
  427. // Account for 3D effect by faking the key position
  428. if (obj.type == 'bar' && obj.Get('chart.variant') == '3d') {
  429. keypos = 'gutter';
  430. }
  431. context.beginPath();
  432. context.fillStyle = obj.Get('chart.text.color') ? obj.Get('chart.text.color') : 'black';
  433. /**
  434. * Vertically center the text if the key is not present
  435. */
  436. if (keypos && keypos != 'gutter') {
  437. var vCenter = 'center';
  438. } else if (!keypos) {
  439. var vCenter = 'center';
  440. } else {
  441. var vCenter = 'bottom';
  442. }
  443. // if chart.title.vpos does not equal 0.5, use that
  444. if (typeof(obj.Get('chart.title.vpos')) == 'number') {
  445. vpos = obj.Get('chart.title.vpos') * gutterTop;
  446. if (obj.Get('chart.xaxispos') == 'top') {
  447. vpos = obj.Get('chart.title.vpos') * gutterBottom + gutterTop + (obj.canvas.height - gutterTop - gutterBottom);
  448. }
  449. } else {
  450. vpos = gutterTop - size - 5;
  451. if (obj.Get('chart.xaxispos') == 'top') {
  452. vpos = obj.canvas.height - gutterBottom + size + 5;
  453. }
  454. }
  455. // if chart.title.hpos is a number, use that. It's multiplied with the (entire) canvas width
  456. if (typeof(hpos) == 'number') {
  457. centerx = hpos * canvas.width;
  458. }
  459. // Set the colour
  460. if (typeof(obj.Get('chart.title.color') != null)) {
  461. var oldColor = context.fillStyle
  462. var newColor = obj.Get('chart.title.color')
  463. context.fillStyle = newColor ? newColor : 'black';
  464. }
  465. /**
  466. * Default font is Verdana
  467. */
  468. var font = obj.Get('chart.text.font');
  469. /**
  470. * Override the default font with chart.title.font
  471. */
  472. if (typeof(obj.Get('chart.title.font')) == 'string') {
  473. font = obj.Get('chart.title.font');
  474. }
  475. /**
  476. * Draw the title itself
  477. */
  478. RGraph.Text(context, font, size, centerx, vpos, text, vCenter, 'center', bgcolor != null, null, bgcolor, bold);
  479. // Reset the fill colour
  480. context.fillStyle = oldColor;
  481. }
  482. /**
  483. * This function returns the mouse position in relation to the canvas
  484. *
  485. * @param object e The event object.
  486. *
  487. RGraph.getMouseXY = function (e)
  488. {
  489. var el = ((RGraph.isIE8() || RGraph.isIE7()) ? event.srcElement : e.target);
  490. var x;
  491. var y;
  492. // ???
  493. var paddingLeft = el.style.paddingLeft ? parseInt(el.style.paddingLeft) : 0;
  494. var paddingTop = el.style.paddingTop ? parseInt(el.style.paddingTop) : 0;
  495. var borderLeft = el.style.borderLeftWidth ? parseInt(el.style.borderLeftWidth) : 0;
  496. var borderTop = el.style.borderTopWidth ? parseInt(el.style.borderTopWidth) : 0;
  497. if (RGraph.isIE8()) e = event;
  498. // Browser with offsetX and offsetY
  499. if (typeof(e.offsetX) == 'number' && typeof(e.offsetY) == 'number') {
  500. x = e.offsetX;
  501. y = e.offsetY;
  502. // FF and other
  503. } else {
  504. x = 0;
  505. y = 0;
  506. while (el != document.body && el) {
  507. x += el.offsetLeft;
  508. y += el.offsetTop;
  509. el = el.offsetParent;
  510. }
  511. x = e.pageX - x;
  512. y = e.pageY - y;
  513. }
  514. return [x, y];
  515. }*/
  516. RGraph.getMouseXY = function(e)
  517. {
  518. var el = e.target;
  519. var ca = el;
  520. var offsetX = 0;
  521. var offsetY = 0;
  522. var x;
  523. var y;
  524. // Add padding and border style widths to offset
  525. var additionalX = (parseInt(ca.style.borderLeftWidth) || 0) + (parseInt(ca.style.paddingLeft) || 0);
  526. var additionalY = (parseInt(ca.style.borderTopWidth) || 0) + (parseInt(ca.style.paddingTop) || 0);
  527. if (typeof(e.offsetX) == 'number' && typeof(e.offsetY) == 'number') {
  528. x = e.offsetX - additionalX;
  529. y = e.offsetY - additionalY;
  530. } else {
  531. if (typeof(el.offsetParent) != 'undefined') {
  532. do {
  533. offsetX += el.offsetLeft;
  534. offsetY += el.offsetTop;
  535. } while ((el = el.offsetParent));
  536. }
  537. x = e.pageX - offsetX - additionalX;
  538. y = e.pageY - offsetY - additionalY;
  539. }
  540. // We return a simple javascript object (a hash) with x and y defined
  541. return [x, y];
  542. }
  543. /**
  544. * This function returns a two element array of the canvas x/y position in
  545. * relation to the page
  546. *
  547. * @param object canvas
  548. */
  549. RGraph.getCanvasXY = function (canvas)
  550. {
  551. var x = 0;
  552. var y = 0;
  553. var obj = canvas;
  554. do {
  555. x += obj.offsetLeft;
  556. y += obj.offsetTop;
  557. obj = obj.offsetParent;
  558. } while (obj && obj.tagName.toLowerCase() != 'body');
  559. var paddingLeft = canvas.style.paddingLeft ? parseInt(canvas.style.paddingLeft) : 0;
  560. var paddingTop = canvas.style.paddingTop ? parseInt(canvas.style.paddingTop) : 0;
  561. var borderLeft = canvas.style.borderLeftWidth ? parseInt(canvas.style.borderLeftWidth) : 0;
  562. var borderTop = canvas.style.borderTopWidth ? parseInt(canvas.style.borderTopWidth) : 0;
  563. return [x + paddingLeft + borderLeft, y + paddingTop + borderTop];
  564. }
  565. /**
  566. * Registers a graph object (used when the canvas is redrawn)
  567. *
  568. * @param object obj The object to be registered
  569. */
  570. RGraph.Register = function (obj)
  571. {
  572. // Checking this property ensures the object is only registered once
  573. if (!obj.Get('chart.noregister')) {
  574. // As of 21st/1/2012 the object registry is now used
  575. RGraph.ObjectRegistry.Add(obj);
  576. obj.Set('chart.noregister', true);
  577. }
  578. }
  579. /**
  580. * Causes all registered objects to be redrawn
  581. *
  582. * @param string An optional color to use to clear the canvas
  583. */
  584. RGraph.Redraw = function ()
  585. {
  586. var objectRegistry = RGraph.ObjectRegistry.objects.byCanvasID;
  587. // Get all of the canvas tags on the page
  588. var tags = document.getElementsByTagName('canvas');
  589. for (var i=0; i<tags.length; ++i) {
  590. if (tags[i].__object__ && tags[i].__object__.isRGraph) {
  591. RGraph.Clear(tags[i], arguments[0] ? arguments[0] : null);
  592. }
  593. }
  594. // Go through the object registry and redraw *all* of the canvas'es that have been registered
  595. for (var i=0; i<objectRegistry.length; ++i) {
  596. if (objectRegistry[i]) {
  597. var id = objectRegistry[i][0];
  598. objectRegistry[i][1].Draw();
  599. }
  600. }
  601. }
  602. /**
  603. * Causes all registered objects ON THE GIVEN CANVAS to be redrawn
  604. *
  605. * @param canvas object The canvas object to redraw
  606. * @param bool Optional boolean which defaults to true and determines whether to clear the canvas
  607. */
  608. RGraph.RedrawCanvas = function (canvas)
  609. {
  610. var objects = RGraph.ObjectRegistry.getObjectsByCanvasID(canvas.id);
  611. /**
  612. * First clear the canvas
  613. */
  614. if (!arguments[1] || (typeof(arguments[1]) == 'boolean' && !arguments[1] == false) ) {
  615. RGraph.Clear(canvas);
  616. }
  617. /**
  618. * Now redraw all the charts associated with that canvas
  619. */
  620. for (var i=0; i<objects.length; ++i) {
  621. if (objects[i]) {
  622. if (objects[i] && objects[i].isRGraph) { // Is it an RGraph object ??
  623. objects[i].Draw();
  624. }
  625. }
  626. }
  627. }
  628. /**
  629. * This function draws the background for the bar chart, line chart and scatter chart.
  630. *
  631. * @param object obj The graph object
  632. */
  633. RGraph.background.Draw = function (obj)
  634. {
  635. var canvas = obj.canvas;
  636. var context = obj.context;
  637. var height = 0;
  638. var gutterLeft = obj.Get('chart.gutter.left');
  639. var gutterRight = obj.Get('chart.gutter.right');
  640. var gutterTop = obj.Get('chart.gutter.top');
  641. var gutterBottom = obj.Get('chart.gutter.bottom');
  642. var variant = obj.Get('chart.variant');
  643. context.fillStyle = obj.Get('chart.text.color');
  644. // If it's a bar and 3D variant, translate
  645. if (variant == '3d') {
  646. context.save();
  647. context.translate(10, -5);
  648. }
  649. // X axis title
  650. if (typeof(obj.Get('chart.title.xaxis')) == 'string' && obj.Get('chart.title.xaxis').length) {
  651. var size = obj.Get('chart.text.size') + 2;
  652. var font = obj.Get('chart.text.font');
  653. var bold = obj.Get('chart.title.xaxis.bold');
  654. if (typeof(obj.Get('chart.title.xaxis.size')) == 'number') {
  655. size = obj.Get('chart.title.xaxis.size');
  656. }
  657. if (typeof(obj.Get('chart.title.xaxis.font')) == 'string') {
  658. font = obj.Get('chart.title.xaxis.font');
  659. }
  660. var hpos = ((obj.canvas.width - obj.gutterLeft - obj.gutterRight) / 2) + obj.gutterLeft;
  661. var vpos = obj.canvas.height - obj.Get('chart.gutter.bottom') + 25;
  662. if (typeof(obj.Get('chart.title.xaxis.pos')) == 'number') {
  663. vpos = obj.canvas.height - (gutterBottom * obj.Get('chart.title.xaxis.pos'));
  664. }
  665. context.beginPath();
  666. RGraph.Text(context,
  667. font,
  668. size,
  669. hpos,
  670. vpos,
  671. obj.Get('chart.title.xaxis'),
  672. 'center',
  673. 'center',
  674. false,
  675. false,
  676. false,
  677. bold);
  678. context.fill();
  679. }
  680. // Y axis title
  681. if (typeof(obj.Get('chart.title.yaxis')) == 'string' && obj.Get('chart.title.yaxis').length) {
  682. var size = obj.Get('chart.text.size') + 2;
  683. var font = obj.Get('chart.text.font');
  684. var angle = 270;
  685. var bold = obj.Get('chart.title.yaxis.bold');
  686. var color = obj.Get('chart.title.yaxis.color');
  687. if (typeof(obj.Get('chart.title.yaxis.pos')) == 'number') {
  688. var yaxis_title_pos = obj.Get('chart.title.yaxis.pos') * obj.Get('chart.gutter.left');
  689. } else {
  690. var yaxis_title_pos = ((obj.Get('chart.gutter.left') - 25) / obj.Get('chart.gutter.left')) * obj.Get('chart.gutter.left');
  691. }
  692. if (typeof(obj.Get('chart.title.yaxis.size')) == 'number') {
  693. size = obj.Get('chart.title.yaxis.size');
  694. }
  695. if (typeof(obj.Get('chart.title.yaxis.font')) == 'string') {
  696. font = obj.Get('chart.title.yaxis.font');
  697. }
  698. if (obj.Get('chart.title.yaxis.align') == 'right' || obj.Get('chart.title.yaxis.position') == 'right') {
  699. angle = 90;
  700. yaxis_title_pos = obj.Get('chart.title.yaxis.pos') ? (obj.canvas.width - obj.Get('chart.gutter.right')) + (obj.Get('chart.title.yaxis.pos') * obj.Get('chart.gutter.right')) :
  701. obj.canvas.width - obj.Get('chart.gutter.right') + obj.Get('chart.text.size') + 5;
  702. } else {
  703. yaxis_title_pos = yaxis_title_pos;
  704. }
  705. context.beginPath();
  706. context.fillStyle = color;
  707. RGraph.Text(context,
  708. font,
  709. size,
  710. yaxis_title_pos,
  711. ((obj.canvas.height - obj.gutterTop - obj.gutterBottom) / 2) + obj.gutterTop,
  712. obj.Get('chart.title.yaxis'),
  713. 'center',
  714. 'center',
  715. false,
  716. angle,
  717. false,
  718. bold);
  719. context.fill();
  720. }
  721. obj.context.beginPath();
  722. // Draw the horizontal bars
  723. context.fillStyle = obj.Get('chart.background.barcolor1');
  724. height = (obj.canvas.height - gutterBottom);
  725. for (var i=gutterTop; i < height ; i+=80) {
  726. obj.context.fillRect(gutterLeft, i, RGraph.GetWidth(obj) - gutterLeft - gutterRight, Math.min(40, RGraph.GetHeight(obj) - gutterBottom - i) );
  727. }
  728. context.fillStyle = obj.Get('chart.background.barcolor2');
  729. height = (RGraph.GetHeight(obj) - gutterBottom);
  730. for (var i= (40 + gutterTop); i < height; i+=80) {
  731. obj.context.fillRect(gutterLeft, i, RGraph.GetWidth(obj) - gutterLeft - gutterRight, i + 40 > (RGraph.GetHeight(obj) - gutterBottom) ? RGraph.GetHeight(obj) - (gutterBottom + i) : 40);
  732. }
  733. context.stroke();
  734. // Draw the background grid
  735. if (obj.Get('chart.background.grid')) {
  736. // If autofit is specified, use the .numhlines and .numvlines along with the width to work
  737. // out the hsize and vsize
  738. if (obj.Get('chart.background.grid.autofit')) {
  739. /**
  740. * Align the grid to the tickmarks
  741. */
  742. if (obj.Get('chart.background.grid.autofit.align')) {
  743. // Align the horizontal lines
  744. obj.Set('chart.background.grid.autofit.numhlines', obj.Get('chart.ylabels.count'));
  745. // Align the vertical lines for the line
  746. if (obj.type == 'line') {
  747. if (obj.Get('chart.labels') && obj.Get('chart.labels').length) {
  748. obj.Set('chart.background.grid.autofit.numvlines', obj.Get('chart.labels').length - 1);
  749. } else {
  750. obj.Set('chart.background.grid.autofit.numvlines', obj.data[0].length - 1);
  751. }
  752. // Align the vertical lines for the bar
  753. } else if (obj.type == 'bar' && obj.Get('chart.labels') && obj.Get('chart.labels').length) {
  754. obj.Set('chart.background.grid.autofit.numvlines', obj.Get('chart.labels').length);
  755. }
  756. }
  757. var vsize = ((obj.canvas.width - gutterLeft - gutterRight)) / obj.Get('chart.background.grid.autofit.numvlines');
  758. var hsize = (obj.canvas.height - gutterTop - gutterBottom) / obj.Get('chart.background.grid.autofit.numhlines');
  759. obj.Set('chart.background.grid.vsize', vsize);
  760. obj.Set('chart.background.grid.hsize', hsize);
  761. }
  762. context.beginPath();
  763. context.lineWidth = obj.Get('chart.background.grid.width') ? obj.Get('chart.background.grid.width') : 1;
  764. context.strokeStyle = obj.Get('chart.background.grid.color');
  765. // Draw the horizontal lines
  766. if (obj.Get('chart.background.grid.hlines')) {
  767. height = (RGraph.GetHeight(obj) - gutterBottom)
  768. for (y=gutterTop; y<height; y+=obj.Get('chart.background.grid.hsize')) {
  769. context.moveTo(gutterLeft, AA(this, y));
  770. context.lineTo(RGraph.GetWidth(obj) - gutterRight, AA(this, y));
  771. }
  772. }
  773. if (obj.Get('chart.background.grid.vlines')) {
  774. // Draw the vertical lines
  775. var width = (obj.canvas.width - gutterRight)
  776. for (x=gutterLeft; x<=width; x+=obj.Get('chart.background.grid.vsize')) {
  777. context.moveTo(AA(this, x), gutterTop);
  778. context.lineTo(AA(this, x), obj.canvas.height - gutterBottom);
  779. }
  780. }
  781. if (obj.Get('chart.background.grid.border')) {
  782. // Make sure a rectangle, the same colour as the grid goes around the graph
  783. context.strokeStyle = obj.Get('chart.background.grid.color');
  784. context.strokeRect(AA(this, gutterLeft), AA(this, gutterTop), RGraph.GetWidth(obj) - gutterLeft - gutterRight, RGraph.GetHeight(obj) - gutterTop - gutterBottom);
  785. }
  786. }
  787. context.stroke();
  788. // If it's a bar and 3D variant, translate
  789. if (variant == '3d') {
  790. context.restore();
  791. }
  792. // Draw the title if one is set
  793. if ( typeof(obj.Get('chart.title')) == 'string') {
  794. if (obj.type == 'gantt') {
  795. gutterTop -= 10;
  796. }
  797. RGraph.DrawTitle(obj,
  798. obj.Get('chart.title'),
  799. gutterTop,
  800. null,
  801. obj.Get('chart.title.size') ? obj.Get('chart.title.size') : obj.Get('chart.text.size') + 2);
  802. }
  803. context.stroke();
  804. }
  805. /**
  806. * Returns the day number for a particular date. Eg 1st February would be 32
  807. *
  808. * @param object obj A date object
  809. * @return int The day number of the given date
  810. */
  811. RGraph.GetDays = function (obj)
  812. {
  813. var year = obj.getFullYear();
  814. var days = obj.getDate();
  815. var month = obj.getMonth();
  816. if (month == 0) return days;
  817. if (month >= 1) days += 31;
  818. if (month >= 2) days += 28;
  819. // Leap years. Crude, but it functions
  820. if (year >= 2008 && year % 4 == 0) days += 1;
  821. if (month >= 3) days += 31;
  822. if (month >= 4) days += 30;
  823. if (month >= 5) days += 31;
  824. if (month >= 6) days += 30;
  825. if (month >= 7) days += 31;
  826. if (month >= 8) days += 31;
  827. if (month >= 9) days += 30;
  828. if (month >= 10) days += 31;
  829. if (month >= 11) days += 30;
  830. return days;
  831. }
  832. /**
  833. * Makes a clone of an object
  834. *
  835. * @param obj val The object to clone
  836. */
  837. RGraph.array_clone = function (obj)
  838. {
  839. if(obj == null || typeof(obj) != 'object') {
  840. return obj;
  841. }
  842. var temp = [];
  843. for (var i=0;i<obj.length; ++i) {
  844. if (typeof(obj[i]) == 'number') {
  845. temp[i] = (function (arg) {return Number(arg);})(obj[i]);
  846. } else if (typeof(obj[i]) == 'string') {
  847. temp[i] = (function (arg) {return String(arg);})(obj[i]);
  848. } else if (typeof(obj[i]) == 'function') {
  849. temp[i] = obj[i];
  850. } else {
  851. temp[i] = RGraph.array_clone(obj[i]);
  852. }
  853. }
  854. return temp;
  855. }
  856. /**
  857. * Formats a number with thousand seperators so it's easier to read
  858. *
  859. * @param integer obj The chart object
  860. * @param integer num The number to format
  861. * @param string The (optional) string to prepend to the string
  862. * @param string The (optional) string to append to the string
  863. * @return string The formatted number
  864. */
  865. RGraph.number_format = function (obj, num)
  866. {
  867. var i;
  868. var prepend = arguments[2] ? String(arguments[2]) : '';
  869. var append = arguments[3] ? String(arguments[3]) : '';
  870. var output = '';
  871. var decimal = '';
  872. var decimal_seperator = obj.Get('chart.scale.point') ? obj.Get('chart.scale.point') : '.';
  873. var thousand_seperator = obj.Get('chart.scale.thousand') ? obj.Get('chart.scale.thousand') : ',';
  874. RegExp.$1 = '';
  875. var i,j;
  876. if (typeof(obj.Get('chart.scale.formatter')) == 'function') {
  877. return obj.Get('chart.scale.formatter')(obj, num);
  878. }
  879. // Ignore the preformatted version of "1e-2"
  880. if (String(num).indexOf('e') > 0) {
  881. return String(prepend + String(num) + append);
  882. }
  883. // We need then number as a string
  884. num = String(num);
  885. // Take off the decimal part - we re-append it later
  886. if (num.indexOf('.') > 0) {
  887. var tmp = num;
  888. num = num.replace(/\.(.*)/, ''); // The front part of the number
  889. decimal = tmp.replace(/(.*)\.(.*)/, '$2'); // The decimal part of the number
  890. }
  891. // Thousand seperator
  892. //var seperator = arguments[1] ? String(arguments[1]) : ',';
  893. var seperator = thousand_seperator;
  894. /**
  895. * Work backwards adding the thousand seperators
  896. */
  897. var foundPoint;
  898. for (i=(num.length - 1),j=0; i>=0; j++,i--) {
  899. var character = num.charAt(i);
  900. if ( j % 3 == 0 && j != 0) {
  901. output += seperator;
  902. }
  903. /**
  904. * Build the output
  905. */
  906. output += character;
  907. }
  908. /**
  909. * Now need to reverse the string
  910. */
  911. var rev = output;
  912. output = '';
  913. for (i=(rev.length - 1); i>=0; i--) {
  914. output += rev.charAt(i);
  915. }
  916. // Tidy up
  917. //output = output.replace(/^-,/, '-');
  918. if (output.indexOf('-' + obj.Get('chart.scale.thousand')) == 0) {
  919. output = '-' + output.substr(('-' + obj.Get('chart.scale.thousand')).length);
  920. }
  921. // Reappend the decimal
  922. if (decimal.length) {
  923. output = output + decimal_seperator + decimal;
  924. decimal = '';
  925. RegExp.$1 = '';
  926. }
  927. // Minor bugette
  928. if (output.charAt(0) == '-') {
  929. output = output.replace(/-/, '');
  930. prepend = '-' + prepend;
  931. }
  932. return prepend + output + append;
  933. }
  934. /**
  935. * Draws horizontal coloured bars on something like the bar, line or scatter
  936. */
  937. RGraph.DrawBars = function (obj)
  938. {
  939. var hbars = obj.Get('chart.background.hbars');
  940. /**
  941. * Draws a horizontal bar
  942. */
  943. obj.context.beginPath();
  944. for (i=0; i<hbars.length; ++i) {
  945. // If null is specified as the "height", set it to the upper max value
  946. if (hbars[i][1] == null) {
  947. hbars[i][1] = obj.max;
  948. // If the first index plus the second index is greater than the max value, adjust accordingly
  949. } else if (hbars[i][0] + hbars[i][1] > obj.max) {
  950. hbars[i][1] = obj.max - hbars[i][0];
  951. }
  952. // If height is negative, and the abs() value is greater than .max, use a negative max instead
  953. if (Math.abs(hbars[i][1]) > obj.max) {
  954. hbars[i][1] = -1 * obj.max;
  955. }
  956. // If start point is greater than max, change it to max
  957. if (Math.abs(hbars[i][0]) > obj.max) {
  958. hbars[i][0] = obj.max;
  959. }
  960. // If start point plus height is less than negative max, use the negative max plus the start point
  961. if (hbars[i][0] + hbars[i][1] < (-1 * obj.max) ) {
  962. hbars[i][1] = -1 * (obj.max + hbars[i][0]);
  963. }
  964. // If the X axis is at the bottom, and a negative max is given, warn the user
  965. if (obj.Get('chart.xaxispos') == 'bottom' && (hbars[i][0] < 0 || (hbars[i][1] + hbars[i][1] < 0)) ) {
  966. alert('[' + obj.type.toUpperCase() + ' (ID: ' + obj.id + ') BACKGROUND HBARS] You have a negative value in one of your background hbars values, whilst the X axis is in the center');
  967. }
  968. var ystart = (obj.grapharea - (((hbars[i][0] - obj.min) / (obj.max - obj.min)) * obj.grapharea));
  969. var height = (Math.min(hbars[i][1], obj.max - hbars[i][0]) / (obj.max - obj.min)) * obj.grapharea;
  970. // Account for the X axis being in the center
  971. if (obj.Get('chart.xaxispos') == 'center') {
  972. ystart /= 2;
  973. height /= 2;
  974. }
  975. ystart += obj.Get('chart.gutter.top')
  976. var x = obj.Get('chart.gutter.left');
  977. var y = ystart - height;
  978. var w = obj.canvas.width - obj.Get('chart.gutter.left') - obj.Get('chart.gutter.right');
  979. var h = height;
  980. // Accommodate Opera :-/
  981. if (navigator.userAgent.indexOf('Opera') != -1 && obj.Get('chart.xaxispos') == 'center' && h < 0) {
  982. h *= -1;
  983. y = y - h;
  984. }
  985. /**
  986. * Account for X axis at the top
  987. */
  988. if (obj.Get('chart.xaxispos') == 'top') {
  989. y = obj.canvas.height - y;
  990. h *= -1;
  991. }
  992. obj.context.fillStyle = hbars[i][2];
  993. obj.context.fillRect(x, y, w, h);
  994. }
  995. obj.context.fill();
  996. }
  997. /**
  998. * Draws in-graph labels.
  999. *
  1000. * @param object obj The graph object
  1001. */
  1002. RGraph.DrawInGraphLabels = function (obj)
  1003. {
  1004. var canvas = obj.canvas;
  1005. var context = obj.context;
  1006. var labels = obj.Get('chart.labels.ingraph');
  1007. var labels_processed = [];
  1008. // Defaults
  1009. var fgcolor = 'black';
  1010. var bgcolor = 'white';
  1011. var direction = 1;
  1012. if (!labels) {
  1013. return;
  1014. }
  1015. /**
  1016. * Preprocess the labels array. Numbers are expanded
  1017. */
  1018. for (var i=0; i<labels.length; ++i) {
  1019. if (typeof(labels[i]) == 'number') {
  1020. for (var j=0; j<labels[i]; ++j) {
  1021. labels_processed.push(null);
  1022. }
  1023. } else if (typeof(labels[i]) == 'string' || typeof(labels[i]) == 'object') {
  1024. labels_processed.push(labels[i]);
  1025. } else {
  1026. labels_processed.push('');
  1027. }
  1028. }
  1029. /**
  1030. * Turn off any shadow
  1031. */
  1032. RGraph.NoShadow(obj);
  1033. if (labels_processed && labels_processed.length > 0) {
  1034. for (var i=0; i<labels_processed.length; ++i) {
  1035. if (labels_processed[i]) {
  1036. var coords = obj.coords[i];
  1037. if (coords && coords.length > 0) {
  1038. var x = (obj.type == 'bar' ? coords[0] + (coords[2] / 2) : coords[0]);
  1039. var y = (obj.type == 'bar' ? coords[1] + (coords[3] / 2) : coords[1]);
  1040. var length = typeof(labels_processed[i][4]) == 'number' ? labels_processed[i][4] : 25;
  1041. context.beginPath();
  1042. context.fillStyle = 'black';
  1043. context.strokeStyle = 'black';
  1044. if (obj.type == 'bar') {
  1045. /**
  1046. * X axis at the top
  1047. */
  1048. if (obj.Get('chart.xaxispos') == 'top') {
  1049. length *= -1;
  1050. }
  1051. if (obj.Get('chart.variant') == 'dot') {
  1052. context.moveTo(AA(obj, x), obj.coords[i][1] - 5);
  1053. context.lineTo(AA(obj, x), obj.coords[i][1] - 5 - length);
  1054. var text_x = AA(obj, x);
  1055. var text_y = obj.coords[i][1] - 5 - length;
  1056. } else if (obj.Get('chart.variant') == 'arrow') {
  1057. context.moveTo(AA(obj, x), obj.coords[i][1] - 5);
  1058. context.lineTo(AA(obj, x), obj.coords[i][1] - 5 - length);
  1059. var text_x = AA(obj, x);
  1060. var text_y = obj.coords[i][1] - 5 - length;
  1061. } else {
  1062. context.arc(AA(obj, x), y, 2.5, 0, 6.28, 0);
  1063. context.moveTo(AA(obj, x), y);
  1064. context.lineTo(AA(obj, x), y - length);
  1065. var text_x = AA(obj, x);
  1066. var text_y = y - length;
  1067. }
  1068. context.stroke();
  1069. context.fill();
  1070. } else if (obj.type == 'line') {
  1071. if (
  1072. typeof(labels_processed[i]) == 'object' &&
  1073. typeof(labels_processed[i][3]) == 'number' &&
  1074. labels_processed[i][3] == -1
  1075. ) {
  1076. context.moveTo(AA(obj, x), y + 5);
  1077. context.lineTo(AA(obj, x), y + 5 + length);
  1078. context.stroke();
  1079. context.beginPath();
  1080. // This draws the arrow
  1081. context.moveTo(AA(obj, x), y + 5);
  1082. context.lineTo(AA(obj, x) - 3, y + 10);
  1083. context.lineTo(AA(obj, x) + 3, y + 10);
  1084. context.closePath();
  1085. var text_x = x;
  1086. var text_y = y + 5 + length;
  1087. } else {
  1088. var text_x = x;
  1089. var text_y = y - 5 - length;
  1090. context.moveTo(AA(obj, x), y - 5);
  1091. context.lineTo(AA(obj, x), y - 5 - length);
  1092. context.stroke();
  1093. context.beginPath();
  1094. // This draws the arrow
  1095. context.moveTo(AA(obj, x), y - 5);
  1096. context.lineTo(AA(obj, x) - 3, y - 10);
  1097. context.lineTo(AA(obj, x) + 3, y - 10);
  1098. context.closePath();
  1099. }
  1100. context.fill();
  1101. }
  1102. // Taken out on the 10th Nov 2010 - unnecessary
  1103. //var width = context.measureText(labels[i]).width;
  1104. context.beginPath();
  1105. // Fore ground color
  1106. context.fillStyle = (typeof(labels_processed[i]) == 'object' && typeof(labels_processed[i][1]) == 'string') ? labels_processed[i][1] : 'black';
  1107. RGraph.Text(context,
  1108. obj.Get('chart.text.font'),
  1109. obj.Get('chart.text.size'),
  1110. text_x,
  1111. text_y,
  1112. (typeof(labels_processed[i]) == 'object' && typeof(labels_processed[i][0]) == 'string') ? labels_processed[i][0] : labels_processed[i],
  1113. 'bottom',
  1114. 'center',
  1115. true,
  1116. null,
  1117. (typeof(labels_processed[i]) == 'object' && typeof(labels_processed[i][2]) == 'string') ? labels_processed[i][2] : 'white');
  1118. context.fill();
  1119. }
  1120. }
  1121. }
  1122. }
  1123. }
  1124. /**
  1125. * This function "fills in" key missing properties that various implementations lack
  1126. *
  1127. * @param object e The event object
  1128. */
  1129. RGraph.FixEventObject = function (e)
  1130. {
  1131. if (RGraph.isIE8() || RGraph.isIE7()) {
  1132. var e = event;
  1133. e.pageX = (event.clientX + document.body.scrollLeft);
  1134. e.pageY = (event.clientY + document.body.scrollTop);
  1135. e.target = event.srcElement;
  1136. if (!document.body.scrollTop && document.documentElement.scrollTop) {
  1137. e.pageX += parseInt(document.documentElement.scrollLeft);
  1138. e.pageY += parseInt(document.documentElement.scrollTop);
  1139. }
  1140. }
  1141. // This is mainly for FF which doesn't provide offsetX
  1142. if (typeof(e.offsetX) == 'undefined' && typeof(e.offsetY) == 'undefined') {
  1143. var coords = RGraph.getMouseXY(e);
  1144. e.offsetX = coords[0];
  1145. e.offsetY = coords[1];
  1146. }
  1147. // Any browser that doesn't implement stopPropagation() (MSIE)
  1148. if (!e.stopPropagation) {
  1149. e.stopPropagation = function () {window.event.cancelBubble = true;}
  1150. }
  1151. return e;
  1152. }
  1153. /**
  1154. * Thisz function hides the crosshairs coordinates
  1155. */
  1156. RGraph.HideCrosshairCoords = function ()
  1157. {
  1158. var div = RGraph.Registry.Get('chart.coordinates.coords.div');
  1159. if ( div
  1160. && div.style.opacity == 1
  1161. && div.__object__.Get('chart.crosshairs.coords.fadeout')
  1162. ) {
  1163. setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0.9;}, 50);
  1164. setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0.8;}, 100);
  1165. setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0.7;}, 150);
  1166. setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0.6;}, 200);
  1167. setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0.5;}, 250);
  1168. setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0.4;}, 300);
  1169. setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0.3;}, 350);
  1170. setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0.2;}, 400);
  1171. setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0.1;}, 450);
  1172. setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0;}, 500);
  1173. setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.display = 'none';}, 550);
  1174. }
  1175. }
  1176. /**
  1177. * Draws the3D axes/background
  1178. */
  1179. RGraph.Draw3DAxes = function (obj)
  1180. {
  1181. var gutterLeft = obj.Get('chart.gutter.left');
  1182. var gutterRight = obj.Get('chart.gutter.right');
  1183. var gutterTop = obj.Get('chart.gutter.top');
  1184. var gutterBottom = obj.Get('chart.gutter.bottom');
  1185. var context = obj.context;
  1186. var canvas = obj.canvas;
  1187. context.strokeStyle = '#aaa';
  1188. context.fillStyle = '#ddd';
  1189. // Draw the vertical left side
  1190. context.beginPath();
  1191. context.moveTo(gutterLeft, gutterTop);
  1192. context.lineTo(gutterLeft + 10, gutterTop - 5);
  1193. context.lineTo(gutterLeft + 10, canvas.height - gutterBottom - 5);
  1194. context.lineTo(gutterLeft, canvas.height - gutterBottom);
  1195. context.closePath();
  1196. context.stroke();
  1197. context.fill();
  1198. // Draw the bottom floor
  1199. context.beginPath();
  1200. context.moveTo(gutterLeft, canvas.height - gutterBottom);
  1201. context.lineTo(gutterLeft + 10, canvas.height - gutterBottom - 5);
  1202. context.lineTo(canvas.width - gutterRight + 10, canvas.height - gutterBottom - 5);
  1203. context.lineTo(canvas.width - gutterRight, canvas.height - gutterBottom);
  1204. context.closePath();
  1205. context.stroke();
  1206. context.fill();
  1207. }
  1208. /**
  1209. * This function attempts to "fill in" missing functions from the canvas
  1210. * context object. Only two at the moment - measureText() nd fillText().
  1211. *
  1212. * @param object context The canvas 2D context
  1213. */
  1214. RGraph.OldBrowserCompat = function (context)
  1215. {
  1216. if (!context) {
  1217. return;
  1218. }
  1219. if (!context.measureText) {
  1220. // This emulates the measureText() function
  1221. context.measureText = function (text)
  1222. {
  1223. var textObj = document.createElement('DIV');
  1224. textObj.innerHTML = text;
  1225. textObj.style.position = 'absolute';
  1226. textObj.style.top = '-100px';
  1227. textObj.style.left = 0;
  1228. document.body.appendChild(textObj);
  1229. var width = {width: textObj.offsetWidth};
  1230. textObj.style.display = 'none';
  1231. return width;
  1232. }
  1233. }
  1234. if (!context.fillText) {
  1235. // This emulates the fillText() method
  1236. context.fillText = function (text, targetX, targetY)
  1237. {
  1238. return false;
  1239. }
  1240. }
  1241. // If IE8, add addEventListener()
  1242. if (!context.canvas.addEventListener) {
  1243. window.addEventListener = function (ev, func, bubble)
  1244. {
  1245. return this.attachEvent('on' + ev, func);
  1246. }
  1247. context.canvas.addEventListener = function (ev, func, bubble)
  1248. {
  1249. return this.attachEvent('on' + ev, func);
  1250. }
  1251. }
  1252. }
  1253. /**
  1254. * Draws a rectangle with curvy corners
  1255. *
  1256. * @param context object The context
  1257. * @param x number The X coordinate (top left of the square)
  1258. * @param y number The Y coordinate (top left of the square)
  1259. * @param w number The width of the rectangle
  1260. * @param h number The height of the rectangle
  1261. * @param number The radius of the curved corners
  1262. * @param boolean Whether the top left corner is curvy
  1263. * @param boolean Whether the top right corner is curvy
  1264. * @param boolean Whether the bottom right corner is curvy
  1265. * @param boolean Whether the bottom left corner is curvy
  1266. */
  1267. RGraph.strokedCurvyRect = function (context, x, y, w, h)
  1268. {
  1269. // The corner radius
  1270. var r = arguments[5] ? arguments[5] : 3;
  1271. // The corners
  1272. var corner_tl = (arguments[6] || arguments[6] == null) ? true : false;
  1273. var corner_tr = (arguments[7] || arguments[7] == null) ? true : false;
  1274. var corner_br = (arguments[8] || arguments[8] == null) ? true : false;
  1275. var corner_bl = (arguments[9] || arguments[9] == null) ? true : false;
  1276. context.beginPath();
  1277. // Top left side
  1278. context.moveTo(x + (corner_tl ? r : 0), y);
  1279. context.lineTo(x + w - (corner_tr ? r : 0), y);
  1280. // Top right corner
  1281. if (corner_tr) {
  1282. context.arc(x + w - r, y + r, r, PI + HALFPI, TWOPI, false);
  1283. }
  1284. // Top right side
  1285. context.lineTo(x + w, y + h - (corner_br ? r : 0) );
  1286. // Bottom right corner
  1287. if (corner_br) {
  1288. context.arc(x + w - r, y - r + h, r, TWOPI, HALFPI, false);
  1289. }
  1290. // Bottom right side
  1291. context.lineTo(x + (corner_bl ? r : 0), y + h);
  1292. // Bottom left corner
  1293. if (corner_bl) {
  1294. context.arc(x + r, y - r + h, r, HALFPI, PI, false);
  1295. }
  1296. // Bottom left side
  1297. context.lineTo(x, y + (corner_tl ? r : 0) );
  1298. // Top left corner
  1299. if (corner_tl) {
  1300. context.arc(x + r, y + r, r, PI, PI + HALFPI, false);
  1301. }
  1302. context.stroke();
  1303. }
  1304. /**
  1305. * Draws a filled rectangle with curvy corners
  1306. *
  1307. * @param context object The context
  1308. * @param x number The X coordinate (top left of the square)
  1309. * @param y number The Y coordinate (top left of the square)
  1310. * @param w number The width of the rectangle
  1311. * @param h number The height of the rectangle
  1312. * @param number The radius of the curved corners
  1313. * @param boolean Whether the top left corner is curvy
  1314. * @param boolean Whether the top right corner is curvy
  1315. * @param boolean Whether the bottom right corner is curvy
  1316. * @param boolean Whether the bottom left corner is curvy
  1317. */
  1318. RGraph.filledCurvyRect = function (context, x, y, w, h)
  1319. {
  1320. // The corner radius
  1321. var r = arguments[5] ? arguments[5] : 3;
  1322. // The corners
  1323. var corner_tl = (arguments[6] || arguments[6] == null) ? true : false;
  1324. var corner_tr = (arguments[7] || arguments[7] == null) ? true : false;
  1325. var corner_br = (arguments[8] || arguments[8] == null) ? true : false;
  1326. var corner_bl = (arguments[9] || arguments[9] == null) ? true : false;
  1327. context.beginPath();
  1328. // First draw the corners
  1329. // Top left corner
  1330. if (corner_tl) {
  1331. context.moveTo(x + r, y + r);
  1332. context.arc(x + r, y + r, r, PI, PI + HALFPI, false);
  1333. } else {
  1334. context.fillRect(x, y, r, r);
  1335. }
  1336. // Top right corner
  1337. if (corner_tr) {
  1338. context.moveTo(x + w - r, y + r);
  1339. context.arc(x + w - r, y + r, r, PI + HALFPI, 0, false);
  1340. } else {
  1341. context.moveTo(x + w - r, y);
  1342. context.fillRect(x + w - r, y, r, r);
  1343. }
  1344. // Bottom right corner
  1345. if (corner_br) {
  1346. context.moveTo(x + w - r, y + h - r);
  1347. context.arc(x + w - r, y - r + h, r, 0, HALFPI, false);
  1348. } else {
  1349. context.moveTo(x + w - r, y + h - r);
  1350. context.fillRect(x + w - r, y + h - r, r, r);
  1351. }
  1352. // Bottom left corner
  1353. if (corner_bl) {
  1354. context.moveTo(x + r, y + h - r);
  1355. context.arc(x + r, y - r + h, r, HALFPI, PI, false);
  1356. } else {
  1357. context.moveTo(x, y + h - r);
  1358. context.fillRect(x, y + h - r, r, r);
  1359. }
  1360. // Now fill it in
  1361. context.fillRect(x + r, y, w - r - r, h);
  1362. context.fillRect(x, y + r, r + 1, h - r - r);
  1363. context.fillRect(x + w - r - 1, y + r, r + 1, h - r - r);
  1364. context.fill();
  1365. }
  1366. /**
  1367. * Hides the zoomed canvas
  1368. */
  1369. RGraph.HideZoomedCanvas = function ()
  1370. {
  1371. var interval = 15;
  1372. var frames = 10;
  1373. if (typeof(__zoomedimage__) == 'object') {
  1374. obj = __zoomedimage__.obj;
  1375. } else {
  1376. return;
  1377. }
  1378. if (obj.Get('chart.zoom.fade.out')) {
  1379. for (var i=frames,j=1; i>=0; --i, ++j) {
  1380. if (typeof(__zoomedimage__) == 'object') {
  1381. setTimeout("__zoomedimage__.style.opacity = " + String(i / 10), j * interval);
  1382. }
  1383. }
  1384. if (typeof(__zoomedbackground__) == 'object') {
  1385. setTimeout("__zoomedbackground__.style.opacity = " + String(i / frames), j * interval);
  1386. }
  1387. }
  1388. if (typeof(__zoomedimage__) == 'object') {
  1389. setTimeout("__zoomedimage__.style.display = 'none'", obj.Get('chart.zoom.fade.out') ? (frames * interval) + 10 : 0);
  1390. }
  1391. if (typeof(__zoomedbackground__) == 'object') {
  1392. setTimeout("__zoomedbackground__.style.display = 'none'", obj.Get('chart.zoom.fade.out') ? (frames * interval) + 10 : 0);
  1393. }
  1394. }
  1395. /**
  1396. * Adds an event handler
  1397. *
  1398. * @param object obj The graph object
  1399. * @param string event The name of the event, eg ontooltip
  1400. * @param object func The callback function
  1401. */
  1402. RGraph.AddCustomEventListener = function (obj, name, func)
  1403. {
  1404. if (typeof(RGraph.events[obj.uid]) == 'undefined') {
  1405. RGraph.events[obj.uid] = [];
  1406. }
  1407. RGraph.events[obj.uid].push([obj, name, func]);
  1408. return RGraph.events[obj.uid].length - 1;
  1409. }
  1410. /**
  1411. * Used to fire one of the RGraph custom events
  1412. *
  1413. * @param object obj The graph object that fires the event
  1414. * @param string event The name of the event to fire
  1415. */
  1416. RGraph.FireCustomEvent = function (obj, name)
  1417. {
  1418. if (obj && obj.isRGraph) {
  1419. // New style of adding custom events
  1420. if (obj[name]) {
  1421. (obj[name])(obj);
  1422. }
  1423. var uid = obj.uid;
  1424. if ( typeof(uid) == 'string'
  1425. && typeof(RGraph.events) == 'object'
  1426. && typeof(RGraph.events[uid]) == 'object'
  1427. && RGraph.events[uid].length > 0) {
  1428. for(var j=0; j<RGraph.events[uid].length; ++j) {
  1429. if (RGraph.events[uid][j] && RGraph.events[uid][j][1] == name) {
  1430. RGraph.events[uid][j][2](obj);
  1431. }
  1432. }
  1433. }
  1434. }
  1435. }
  1436. /**
  1437. * This function suggests a gutter size based on the widest left label. Given that the bottom
  1438. * labels may be longer, this may be a little out.
  1439. *
  1440. * @param object obj The graph object
  1441. * @param array data An array of graph data
  1442. * @return int A suggested gutter setting
  1443. */
  1444. RGraph.getGutterSuggest = function (obj, data)
  1445. {
  1446. /**
  1447. * Calculate the minimum value
  1448. */
  1449. var min = 0;
  1450. for (var i=0; i<data.length; ++i) {
  1451. min = Math.min(min, data[i]);
  1452. }
  1453. var min = Math.abs(min);
  1454. var str = RGraph.number_format(obj, RGraph.array_max(RGraph.getScale(Math.max(min, RGraph.array_max(data)), obj)), obj.Get('chart.units.pre'), obj.Get('chart.units.post'));
  1455. // Take into account the HBar
  1456. if (obj.type == 'hbar') {
  1457. var str = '';
  1458. var len = 0;
  1459. for (var i=0; i<obj.Get('chart.labels').length; ++i) {
  1460. str = (obj.Get('chart.labels').length > str.length ? obj.Get('chart.labels')[i] : str);
  1461. }
  1462. }
  1463. obj.context.font = obj.Get('chart.text.size') + 'pt ' + obj.Get('chart.text.font');
  1464. len = obj.context.measureText(str).width + 5;
  1465. return (obj.type == 'hbar' ? len / 3 : len);
  1466. }
  1467. /**
  1468. * If you prefer, you can use the SetConfig() method to set the configuration information
  1469. * for your chart. You may find that setting the configuration this way eases reuse.
  1470. *
  1471. * @param object obj The graph object
  1472. * @param object config The graph configuration information
  1473. */
  1474. RGraph.SetConfig = function (obj, c)
  1475. {
  1476. for (i in c) {
  1477. if (typeof(i) == 'string') {
  1478. obj.Set(i, c[i]);
  1479. }
  1480. }
  1481. return obj;
  1482. }
  1483. /**
  1484. * Clears all the custom event listeners that have been registered
  1485. *
  1486. * @param string Limits the clearing to this object ID
  1487. */
  1488. RGraph.RemoveAllCustomEventListeners = function ()
  1489. {
  1490. var id = arguments[0];
  1491. if (id && RGraph.events[id]) {
  1492. RGraph.events[id] = [];
  1493. } else {
  1494. RGraph.events = [];
  1495. }
  1496. }
  1497. /**
  1498. * Clears a particular custom event listener
  1499. *
  1500. * @param object obj The graph object
  1501. * @param number i This is the index that is return by .AddCustomEventListener()
  1502. */
  1503. RGraph.RemoveCustomEventListener = function (obj, i)
  1504. {
  1505. if ( typeof(RGraph.events) == 'object'
  1506. && typeof(RGraph.events[obj.id]) == 'object'
  1507. && typeof(RGraph.events[obj.id][i]) == 'object') {
  1508. RGraph.events[obj.id][i] = null;
  1509. }
  1510. }
  1511. /**
  1512. * This draws the background
  1513. *
  1514. * @param object obj The graph object
  1515. */
  1516. RGraph.DrawBackgroundImage = function (obj)
  1517. {
  1518. if (typeof(obj.Get('chart.background.image')) == 'string') {
  1519. if (typeof(obj.canvas.__rgraph_background_image__) == 'undefined') {
  1520. var img = new Image();
  1521. img.__object__ = obj;
  1522. img.__canvas__ = obj.canvas;
  1523. img.__context__ = obj.context;
  1524. img.src = obj.Get('chart.background.image');
  1525. obj.canvas.__rgraph_background_image__ = img;
  1526. } else {
  1527. img = obj.canvas.__rgraph_background_image__;
  1528. }
  1529. // When the image has loaded - redraw the canvas
  1530. img.onload = function ()
  1531. {
  1532. RGraph.Clear(obj.canvas);
  1533. RGraph.RedrawCanvas(obj.canvas);
  1534. }
  1535. var gutterLeft = obj.Get('chart.gutter.left');
  1536. var gutterRight = obj.Get('chart.gutter.right');
  1537. var gutterTop = obj.Get('chart.gutter.top');
  1538. var gutterBottom = obj.Get('chart.gutter.bottom');
  1539. var stretch = obj.Get('chart.background.image.stretch');
  1540. var align = obj.Get('chart.background.image.align');
  1541. // Handle chart.background.image.align
  1542. if (typeof(align) == 'string') {
  1543. if (align.indexOf('right') != -1) {
  1544. var x = obj.canvas.width - img.width - gutterRight;
  1545. } else {
  1546. var x = gutterLeft;
  1547. }
  1548. if (align.indexOf('bottom') != -1) {
  1549. var y = obj.canvas.height - img.height - gutterBottom;
  1550. } else {
  1551. var y = gutterTop;
  1552. }
  1553. } else {
  1554. var x = gutterLeft;
  1555. var y = gutterTop;
  1556. }
  1557. // X/Y coords take precedence over the align
  1558. var x = typeof(obj.Get('chart.background.image.x')) == 'number' ? obj.Get('chart.background.image.x') : x;
  1559. var y = typeof(obj.Get('chart.background.image.y')) == 'number' ? obj.Get('chart.background.image.y') : y;
  1560. var w = stretch ? obj.canvas.width - gutterLeft - gutterRight : img.width;
  1561. var h = stretch ? obj.canvas.height - gutterTop - gutterBottom : img.height;
  1562. /**
  1563. * You can now specify the width and height of the image
  1564. */
  1565. if (typeof(obj.Get('chart.background.image.w')) == 'number') w = obj.Get('chart.background.image.w');
  1566. if (typeof(obj.Get('chart.background.image.h')) == 'number') h = obj.Get('chart.background.image.h');
  1567. obj.context.drawImage(img,x,y,w, h);
  1568. }
  1569. }
  1570. /**
  1571. * This function determines wshether an object has tooltips or not
  1572. *
  1573. * @param object obj The chart object
  1574. */
  1575. RGraph.hasTooltips = function (obj)
  1576. {
  1577. if (typeof(obj.Get('chart.tooltips')) == 'object' && obj.Get('chart.tooltips')) {
  1578. for (var i=0; i<obj.Get('chart.tooltips').length; ++i) {
  1579. if (!RGraph.is_null(obj.Get('chart.tooltips')[i])) {
  1580. return true;
  1581. }
  1582. }
  1583. } else if (typeof(obj.Get('chart.tooltips')) == 'function') {
  1584. return true;
  1585. }
  1586. return false;
  1587. }
  1588. /**
  1589. * This function creates a (G)UID which can be used to identify objects.
  1590. *
  1591. * @return string (g)uid The (G)UID
  1592. */
  1593. RGraph.CreateUID = function ()
  1594. {
  1595. return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c)
  1596. {
  1597. var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
  1598. return v.toString(16);
  1599. });
  1600. }
  1601. /**
  1602. * This is the new object registry, used to facilitate multiple objects per canvas.
  1603. *
  1604. * @param object obj The object to register
  1605. */
  1606. RGraph.ObjectRegistry.Add = function (obj)
  1607. {
  1608. var uid = obj.uid;
  1609. var canvasID = obj.canvas.id;
  1610. /**
  1611. * Index the objects by UID
  1612. */
  1613. RGraph.ObjectRegistry.objects.byUID.push([uid, obj]);
  1614. /**
  1615. * Index the objects by the canvas that they're drawn on
  1616. */
  1617. RGraph.ObjectRegistry.objects.byCanvasID.push([canvasID, obj]);
  1618. }
  1619. /**
  1620. * Remove an object from the object registry
  1621. *
  1622. * @param object obj The object to remove.
  1623. */
  1624. RGraph.ObjectRegistry.Remove = function (obj)
  1625. {
  1626. var id = obj.id;
  1627. var uid = obj.uid;
  1628. for (var i=0; i<RGraph.ObjectRegistry.objects.byUID.length; ++i) {
  1629. if (RGraph.ObjectRegistry.objects.byUID[i] && RGraph.ObjectRegistry.objects.byUID[i][1].uid == uid) {
  1630. RGraph.ObjectRegistry.objects.byUID[i] = null;
  1631. }
  1632. }
  1633. // RGraph.ObjectRegistry.objects.byCanvasID.push([canvasID, obj]);
  1634. for (var i=0; i<RGraph.ObjectRegistry.objects.byCanvasID.length; ++i) {
  1635. if (RGraph.ObjectRegistry.objects.byCanvasID[i] && RGraph.ObjectRegistry.objects.byCanvasID[i][0] == id) {
  1636. RGraph.ObjectRegistry.objects.byCanvasID[i] = null;
  1637. }
  1638. }
  1639. }
  1640. /**
  1641. * Removes all objects from the ObjectRegistry. If either the ID of a canvas is supplied,
  1642. * or the canvas itself, then only objects pertaining to that canvas are cleared.
  1643. *
  1644. * @param mixed Either a canvas object (as returned by document.getElementById()
  1645. * or the ID of a canvas (ie a string)
  1646. */
  1647. RGraph.ObjectRegistry.Clear = function ()
  1648. {
  1649. // If an ID is supplied restrict the learing to that
  1650. if (arguments[0]) {
  1651. var id = (typeof(arguments[0]) == 'object' ? arguments[0].id : arguments[0]);
  1652. var objects = RGraph.ObjectRegistry.getObjectsByCanvasID(id);
  1653. for (var i=0; i<objects.length; ++i) {
  1654. RGraph.ObjectRegistry.Remove(objects[i]);
  1655. }
  1656. } else {
  1657. RGraph.ObjectRegistry.objects = {};
  1658. RGraph.ObjectRegistry.objects.byUID = [];
  1659. RGraph.ObjectRegistry.objects.byCanvasID = [];
  1660. }
  1661. }
  1662. /**
  1663. * Retrieves all objects for a given canvas id
  1664. *
  1665. * @patarm id string The canvas ID to get objects for.
  1666. */
  1667. RGraph.ObjectRegistry.getObjectsByCanvasID = function (id)
  1668. {
  1669. var store = RGraph.ObjectRegistry.objects.byCanvasID;
  1670. var ret = [];
  1671. // Loop through all of the objects and return the appropriate ones
  1672. for (var i=0; i<store.length; ++i) {
  1673. if (store[i] && store[i][0] == id ) {
  1674. ret.push(store[i][1]);
  1675. }
  1676. }
  1677. return ret;
  1678. }
  1679. /**
  1680. * Retrieves the relevant object based on the X/Y position.
  1681. *
  1682. * @param object e The event object
  1683. * @return object The applicable (if any) object
  1684. */
  1685. RGraph.ObjectRegistry.getFirstObjectByXY =
  1686. RGraph.ObjectRegistry.getObjectByXY = function (e)
  1687. {
  1688. var canvas = e.target;
  1689. var ret = null;
  1690. var objects = RGraph.ObjectRegistry.getObjectsByCanvasID(canvas.id);
  1691. for (var i=(objects.length - 1); i>=0; --i) {
  1692. var obj = objects[i].getObjectByXY(e);
  1693. if (obj) {
  1694. return obj;
  1695. }
  1696. }
  1697. }
  1698. /**
  1699. * Retrieves the relevant objects based on the X/Y position.
  1700. * NOTE This function returns an array of objects
  1701. *
  1702. * @param object e The event object
  1703. * @return An array of pertinent objects. Note the there may be only one object
  1704. */
  1705. RGraph.ObjectRegistry.getObjectsByXY = function (e)
  1706. {
  1707. var canvas = e.target;
  1708. var ret = [];
  1709. var objects = RGraph.ObjectRegistry.getObjectsByCanvasID(canvas.id);
  1710. // Retrieve objects "front to back"
  1711. for (var i=(objects.length - 1); i>=0; --i) {
  1712. var obj = objects[i].getObjectByXY(e);
  1713. if (obj) {
  1714. ret.push(obj);
  1715. }
  1716. }
  1717. return ret;
  1718. }
  1719. /**
  1720. * Retrieves the object with the corresponding UID
  1721. *
  1722. * @param string uid The UID to get the relevant object for
  1723. */
  1724. RGraph.ObjectRegistry.getObjectByUID = function (uid)
  1725. {
  1726. var objects = RGraph.ObjectRegistry.objects.byUID;
  1727. for (var i=0; i<objects.length; ++i) {
  1728. if (objects[i] && objects[i][1].uid == uid) {
  1729. return objects[i][1];
  1730. }
  1731. }
  1732. }
  1733. /**
  1734. * Retrieves the objects that are the given type
  1735. *
  1736. * @param mixed canvas The canvas to check. It can either be the canvas object itself or just the ID
  1737. * @param string type The type to look for
  1738. * @return array An array of one or more objects
  1739. */
  1740. RGraph.ObjectRegistry.getObjectsByType = function (canvas, type)
  1741. {
  1742. if (typeof(canvas) == 'string') {
  1743. canvas = document.getElementById(canvas);
  1744. }
  1745. var objects = RGraph.ObjectRegistry.getObjectsByCanvasID(canvas.id);
  1746. var ret = [];
  1747. for (var i=0; i<objects.length; ++i) {
  1748. if (objects[i] && objects[i].type && objects[i].type == type) {
  1749. ret.push(objects[i]);
  1750. }
  1751. }
  1752. return ret;
  1753. }
  1754. /**
  1755. * Retrieves the FIRST object that matches the given type
  1756. *
  1757. * @param mixed canvas The type to look for
  1758. * @param string type The type of object to look for
  1759. * @return object The FIRST object that matches the given type
  1760. */
  1761. RGraph.ObjectRegistry.getFirstObjectByType = function (canvas, type)
  1762. {
  1763. var objects = RGraph.ObjectRegistry.getObjectsByType(canvas, type);
  1764. return objects.length > 0 ? objects[0] : null;
  1765. }
  1766. /**
  1767. * This takes centerx, centery, x and y coordinates and returns the
  1768. * appropriate angle relative to the canvas angle system. Remember
  1769. * that the canvas angle system starts at the EAST axis
  1770. *
  1771. * @param number cx The centerx coordinate
  1772. * @param number cy The centery coordinate
  1773. * @param number x The X coordinate (eg the mouseX if coming from a click)
  1774. * @param number y The Y coordinate (eg the mouseY if coming from a click)
  1775. * @return number The relevant angle (measured in in RADIANS)
  1776. */
  1777. RGraph.getAngleByXY = function (cx, cy, x, y)
  1778. {
  1779. var angle = Math.atan((y - cy) / (x - cx));
  1780. angle = Math.abs(angle)
  1781. if (x >= cx && y >= cy) {
  1782. angle += TWOPI;
  1783. } else if (x >= cx && y < cy) {
  1784. angle = (HALFPI - angle) + (PI + HALFPI);
  1785. } else if (x < cx && y < cy) {
  1786. angle += PI;
  1787. } else {
  1788. angle = PI - angle;
  1789. }
  1790. /**
  1791. * Upper and lower limit checking
  1792. */
  1793. if (angle > TWOPI) {
  1794. angle -= TWOPI;
  1795. }
  1796. return angle;
  1797. }
  1798. /**
  1799. * This function returns the distance between two points. In effect the
  1800. * radius of an imaginary circle that is centered on x1 and y1. The name
  1801. * of this function is derived from the word "Hypoteneuse", which in
  1802. * trigonmetry is the longest side of a triangle
  1803. *
  1804. * @param number x1 The original X coordinate
  1805. * @param number y1 The original Y coordinate
  1806. * @param number x2 The target X coordinate
  1807. * @param number y2 The target Y coordinate
  1808. */
  1809. RGraph.getHypLength = function (x1, y1, x2, y2)
  1810. {
  1811. var ret = Math.sqrt(((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)));
  1812. return ret;
  1813. }
  1814. /**
  1815. * This function gets the end point (X/Y coordinates) of a given radius.
  1816. * You pass it the center X/Y and the radius and this function will return
  1817. * the endpoint X/Y coordinates.
  1818. *
  1819. * @param number cx The center X coord
  1820. * @param number cy The center Y coord
  1821. * @param number r The lrngth of the radius
  1822. */
  1823. RGraph.getRadiusEndPoint = function (cx, cy, angle, radius)
  1824. {
  1825. var x = cx + (Math.cos(angle) * radius);
  1826. var y = cy + (Math.sin(angle) * radius);
  1827. return [x, y];
  1828. }
  1829. /**
  1830. * This installs all of the event listeners
  1831. *
  1832. * @param object obj The chart object
  1833. */
  1834. RGraph.InstallEventListeners = function (obj)
  1835. {
  1836. /**
  1837. * Don't attempt to install event listeners for older versions of MSIE
  1838. */
  1839. if (RGraph.isOld()) {
  1840. return;
  1841. }
  1842. /**
  1843. * If this function exists, then the dynamic file has been included.
  1844. */
  1845. if (RGraph.InstallCanvasClickListener) {
  1846. RGraph.InstallWindowMousedownListener(obj);
  1847. RGraph.InstallWindowMouseupListener(obj);
  1848. RGraph.InstallCanvasMousemoveListener(obj);
  1849. RGraph.InstallCanvasMouseupListener(obj);
  1850. RGraph.InstallCanvasMousedownListener(obj);
  1851. RGraph.InstallCanvasClickListener(obj);
  1852. } else if ( RGraph.hasTooltips(obj)
  1853. || obj.Get('chart.adjustable')
  1854. || obj.Get('chart.annotatable')
  1855. || obj.Get('chart.contextmenu')
  1856. || obj.Get('chart.resizable')
  1857. || obj.Get('chart.key.interactive')
  1858. || obj.Get('chart.events.click')
  1859. || obj.Get('chart.events.mousemove')
  1860. ) {
  1861. alert('[RGRAPH] You appear to have used dynamic features but not included the file: RGraph.common.dynamic.js');
  1862. }
  1863. }
  1864. /**
  1865. * Loosly mimicks the PHP function print_r();
  1866. */
  1867. RGraph.pr = function (obj)
  1868. {
  1869. var str = '';
  1870. var indent = (arguments[2] ? arguments[2] : '');
  1871. switch (typeof(obj)) {
  1872. case 'number':
  1873. if (indent == '') {
  1874. str+= 'Number: '
  1875. }
  1876. str += String(obj);
  1877. break;
  1878. case 'string':
  1879. if (indent == '') {
  1880. str+= 'String (' + obj.length + '):'
  1881. }
  1882. str += '"' + String(obj) + '"';
  1883. break;
  1884. case 'object':
  1885. // In case of null
  1886. if (obj == null) {
  1887. str += 'null';
  1888. break;
  1889. }
  1890. str += 'Object\n' + indent + '(\n';
  1891. for (var i in obj) {
  1892. if (typeof(i) == 'string' || typeof(i) == 'number') {
  1893. str += indent + ' ' + i + ' => ' + RGraph.pr(obj[i], true, indent + ' ') + '\n';
  1894. }
  1895. }
  1896. var str = str + indent + ')';
  1897. break;
  1898. case 'function':
  1899. str += obj;
  1900. break;
  1901. case 'boolean':
  1902. str += 'Boolean: ' + (obj ? 'true' : 'false');
  1903. break;
  1904. }
  1905. /**
  1906. * Finished, now either return if we're in a recursed call, or alert()
  1907. * if we're not.
  1908. */
  1909. if (arguments[1]) {
  1910. return str;
  1911. } else {
  1912. alert(str);
  1913. }
  1914. }
  1915. /**
  1916. * Produces a dashed line
  1917. *
  1918. * @param
  1919. */
  1920. RGraph.DashedLine = function(context, x1, y1, x2, y2)
  1921. {
  1922. /**
  1923. * This is the size of the dashes
  1924. */
  1925. var size = 5;
  1926. /**
  1927. * The optional fifth argument can be the size of the dashes
  1928. */
  1929. if (typeof(arguments[5]) == 'number') {
  1930. size = arguments[5];
  1931. }
  1932. var dx = x2 - x1;
  1933. var dy = y2 - y1;
  1934. var num = Math.floor(Math.sqrt((dx * dx) + (dy * dy)) / size);
  1935. var xLen = dx / num;
  1936. var yLen = dy / num;
  1937. var count = 0;
  1938. do {
  1939. (count % 2 == 0 && count > 0) ? context.lineTo(x1, y1) : context.moveTo(x1, y1);
  1940. x1 += xLen;
  1941. y1 += yLen;
  1942. } while(count++ <= num);
  1943. }
  1944. /**
  1945. * Makes an AJAX call. It calls the given callback (a function) when ready
  1946. *
  1947. * @param string url The URL to retrieve
  1948. * @param function callback A function that is called when the response is ready, there's an example below
  1949. * called "myCallback".
  1950. */
  1951. RGraph.AJAX = function (url, callback)
  1952. {
  1953. // Mozilla, Safari, ...
  1954. if (window.XMLHttpRequest) {
  1955. var httpRequest = new XMLHttpRequest();
  1956. // MSIE
  1957. } else if (window.ActiveXObject) {
  1958. var httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
  1959. }
  1960. httpRequest.onreadystatechange = function ()
  1961. {
  1962. if (this.readyState == 4 && this.status == 200) {
  1963. this.__user_callback__ = callback;
  1964. this.__user_callback__();
  1965. }
  1966. }
  1967. httpRequest.open('GET', url, true);
  1968. httpRequest.send();
  1969. }
  1970. /**
  1971. * Rotates the canvas
  1972. *
  1973. * @param object canvas The canvas to rotate
  1974. * @param int x The X coordinate about which to rotate the canvas
  1975. * @param int y The Y coordinate about which to rotate the canvas
  1976. * @param int angle The angle(in RADIANS) to rotate the canvas by
  1977. */
  1978. RGraph.RotateCanvas = function (canvas, x, y, angle)
  1979. {
  1980. var context = canvas.getContext('2d');
  1981. context.translate(x, y);
  1982. context.rotate(angle);
  1983. context.translate(0 - x, 0 - y);
  1984. }
  1985. /**
  1986. * This draws an extra set of axes
  1987. *
  1988. * @param object obj The chart object
  1989. * @param object prop An assoc array of options:
  1990. * axis.x REQUIRED
  1991. * axis.min Minimum value - default is 0
  1992. * axis.max Maximum value - REQUIRED
  1993. * axis.color Color - default is black
  1994. * axis.title Title - default is nothing
  1995. * axis.title.color The color of the title text, default is black
  1996. * axis.font The font that the text is rendered in, default is Arial
  1997. * axis.text.size The default is 12 (ish)
  1998. * axis.numticks The number of tickmarks, default is 10
  1999. * axis.numylabels The number of Y labels
  2000. * axis.align This determines which side of the axis that the labels are draswn on.
  2001. * It can be left or right
  2002. * axis.scale.formatter This should be a function which accepts two arguments - the chart
  2003. * object and the value. The function should then format the number
  2004. * as required and then return it.
  2005. */
  2006. RGraph.DrawAxes = function (obj, prop)
  2007. {
  2008. var gutterTop = obj.gutterTop;
  2009. var gutterBottom = obj.gutterBottom;
  2010. var context = obj.context;
  2011. var x = prop['axis.x'];
  2012. var y = obj.properties['chart.gutter.top'];
  2013. var min = prop['axis.min'] ? prop['axis.min'] : 0;
  2014. var max = prop['axis.max'];
  2015. var color = prop['axis.color'] ? prop['axis.color'] : 'black';
  2016. var title = prop['axis.title'] ? prop['axis.title'] : '';
  2017. var title_color = prop['axis.title.color'] ? prop['axis.title.color'] : color;
  2018. var label_color = prop['axis.text.color'] ? prop['axis.color'] : color;
  2019. var height = obj.canvas.height - obj.gutterBottom - obj.gutterTop;
  2020. var numticks = prop['axis.numticks'] ? prop['axis.numticks'] : 10;
  2021. var numlabels = prop['axis.numlabels'] ? prop['axis.numlabels'] : 5;
  2022. var gap = height / numticks;
  2023. var font = prop['axis.font'] ? prop['axis.font'] : 'Arial';
  2024. var size = prop['axis.text.size'] ? prop['axis.text.size'] : 10;
  2025. var align = typeof(prop['axis.align']) == 'string'? prop['axis.align'] : 'left';
  2026. var formatter = prop['axis.scale.formatter'];
  2027. var decimals = prop['axis.scale.decimals'];
  2028. var units_pre = prop['axis.units.pre'];
  2029. var units_post = prop['axis.units.post'];
  2030. /**
  2031. * Set the color
  2032. */
  2033. context.strokeStyle = color;
  2034. /**
  2035. * Draw the main vertical line
  2036. */
  2037. context = obj.context;
  2038. context.beginPath();
  2039. context.moveTo(AA(obj, x), y);
  2040. context.lineTo(AA(obj, x), y + height);
  2041. context.stroke();
  2042. /**
  2043. * Draw the axes tickmarks
  2044. */
  2045. context.beginPath();
  2046. for (var i=0; i<=numticks; ++i) {
  2047. context.moveTo(align == 'right' ? x + 3 : x - 3,AA(obj, y + (gap *i)));
  2048. context.lineTo(x,AA(obj, y + (gap *i)));
  2049. }
  2050. context.stroke();
  2051. /**
  2052. * Draw the scale for the axes
  2053. */
  2054. context.fillStyle = label_color;
  2055. context.beginPath();
  2056. var text_len = 0;
  2057. for (var i=0; i<=numlabels; ++i) {
  2058. var original = ((max - min) * ((numlabels-i) / numlabels)) + min;
  2059. var text = RGraph.number_format(obj, original.toFixed(decimals), units_pre, units_post);
  2060. var text = String(typeof(formatter) == 'function' ? formatter(obj, original) : text);
  2061. var text_len = Math.max(text_len, context.measureText(text).width);
  2062. RGraph.Text(
  2063. context,
  2064. font,
  2065. size,
  2066. x - (align == 'right' ? -5 : 5),
  2067. gutterTop + ((height / numlabels)*i),
  2068. text,
  2069. 'center',
  2070. align == 'right' ? 'left' : 'right'
  2071. );
  2072. }
  2073. context.stroke();
  2074. /**
  2075. * Draw the title for the axes
  2076. */
  2077. if (title) {
  2078. context.beginPath();
  2079. context.fillStyle = title_color
  2080. RGraph.Text(context, font, size + 2,
  2081. align == 'right' ? x + size + text_len + 2 : x - size - text_len - 2,
  2082. height / 2 + gutterTop, title, 'center', 'center', null, align == 'right' ? 90 : -90);
  2083. context.stroke();
  2084. }
  2085. }
  2086. /**
  2087. * Measures text by creating a DIV in the document and adding the relevant text to it.
  2088. * Then checking the .offsetWidth and .offsetHeight.
  2089. *
  2090. * @param string text The text to measure
  2091. * @param bool bold Whether the text is bold or not
  2092. * @param string font The font to use
  2093. * @param size number The size of the text (in pts)
  2094. * @return array A two element array of the width and height of the text
  2095. */
  2096. RGraph.MeasureText = function (text, bold, font, size)
  2097. {
  2098. var div = document.createElement('DIV');
  2099. div.innerHTML = text;
  2100. div.style.position = 'absolute';
  2101. div.style.top = '-100px';
  2102. div.style.left = '-100px';
  2103. div.style.color = 'rgba(0,0,0,0)';
  2104. div.style.fontFamily = font;
  2105. div.style.fontWeight = bold ? 'bold' : 'normal';
  2106. div.style.fontSize = size + 'pt';
  2107. document.body.appendChild(div);
  2108. var size = [div.offsetWidth, div.offsetHeight];
  2109. document.body.removeChild(div);
  2110. return size;
  2111. }
  2112. /**
  2113. * If the effects library isn't included already this prevents an unknown function error
  2114. */
  2115. if (!RGraph.AddEffects) {
  2116. RGraph.AddEffects = function (obj) {}
  2117. }
  2118. // Some other functions. Because they're rarely changed - they're hand minified
  2119. function AA(obj,value){var value=String(value).replace(/^(\d+)\.\d+/,'$1');var newvalue=Number(value)+0.5;return(newvalue-value)>=0?newvalue:Math.floor(value);}
  2120. RGraph.LinearGradient=function(obj,x1,y1,x2,y2,color1,color2){var gradient=obj.context.createLinearGradient(x1,y1,x2,y2);var numColors=arguments.length-5;for (var i=5;i<arguments.length;++i){var color=arguments[i];var stop=(i-5)/(numColors-1);gradient.addColorStop(stop,color);}return gradient;}
  2121. RGraph.RadialGradient=function(obj,x1,y1,r1,x2,y2,r2,color1,color2){var gradient=obj.context.createRadialGradient(x1,y1,r1,x2,y2,r2);var numColors=arguments.length-7;for(var i=7;i<arguments.length; ++i){var color=arguments[i];var stop=(i-7)/(numColors-1);gradient.addColorStop(stop,color);}return gradient;}
  2122. RGraph.array_shift=function(arr){var ret=[];for(var i=1;i<arr.length;++i){ret.push(arr[i]);}return ret;}
  2123. RGraph.AddEventListener=function(id,e,func){var type=arguments[3]?arguments[3]:'unknown';RGraph.Registry.Get('chart.event.handlers').push([id,e,func,type]);}
  2124. RGraph.ClearEventListeners=function(id){if(id&&id=='window'){window.removeEventListener('mousedown',window.__rgraph_mousedown_event_listener_installed__,false);window.removeEventListener('mouseup',window.__rgraph_mouseup_event_listener_installed__,false);}else{var canvas = document.getElementById(id);canvas.removeEventListener('mouseup',canvas.__rgraph_mouseup_event_listener_installed__,false);canvas.removeEventListener('mousemove',canvas.__rgraph_mousemove_event_listener_installed__,false);canvas.removeEventListener('mousedown',canvas.__rgraph_mousedown_event_listener_installed__,false);canvas.removeEventListener('click',canvas.__rgraph_click_event_listener_installed__,false);}}
  2125. RGraph.HidePalette=function(){var div=RGraph.Registry.Get('palette');if(typeof(div)=='object'&&div){div.style.visibility='hidden';div.style.display='none';RGraph.Registry.Set('palette',null);}}
  2126. RGraph.random=function(min,max){var dp=arguments[2]?arguments[2]:0;var r=Math.random();return Number((((max - min) * r) + min).toFixed(dp));}
  2127. RGraph.NoShadow=function(obj){obj.context.shadowColor='rgba(0,0,0,0)';obj.context.shadowBlur=0;obj.context.shadowOffsetX=0;obj.context.shadowOffsetY=0;}
  2128. RGraph.SetShadow=function(obj,color,offsetx,offsety,blur){obj.context.shadowColor=color;obj.context.shadowOffsetX=offsetx;obj.context.shadowOffsetY=offsety;obj.context.shadowBlur=blur;}
  2129. RGraph.array_reverse=function(arr){var newarr=[];for(var i=arr.length-1;i>=0;i--){newarr.push(arr[i]);}return newarr;}
  2130. RGraph.Registry.Set=function(name,value){RGraph.Registry.store[name]=value;return value;}
  2131. RGraph.Registry.Get=function(name){return RGraph.Registry.store[name];}
  2132. RGraph.degrees2Radians=function(degrees){return degrees*(PI/180);}
  2133. RGraph.is_array=function(obj){return obj!=null&&obj.constructor.toString().indexOf('Array')!=-1;}
  2134. RGraph.trim=function(str){return RGraph.ltrim(RGraph.rtrim(str));}
  2135. RGraph.ltrim=function(str){return str.replace(/^(\s|\0)+/, '');}
  2136. RGraph.rtrim=function(str){return str.replace(/(\s|\0)+$/, '');}
  2137. RGraph.GetHeight=function(obj){return obj.canvas.height;}
  2138. RGraph.GetWidth=function(obj){return obj.canvas.width;}
  2139. RGraph.is_null=function(arg){if(arg==null||(typeof(arg))=='object'&&!arg){return true;}return false;}
  2140. RGraph.Timer=function(label){var d=new Date();console.log(label+': '+d.getSeconds()+'.'+d.getMilliseconds());}
  2141. RGraph.Async=function(func){return setTimeout(func,arguments[1]?arguments[1]:1);}
  2142. RGraph.isIE7=function(){return navigator.userAgent.indexOf('MSIE 7')>0;}
  2143. RGraph.isIE8=function(){return navigator.userAgent.indexOf('MSIE 8')>0;}
  2144. RGraph.isIE9=function(){return navigator.userAgent.indexOf('MSIE 9')>0;}
  2145. RGraph.isIE9up=function(){navigator.userAgent.match(/MSIE (\d+)/);return Number(RegExp.$1)>=9;}
  2146. RGraph.isOld=function(){return RGraph.isIE7()||RGraph.isIE8();}
  2147. RGraph.Reset=function(canvas){canvas.width=canvas.width;}
  2148. function pd(variable){RGraph.pr(variable);}
  2149. function p(variable){RGraph.pr(variable);}
  2150. function a(variable){alert(variable);}
  2151. function cl(variable){return console.log(variable);}