RGraph.funnel.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808
  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 |
  12. * o------------------------------------------------------------------------------o
  13. */
  14. if (typeof(RGraph) == 'undefined') RGraph = {};
  15. /**
  16. * The bar chart constructor
  17. *
  18. * @param object canvas The canvas object
  19. * @param array data The chart data
  20. */
  21. RGraph.Funnel = function (id, data)
  22. {
  23. // Get the canvas and context objects
  24. this.id = id;
  25. this.canvas = document.getElementById(typeof id === 'object' ? id.id : id);
  26. this.context = this.canvas.getContext ? this.canvas.getContext("2d") : null;
  27. this.canvas.__object__ = this;
  28. this.type = 'funnel';
  29. this.coords = [];
  30. this.isRGraph = true;
  31. this.uid = RGraph.CreateUID();
  32. this.canvas.uid = this.canvas.uid ? this.canvas.uid : RGraph.CreateUID();
  33. this.coordsText = [];
  34. /**
  35. * Compatibility with older browsers
  36. */
  37. RGraph.OldBrowserCompat(this.context);
  38. // Check for support
  39. if (!this.canvas) {
  40. alert('[FUNNEL] No canvas support');
  41. return;
  42. }
  43. /**
  44. * The funnel charts properties
  45. */
  46. this.properties = {
  47. 'chart.strokestyle': 'rgba(0,0,0,0)',
  48. 'chart.gutter.left': 25,
  49. 'chart.gutter.right': 25,
  50. 'chart.gutter.top': 25,
  51. 'chart.gutter.bottom': 25,
  52. 'chart.labels': null,
  53. 'chart.labels.sticks': false,
  54. 'chart.labels.x': null,
  55. 'chart.title': '',
  56. 'chart.title.background': null,
  57. 'chart.title.hpos': null,
  58. 'chart.title.vpos': null,
  59. 'chart.title.bold': true,
  60. 'chart.title.font': null,
  61. 'chart.title.x': null,
  62. 'chart.title.y': null,
  63. 'chart.title.halign': null,
  64. 'chart.title.valign': null,
  65. 'chart.colors': ['Gradient(white:red)','Gradient(white:green)','Gradient(white:gray)','Gradient(white:blue)','Gradient(white:black)','Gradient(white:gray)','Gradient(white:pink)','Gradient(white:blue)','Gradient(white:yellow)','Gradient(white:green)','Gradient(white:red)'],
  66. 'chart.text.size': 10,
  67. 'chart.text.boxed': true,
  68. 'chart.text.halign': 'left',
  69. 'chart.text.color': 'black',
  70. 'chart.text.font': 'Arial',
  71. 'chart.contextmenu': null,
  72. 'chart.shadow': false,
  73. 'chart.shadow.color': '#666',
  74. 'chart.shadow.blur': 3,
  75. 'chart.shadow.offsetx': 3,
  76. 'chart.shadow.offsety': 3,
  77. 'chart.key': null,
  78. 'chart.key.background': 'white',
  79. 'chart.key.position': 'graph',
  80. 'chart.key.halign': 'right',
  81. 'chart.key.shadow': false,
  82. 'chart.key.shadow.color': '#666',
  83. 'chart.key.shadow.blur': 3,
  84. 'chart.key.shadow.offsetx': 2,
  85. 'chart.key.shadow.offsety': 2,
  86. 'chart.key.position.gutter.boxed': false,
  87. 'chart.key.position.x': null,
  88. 'chart.key.position.y': null,
  89. 'chart.key.color.shape': 'square',
  90. 'chart.key.rounded': true,
  91. 'chart.key.linewidth': 1,
  92. 'chart.key.colors': null,
  93. 'chart.key.interactive': false,
  94. 'chart.key.interactive.highlight.chart.stroke': 'black',
  95. 'chart.key.interactive.highlight.chart.fill': 'rgba(255,255,255,0.7)',
  96. 'chart.key.interactive.highlight.label': 'rgba(255,0,0,0.2)',
  97. 'chart.key.text.color': 'black',
  98. 'chart.tooltips': null,
  99. 'chart.tooltips.effect': 'fade',
  100. 'chart.tooltips.css.class': 'RGraph_tooltip',
  101. 'chart.tooltips.event': 'onclick',
  102. 'chart.highlight.stroke': 'rgba(0,0,0,0)',
  103. 'chart.highlight.fill': 'rgba(255,255,255,0.7)',
  104. 'chart.tooltips.highlight': true,
  105. 'chart.annotatable': false,
  106. 'chart.annotate.color': 'black',
  107. 'chart.zoom.factor': 1.5,
  108. 'chart.zoom.fade.in': true,
  109. 'chart.zoom.fade.out': true,
  110. 'chart.zoom.factor': 1.5,
  111. 'chart.zoom.fade.in': true,
  112. 'chart.zoom.fade.out': true,
  113. 'chart.zoom.hdir': 'right',
  114. 'chart.zoom.vdir': 'down',
  115. 'chart.zoom.frames': 25,
  116. 'chart.zoom.delay': 16.666,
  117. 'chart.zoom.shadow': true,
  118. 'chart.zoom.background': true,
  119. 'chart.zoom.action': 'zoom',
  120. 'chart.resizable': false,
  121. 'chart.events.click': null,
  122. 'chart.events.mousemove': null
  123. }
  124. // Store the data
  125. this.data = data;
  126. /**
  127. * Create the dollar objects so that functions can be added to them
  128. */
  129. for (var i=0; i<data.length; ++i) {
  130. this['$' + i] = {};
  131. }
  132. /**
  133. * Translate half a pixel for antialiasing purposes - but only if it hasn't beeen
  134. * done already
  135. */
  136. if (!this.canvas.__rgraph_aa_translated__) {
  137. this.context.translate(0.5,0.5);
  138. this.canvas.__rgraph_aa_translated__ = true;
  139. }
  140. ///////////////////////////////// SHORT PROPERTIES /////////////////////////////////
  141. var RG = RGraph;
  142. var ca = this.canvas;
  143. var co = ca.getContext('2d');
  144. var prop = this.properties;
  145. //////////////////////////////////// METHODS ///////////////////////////////////////
  146. /**
  147. * A setter
  148. *
  149. * @param name string The name of the property to set
  150. * @param value mixed The value of the property
  151. */
  152. this.Set = function (name, value)
  153. {
  154. name = name.toLowerCase();
  155. /**
  156. * This should be done first - prepend the propertyy name with "chart." if necessary
  157. */
  158. if (name.substr(0,6) != 'chart.') {
  159. name = 'chart.' + name;
  160. }
  161. prop[name] = value;
  162. return this;
  163. }
  164. /**
  165. * A getter
  166. *
  167. * @param name string The name of the property to get
  168. */
  169. this.Get = function (name)
  170. {
  171. /**
  172. * This should be done first - prepend the property name with "chart." if necessary
  173. */
  174. if (name.substr(0,6) != 'chart.') {
  175. name = 'chart.' + name;
  176. }
  177. return prop[name.toLowerCase()];
  178. }
  179. /**
  180. * The function you call to draw the bar chart
  181. */
  182. this.Draw = function ()
  183. {
  184. /**
  185. * Fire the onbeforedraw event
  186. */
  187. RG.FireCustomEvent(this, 'onbeforedraw');
  188. /**
  189. * Parse the colors. This allows for simple gradient syntax
  190. */
  191. if (!this.colorsParsed) {
  192. this.parseColors();
  193. // Don't want to do this again
  194. this.colorsParsed = true;
  195. }
  196. /**
  197. * This is new in May 2011 and facilitates indiviual gutter settings,
  198. * eg chart.gutter.left
  199. */
  200. this.gutterLeft = prop['chart.gutter.left'];
  201. this.gutterRight = prop['chart.gutter.right'];
  202. this.gutterTop = prop['chart.gutter.top'];
  203. this.gutterBottom = prop['chart.gutter.bottom'];
  204. // This stops the coords array from growing
  205. this.coords = [];
  206. RG.DrawTitle(this, prop['chart.title'], this.gutterTop, null, prop['chart.title.size'] ? prop['chart.title.size'] : prop['chart.text.size'] + 2);
  207. this.DrawFunnel();
  208. /**
  209. * Setup the context menu if required
  210. */
  211. if (prop['chart.contextmenu']) {
  212. RG.ShowContext(this);
  213. }
  214. /**
  215. * Draw the labels on the chart
  216. */
  217. this.DrawLabels();
  218. /**
  219. * This function enables resizing
  220. */
  221. if (prop['chart.resizable']) {
  222. RG.AllowResizing(this);
  223. }
  224. /**
  225. * This installs the event listeners
  226. */
  227. RG.InstallEventListeners(this);
  228. /**
  229. * Fire the RGraph ondraw event
  230. */
  231. RG.FireCustomEvent(this, 'ondraw');
  232. return this;
  233. }
  234. /**
  235. * This function actually draws the chart
  236. */
  237. this.DrawFunnel = function ()
  238. {
  239. var width = ca.width - this.gutterLeft - this.gutterRight;
  240. var height = ca.height - this.gutterTop - this.gutterBottom;
  241. var total = RG.array_max(this.data);
  242. var accheight = this.gutterTop;
  243. /**
  244. * Loop through each segment to draw
  245. */
  246. // Set a shadow if it's been requested
  247. if (prop['chart.shadow']) {
  248. co.shadowColor = prop['chart.shadow.color'];
  249. co.shadowBlur = prop['chart.shadow.blur'];
  250. co.shadowOffsetX = prop['chart.shadow.offsetx'];
  251. co.shadowOffsetY = prop['chart.shadow.offsety'];
  252. }
  253. for (i=0,len=this.data.length; i<len; ++i) {
  254. var firstvalue = this.data[0];
  255. var firstwidth = (firstvalue / total) * width;
  256. var curvalue = this.data[i];
  257. var curwidth = (curvalue / total) * width;
  258. var curheight = height / this.data.length;
  259. var halfCurWidth = (curwidth / 2);
  260. var nextvalue = this.data[i + 1];
  261. var nextwidth = this.data[i + 1] ? (nextvalue / total) * width : null;
  262. var halfNextWidth = (nextwidth / 2);
  263. var center = this.gutterLeft + (firstwidth / 2);
  264. var x1 = center - halfCurWidth;
  265. var y1 = accheight;
  266. var x2 = center + halfCurWidth;
  267. var y2 = accheight;
  268. var x3 = center + halfNextWidth;
  269. var y3 = accheight + curheight;
  270. var x4 = center - halfNextWidth;
  271. var y4 = accheight + curheight;
  272. if (nextwidth && i < this.data.length - 1) {
  273. co.beginPath();
  274. co.strokeStyle = prop['chart.strokestyle'];
  275. co.fillStyle = prop['chart.colors'][i];
  276. co.moveTo(x1, y1);
  277. co.lineTo(x2, y2);
  278. co.lineTo(x3, y3);
  279. co.lineTo(x4, y4);
  280. co.closePath();
  281. /**
  282. * Store the coordinates
  283. */
  284. this.coords.push([x1, y1, x2, y2, x3, y3, x4, y4]);
  285. }
  286. // The redrawing if the shadow is on will do the stroke
  287. if (!prop['chart.shadow']) {
  288. co.stroke();
  289. }
  290. co.fill();
  291. accheight += curheight;
  292. }
  293. /**
  294. * If the shadow is enabled, redraw every segment, in order to allow for shadows going upwards
  295. */
  296. if (prop['chart.shadow']) {
  297. RG.NoShadow(this);
  298. for (i=0; i<this.coords.length; ++i) {
  299. co.strokeStyle = prop['chart.strokestyle'];
  300. co.fillStyle = prop['chart.colors'][i];
  301. co.beginPath();
  302. co.moveTo(this.coords[i][0], this.coords[i][1]);
  303. co.lineTo(this.coords[i][2], this.coords[i][3]);
  304. co.lineTo(this.coords[i][4], this.coords[i][5]);
  305. co.lineTo(this.coords[i][6], this.coords[i][7]);
  306. co.closePath();
  307. co.stroke();
  308. co.fill();
  309. }
  310. }
  311. /**
  312. * Lastly, draw the key if necessary
  313. */
  314. if (prop['chart.key'] && prop['chart.key'].length) {
  315. RG.DrawKey(this, prop['chart.key'], prop['chart.colors']);
  316. }
  317. }
  318. /**
  319. * Draws the labels
  320. */
  321. this.DrawLabels = function ()
  322. {
  323. /**
  324. * Draws the labels
  325. */
  326. if (prop['chart.labels'] && prop['chart.labels'].length > 0) {
  327. var font = prop['chart.text.font'];
  328. var size = prop['chart.text.size'];
  329. var color = prop['chart.text.color'];
  330. var labels = prop['chart.labels'];
  331. var halign = prop['chart.text.halign'] == 'left' ? 'left' : 'center';
  332. var bgcolor = prop['chart.text.boxed'] ? 'white' : null;
  333. if (typeof prop['chart.labels.x'] == 'number') {
  334. var x = prop['chart.labels.x'];
  335. } else {
  336. var x = halign == 'left' ? (this.gutterLeft - 15) : ((ca.width - this.gutterLeft - this.gutterRight) / 2) + this.gutterLeft;
  337. }
  338. for (var j=0; j<this.coords.length; ++j) { // MUST be "j"
  339. co.beginPath();
  340. // Set the color back to black
  341. co.strokeStyle = 'black';
  342. co.fillStyle = color;
  343. // Turn off any shadow
  344. RG.NoShadow(this);
  345. var label = labels[j];
  346. RG.Text2(this,{'font':font,
  347. 'size':size,
  348. 'x':x,
  349. 'y':this.coords[j][1],
  350. 'text':label,
  351. 'valign':'center',
  352. 'halign':halign,
  353. 'bounding': prop['chart.text.boxed'],
  354. 'boundingFill':bgcolor,
  355. 'tag': 'labels'
  356. });
  357. if (prop['chart.labels.sticks']) {
  358. /**
  359. * Measure the text
  360. */
  361. co.font = size + 'pt ' + font;
  362. var labelWidth = co.measureText(label).width;
  363. /**
  364. * Draw the horizontal indicator line
  365. */
  366. co.beginPath();
  367. co.strokeStyle = 'gray';
  368. co.moveTo(x + labelWidth + 10, Math.round(this.coords[j][1]));
  369. co.lineTo(this.coords[j][0] - 10, Math.round(this.coords[j][1]));
  370. co.stroke();
  371. }
  372. }
  373. /**
  374. * This draws the last labels if defined
  375. */
  376. var lastLabel = labels[j];
  377. if (lastLabel) {
  378. RG.Text2(this,{'font':font,
  379. 'size':size,
  380. 'x':x,
  381. 'y':this.coords[j - 1][5],
  382. 'text':lastLabel,
  383. 'valign':'center',
  384. 'halign':halign,
  385. 'bounding': prop['chart.text.boxed'],
  386. 'boundingFill':bgcolor,
  387. 'tag': 'labels'
  388. });
  389. if (prop['chart.labels.sticks']) {
  390. /**
  391. * Measure the text
  392. */
  393. co.font = size + 'pt ' + font;
  394. var labelWidth = co.measureText(lastLabel).width;
  395. /**
  396. * Draw the horizontal indicator line
  397. */
  398. co.beginPath();
  399. co.strokeStyle = 'gray';
  400. co.moveTo(x + labelWidth + 10, Math.round(this.coords[j - 1][7]));
  401. co.lineTo(this.coords[j - 1][0] - 10, Math.round(this.coords[j - 1][7]));
  402. co.stroke();
  403. }
  404. }
  405. }
  406. }
  407. /**
  408. * Gets the appropriate segment that has been highlighted
  409. */
  410. this.getShape =
  411. this.getSegment = function (e)
  412. {
  413. //var canvas = ca = e.target;
  414. //var co = this.context;
  415. //var prop = this.properties;
  416. var coords = this.coords;
  417. var mouseCoords = RG.getMouseXY(e);
  418. var x = mouseCoords[0];
  419. var y = mouseCoords[1];
  420. for (i=0,len=coords.length; i<len; ++i) {
  421. var segment = coords[i]
  422. // Path testing
  423. co.beginPath();
  424. co.moveTo(segment[0], segment[1]);
  425. co.lineTo(segment[2], segment[3]);
  426. co.lineTo(segment[4], segment[5]);
  427. co.lineTo(segment[6], segment[7]);
  428. co.lineTo(segment[8], segment[9]);
  429. if (co.isPointInPath(x, y)) {
  430. var tooltip = RGraph.parseTooltipText(prop['chart.tooltips'], i);
  431. return {0: this, 1: coords, 2: i, 'object': this, 'coords': segment, 'index': i, 'tooltip': tooltip};
  432. }
  433. }
  434. return null;
  435. }
  436. /**
  437. * Each object type has its own Highlight() function which highlights the appropriate shape
  438. *
  439. * @param object shape The shape to highlight
  440. */
  441. this.Highlight = function (shape)
  442. {
  443. if (prop['chart.tooltips.highlight']) {
  444. // Add the new highlight
  445. var coords = shape['coords'];
  446. co.beginPath();
  447. co.strokeStyle = prop['chart.highlight.stroke'];
  448. co.fillStyle = prop['chart.highlight.fill'];
  449. co.moveTo(coords[0], coords[1]);
  450. co.lineTo(coords[2], coords[3]);
  451. co.lineTo(coords[4], coords[5]);
  452. co.lineTo(coords[6], coords[7]);
  453. co.closePath();
  454. co.stroke();
  455. co.fill();
  456. }
  457. }
  458. /**
  459. * The getObjectByXY() worker method. Don't call this call:
  460. *
  461. * RGraph.ObjectRegistry.getObjectByXY(e)
  462. *
  463. * @param object e The event object
  464. */
  465. this.getObjectByXY = function (e)
  466. {
  467. var mouseXY = RGraph.getMouseXY(e);
  468. if (
  469. mouseXY[0] > prop['chart.gutter.left']
  470. && mouseXY[0] < (ca.width - prop['chart.gutter.right'])
  471. && mouseXY[1] > prop['chart.gutter.top']
  472. && mouseXY[1] < (ca.height - prop['chart.gutter.bottom'])
  473. ) {
  474. return this;
  475. }
  476. }
  477. /**
  478. * This function positions a tooltip when it is displayed
  479. *
  480. * @param obj object The chart object
  481. * @param int x The X coordinate specified for the tooltip
  482. * @param int y The Y coordinate specified for the tooltip
  483. * @param objec tooltip The tooltips DIV element
  484. */
  485. this.positionTooltip = function (obj, x, y, tooltip, idx)
  486. {
  487. var coords = obj.coords[tooltip.__index__];
  488. var x1 = coords[0];
  489. var y1 = coords[1];
  490. var x2 = coords[2];
  491. var y2 = coords[3];
  492. var x3 = coords[4];
  493. var y3 = coords[5];
  494. var x4 = coords[6];
  495. var y4 = coords[7];
  496. var coordW = x2 - x1;
  497. var coordX = x1 + (coordW / 2);
  498. var canvasXY = RG.getCanvasXY(ca);
  499. var gutterLeft = prop['chart.gutter.left'];
  500. var gutterTop = prop['chart.gutter.top'];
  501. var width = tooltip.offsetWidth;
  502. var height = tooltip.offsetHeight;
  503. // Set the top position
  504. tooltip.style.left = 0;
  505. tooltip.style.top = canvasXY[1] + y1 + ((y3 - y2) / 2) - height - 7 + 'px';
  506. // By default any overflow is hidden
  507. tooltip.style.overflow = '';
  508. // The arrow
  509. var img = new Image();
  510. img.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAFCAYAAACjKgd3AAAARUlEQVQYV2NkQAN79+797+RkhC4M5+/bd47B2dmZEVkBCgcmgcsgbAaA9GA1BCSBbhAuA/AagmwQPgMIGgIzCD0M0AMMAEFVIAa6UQgcAAAAAElFTkSuQmCC';
  511. img.style.position = 'absolute';
  512. img.id = '__rgraph_tooltip_pointer__';
  513. img.style.top = (tooltip.offsetHeight - 2) + 'px';
  514. tooltip.appendChild(img);
  515. // Reposition the tooltip if at the edges:
  516. // LEFT edge
  517. if ((canvasXY[0] + coordX - (width / 2)) < 5) {
  518. tooltip.style.left = (canvasXY[0] + coordX - (width * 0.1)) + 'px';
  519. img.style.left = ((width * 0.1) - 8.5) + 'px';
  520. // RIGHT edge
  521. } else if ((canvasXY[0] + coordX + (width / 2)) > document.body.offsetWidth) {
  522. tooltip.style.left = canvasXY[0] + coordX - (width * 0.9) + 'px';
  523. img.style.left = ((width * 0.9) - 8.5) + 'px';
  524. // Default positioning - CENTERED
  525. } else {
  526. tooltip.style.left = (canvasXY[0] + coordX - (width / 2)) + 'px';
  527. img.style.left = ((width * 0.5) - 8.5) + 'px';
  528. }
  529. }
  530. /**
  531. * This allows for easy specification of gradients
  532. */
  533. this.parseColors = function ()
  534. {
  535. var colors = prop['chart.colors'];
  536. for (var i=0; i<colors.length; ++i) {
  537. colors[i] = this.parseSingleColorForHorizontalGradient(colors[i]);
  538. }
  539. var keyColors = prop['chart.key.colors'];
  540. if (keyColors) {
  541. for (var i=0; i<keyColors.length; ++i) {
  542. keyColors[i] = this.parseSingleColorForHorizontalGradient(keyColors[i]);
  543. }
  544. }
  545. prop['chart.strokestyle'] = this.parseSingleColorForVerticalGradient(prop['chart.strokestyle']);
  546. prop['chart.highlight.stroke'] = this.parseSingleColorForHorizontalGradient(prop['chart.highlight.stroke']);
  547. prop['chart.highlight.fill'] = this.parseSingleColorForHorizontalGradient(prop['chart.highlight.fill']);
  548. }
  549. /**
  550. * This parses a single color value
  551. */
  552. this.parseSingleColorForHorizontalGradient = function (color)
  553. {
  554. if (!color || typeof(color) != 'string') {
  555. return color;
  556. }
  557. if (color.match(/^gradient\((.*)\)$/i)) {
  558. var parts = RegExp.$1.split(':');
  559. // Create the gradient
  560. var grad = co.createLinearGradient(prop['chart.gutter.left'],0,ca.width - prop['chart.gutter.right'],0);
  561. var diff = 1 / (parts.length - 1);
  562. grad.addColorStop(0, RGraph.trim(parts[0]));
  563. for (var j=1; j<parts.length; ++j) {
  564. grad.addColorStop(j * diff, RG.trim(parts[j]));
  565. }
  566. }
  567. return grad ? grad : color;
  568. }
  569. /**
  570. * This parses a single color value
  571. */
  572. this.parseSingleColorForVerticalGradient = function (color)
  573. {
  574. if (!color || typeof(color) != 'string') {
  575. return color;
  576. }
  577. if (color.match(/^gradient\((.*)\)$/i)) {
  578. var parts = RegExp.$1.split(':');
  579. // Create the gradient
  580. var grad = co.createLinearGradient(0, prop['chart.gutter.top'],0,ca.height - prop['chart.gutter.bottom']);
  581. var diff = 1 / (parts.length - 1);
  582. grad.addColorStop(0, RGraph.trim(parts[0]));
  583. for (var j=1; j<parts.length; ++j) {
  584. grad.addColorStop(j * diff, RG.trim(parts[j]));
  585. }
  586. }
  587. return grad ? grad : color;
  588. }
  589. /**
  590. * This function handles highlighting an entire data-series for the interactive
  591. * key
  592. *
  593. * @param int index The index of the data series to be highlighted
  594. */
  595. this.interactiveKeyHighlight = function (index)
  596. {
  597. var coords = this.coords[index];
  598. if (coords && coords.length == 8) {
  599. var pre_linewidth = co.lineWidth;
  600. co.lineWidth = 2;
  601. co.strokeStyle = prop['chart.key.interactive.highlight.chart.stroke'];
  602. co.fillStyle = prop['chart.key.interactive.highlight.chart.fill'];
  603. co.beginPath();
  604. co.moveTo(coords[0], coords[1]);
  605. co.lineTo(coords[2], coords[3]);
  606. co.lineTo(coords[4], coords[5]);
  607. co.lineTo(coords[6], coords[7]);
  608. co.closePath();
  609. co.fill();
  610. co.stroke();
  611. // Reset the linewidth
  612. co.lineWidth = pre_linewidth;
  613. }
  614. }
  615. /**
  616. * Always now regsiter the object
  617. */
  618. RG.Register(this);
  619. }