RGraph.common.key.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621
  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. * Draws the graph key (used by various graphs)
  17. *
  18. * @param object obj The graph object
  19. * @param array key An array of the texts to be listed in the key
  20. * @param colors An array of the colors to be used
  21. */
  22. RGraph.DrawKey = function (obj, key, colors)
  23. {
  24. if (!key) {
  25. return;
  26. }
  27. var RG = RGraph,
  28. ca = obj.canvas,
  29. co = obj.context,
  30. prop = obj.properties,
  31. // Key positioned in the gutter
  32. keypos = prop['chart.key.position'],
  33. textsize = prop['chart.text.size'],
  34. key_non_null = [],
  35. colors_non_null = [];
  36. co.lineWidth = 1;
  37. co.beginPath();
  38. /**
  39. * Change the older chart.key.vpos to chart.key.position.y
  40. */
  41. if (typeof(prop['chart.key.vpos']) == 'number') {
  42. obj.Set('chart.key.position.y', prop['chart.key.vpos'] * prop['chart.gutter.top']);
  43. }
  44. /**
  45. * Account for null values in the key
  46. */
  47. for (var i=0; i<key.length; ++i) {
  48. if (key[i] != null) {
  49. colors_non_null.push(colors[i]);
  50. key_non_null.push(key[i]);
  51. }
  52. }
  53. key = key_non_null;
  54. colors = colors_non_null;
  55. /**
  56. * This does the actual drawing of the key when it's in the graph
  57. *
  58. * @param object obj The graph object
  59. * @param array key The key items to draw
  60. * @param array colors An aray of colors that the key will use
  61. */
  62. function DrawKey_graph (obj, key, colors)
  63. {
  64. var text_size = typeof(prop['chart.key.text.size']) == 'number' ? prop['chart.key.text.size'] : prop['chart.text.size'];
  65. var text_font = prop['chart.text.font'];
  66. var gutterLeft = obj.gutterLeft;
  67. var gutterRight = obj.gutterRight;
  68. var gutterTop = obj.gutterTop;
  69. var gutterBottom = obj.gutterBottom;
  70. var hpos = prop['chart.yaxispos'] == 'right' ? gutterLeft + 10 : ca.width - gutterRight - 10;
  71. var vpos = gutterTop + 10;
  72. var title = prop['chart.title'];
  73. var blob_size = text_size; // The blob of color
  74. var hmargin = 8; // This is the size of the gaps between the blob of color and the text
  75. var vmargin = 4; // This is the vertical margin of the key
  76. var fillstyle = prop['chart.key.background'];
  77. var text_color = prop['chart.key.text.color'];
  78. var strokestyle = '#333';
  79. var height = 0;
  80. var width = 0;
  81. if (!obj.coords) obj.coords = {};
  82. obj.coords.key = [];
  83. // Need to set this so that measuring the text works out OK
  84. co.font = text_size + 'pt ' + prop['chart.text.font'];
  85. // Work out the longest bit of text
  86. for (i=0; i<key.length; ++i) {
  87. width = Math.max(width, co.measureText(key[i]).width);
  88. }
  89. width += 5;
  90. width += blob_size;
  91. width += 5;
  92. width += 5;
  93. width += 5;
  94. /**
  95. * Now we know the width, we can move the key left more accurately
  96. */
  97. if ( prop['chart.yaxispos'] == 'left'
  98. || (obj.type == 'pie' && !prop['chart.yaxispos'])
  99. || (obj.type == 'hbar' && !prop['chart.yaxispos'])
  100. || (obj.type == 'hbar' && prop['chart.yaxispos'] == 'center')
  101. || (obj.type == 'rscatter' && !prop['chart.yaxispos'])
  102. || (obj.type == 'radar' && !prop['chart.yaxispos'])
  103. || (obj.type == 'rose' && !prop['chart.yaxispos'])
  104. || (obj.type == 'funnel' && !prop['chart.yaxispos'])
  105. || (obj.type == 'vprogress' && !prop['chart.yaxispos'])
  106. || (obj.type == 'hprogress' && !prop['chart.yaxispos'])
  107. ) {
  108. hpos -= width;
  109. }
  110. /**
  111. * Horizontal alignment
  112. */
  113. if (typeof(prop['chart.key.halign']) == 'string') {
  114. if (prop['chart.key.halign'] == 'left') {
  115. hpos = gutterLeft + 10;
  116. } else if (prop['chart.key.halign'] == 'right') {
  117. hpos = ca.width - gutterRight - width;
  118. }
  119. }
  120. /**
  121. * Specific location coordinates
  122. */
  123. if (typeof(prop['chart.key.position.x']) == 'number') {
  124. hpos = prop['chart.key.position.x'];
  125. }
  126. if (typeof(prop['chart.key.position.y']) == 'number') {
  127. vpos = prop['chart.key.position.y'];
  128. }
  129. // Stipulate the shadow for the key box
  130. if (prop['chart.key.shadow']) {
  131. co.shadowColor = prop['chart.key.shadow.color'];
  132. co.shadowBlur = prop['chart.key.shadow.blur'];
  133. co.shadowOffsetX = prop['chart.key.shadow.offsetx'];
  134. co.shadowOffsetY = prop['chart.key.shadow.offsety'];
  135. }
  136. // Draw the box that the key resides in
  137. co.beginPath();
  138. co.fillStyle = prop['chart.key.background'];
  139. co.strokeStyle = 'black';
  140. if (typeof(prop['chart.key.position.graph.boxed']) == 'undefined' || (typeof(prop['chart.key.position.graph.boxed']) == 'boolean' && prop['chart.key.position.graph.boxed']) ) {
  141. if (arguments[3] != false) {
  142. co.lineWidth = typeof(prop['chart.key.linewidth']) == 'number' ? prop['chart.key.linewidth'] : 1;
  143. // The older square rectangled key
  144. if (prop['chart.key.rounded'] == true) {
  145. co.beginPath();
  146. co.strokeStyle = strokestyle;
  147. RG.strokedCurvyRect(co, Math.round(hpos), Math.round(vpos), width - 5, 5 + ( (text_size + 5) * RG.getKeyLength(key)),4);
  148. co.stroke();
  149. co.fill();
  150. RG.NoShadow(obj);
  151. } else {
  152. co.strokeRect(Math.round(hpos), Math.round(vpos), width - 5, 5 + ( (text_size + 5) * RG.getKeyLength(key)));
  153. co.fillRect(Math.round(hpos), Math.round(vpos), width - 5, 5 + ( (text_size + 5) * RG.getKeyLength(key)));
  154. }
  155. }
  156. }
  157. RG.NoShadow(obj);
  158. co.beginPath();
  159. /**
  160. * Custom colors for the key
  161. */
  162. if (prop['chart.key.colors']) {
  163. colors = prop['chart.key.colors'];
  164. }
  165. ////////////////////////////////////////////////////////////////////////////////////////////
  166. // Draw the labels given
  167. for (var i=key.length - 1; i>=0; i--) {
  168. var j = Number(i) + 1;
  169. /**
  170. * Draw the blob of color
  171. */
  172. if (typeof(prop['chart.key.color.shape']) == 'object' && typeof(prop['chart.key.color.shape'][i]) == 'string') {
  173. var blob_shape = prop['chart.key.color.shape'][i];
  174. } else if (typeof(prop['chart.key.color.shape']) == 'string') {
  175. var blob_shape = prop['chart.key.color.shape'];
  176. } else {
  177. var blob_shape = 'square';
  178. }
  179. if (blob_shape == 'circle') {
  180. co.beginPath();
  181. co.fillStyle = colors[i];
  182. co.arc(hpos + 5 + (blob_size / 2), vpos + (5 * j) + (text_size * j) - text_size + (blob_size / 2), blob_size / 2, 0, 6.26, 0);
  183. co.fill();
  184. } else if (blob_shape == 'line') {
  185. co.beginPath();
  186. co.strokeStyle = colors[i];
  187. co.moveTo(hpos + 5, vpos + (5 * j) + (text_size * j) - text_size + (blob_size / 2));
  188. co.lineTo(hpos + blob_size + 5, vpos + (5 * j) + (text_size * j) - text_size + (blob_size / 2));
  189. co.stroke();
  190. } else if (blob_shape == 'triangle') {
  191. co.beginPath();
  192. co.strokeStyle = colors[i];
  193. co.moveTo(hpos + 5, vpos + (5 * j) + (text_size * j) - text_size + blob_size);
  194. co.lineTo(hpos + (blob_size / 2) + 5, vpos + (5 * j) + (text_size * j) - text_size );
  195. co.lineTo(hpos + blob_size + 5, vpos + (5 * j) + (text_size * j) - text_size + blob_size);
  196. co.closePath();
  197. co.fillStyle = colors[i];
  198. co.fill();
  199. } else {
  200. co.fillStyle = colors[i];
  201. co.fillRect(hpos + 5, vpos + (5 * j) + (text_size * j) - text_size, text_size, text_size + 1);
  202. }
  203. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  204. co.beginPath();
  205. co.fillStyle = typeof text_color == 'object' ? text_color[i] : text_color;
  206. ret = RG.Text2(obj, {'font': text_font,
  207. 'size': text_size,
  208. 'x': hpos + blob_size + 5 + 5,
  209. 'y': vpos + (5 * j) + (text_size * j) + 3,
  210. 'text': key[i]});
  211. obj.coords.key[i] = [ret.x, ret.y, ret.width, ret.height, key[i], colors[i], obj];
  212. }
  213. co.fill();
  214. }
  215. /**
  216. * This does the actual drawing of the key when it's in the gutter
  217. *
  218. * @param object obj The graph object
  219. * @param array key The key items to draw
  220. * @param array colors An aray of colors that the key will use
  221. */
  222. function DrawKey_gutter (obj, key, colors)
  223. {
  224. var text_size = typeof(prop['chart.key.text.size']) == 'number' ? prop['chart.key.text.size'] : prop['chart.text.size'],
  225. text_font = prop['chart.text.font'],
  226. text_color = prop['chart.key.text.color'],
  227. gutterLeft = obj.gutterLeft,
  228. gutterRight = obj.gutterRight,
  229. gutterTop = obj.gutterTop,
  230. gutterBottom = obj.gutterBottom,
  231. hpos = ((ca.width - gutterLeft - gutterRight) / 2) + obj.gutterLeft,
  232. vpos = gutterTop - text_size - 5,
  233. title = prop['chart.title'],
  234. blob_size = text_size, // The blob of color
  235. hmargin = 8, // This is the size of the gaps between the blob of color and the text
  236. vmargin = 4, // This is the vertical margin of the key
  237. fillstyle = prop['chart.key.background'],
  238. strokestyle = '#999',
  239. length = 0;
  240. if (!obj.coords) obj.coords = {};
  241. obj.coords.key = [];
  242. // Need to work out the length of the key first
  243. co.font = text_size + 'pt ' + text_font;
  244. for (i=0; i<key.length; ++i) {
  245. length += hmargin;
  246. length += blob_size;
  247. length += hmargin;
  248. length += co.measureText(key[i]).width;
  249. }
  250. length += hmargin;
  251. /**
  252. * Work out hpos since in the Pie it isn't necessarily dead center
  253. */
  254. if (obj.type == 'pie') {
  255. if (prop['chart.align'] == 'left') {
  256. var hpos = obj.radius + gutterLeft;
  257. } else if (prop['chart.align'] == 'right') {
  258. var hpos = ca.width - obj.radius - gutterRight;
  259. } else {
  260. hpos = ca.width / 2;
  261. }
  262. }
  263. /**
  264. * This makes the key centered
  265. */
  266. hpos -= (length / 2);
  267. /**
  268. * Override the horizontal/vertical positioning
  269. */
  270. if (typeof(prop['chart.key.position.x']) == 'number') {
  271. hpos = prop['chart.key.position.x'];
  272. }
  273. if (typeof(prop['chart.key.position.y']) == 'number') {
  274. vpos = prop['chart.key.position.y'];
  275. }
  276. /**
  277. * Draw the box that the key sits in
  278. */
  279. if (obj.Get('chart.key.position.gutter.boxed')) {
  280. if (prop['chart.key.shadow']) {
  281. co.shadowColor = prop['chart.key.shadow.color'];
  282. co.shadowBlur = prop['chart.key.shadow.blur'];
  283. co.shadowOffsetX = prop['chart.key.shadow.offsetx'];
  284. co.shadowOffsetY = prop['chart.key.shadow.offsety'];
  285. }
  286. co.beginPath();
  287. co.fillStyle = fillstyle;
  288. co.strokeStyle = strokestyle;
  289. if (prop['chart.key.rounded']) {
  290. RG.strokedCurvyRect(co, hpos, vpos - vmargin, length, text_size + vmargin + vmargin)
  291. // Odd... RG.filledCurvyRect(co, hpos, vpos - vmargin, length, text_size + vmargin + vmargin);
  292. } else {
  293. co.rect(hpos, vpos - vmargin, length, text_size + vmargin + vmargin);
  294. }
  295. co.stroke();
  296. co.fill();
  297. RG.NoShadow(obj);
  298. }
  299. /**
  300. * Draw the blobs of color and the text
  301. */
  302. // Custom colors for the key
  303. if (prop['chart.key.colors']) {
  304. colors = prop['chart.key.colors'];
  305. }
  306. for (var i=0, pos=hpos; i<key.length; ++i) {
  307. pos += hmargin;
  308. //////////////////////////////////////////////////////////////////////////////////////////////////////
  309. // Draw the blob of color
  310. if (typeof(prop['chart.key.color.shape']) == 'object' && typeof(prop['chart.key.color.shape'][i]) == 'string') {
  311. var blob_shape = prop['chart.key.color.shape'][i];
  312. } else if (typeof(prop['chart.key.color.shape']) == 'string') {
  313. var blob_shape = prop['chart.key.color.shape'];
  314. } else {
  315. var blob_shape = 'square';
  316. }
  317. /**
  318. * Draw the blob of color - line
  319. */
  320. if (blob_shape =='line') {
  321. co.beginPath();
  322. co.strokeStyle = colors[i];
  323. co.moveTo(pos, vpos + (blob_size / 2));
  324. co.lineTo(pos + blob_size, vpos + (blob_size / 2));
  325. co.stroke();
  326. // Circle
  327. } else if (blob_shape == 'circle') {
  328. co.beginPath();
  329. co.fillStyle = colors[i];
  330. co.moveTo(pos, vpos + (blob_size / 2));
  331. co.arc(pos + (blob_size / 2), vpos + (blob_size / 2), (blob_size / 2), 0, 6.28, 0);
  332. co.fill();
  333. } else if (blob_shape == 'triangle') {
  334. co.fillStyle = colors[i];
  335. co.beginPath();
  336. co.strokeStyle = colors[i];
  337. co.moveTo(pos, vpos + blob_size);
  338. co.lineTo(pos + (blob_size / 2), vpos);
  339. co.lineTo(pos + blob_size, vpos + blob_size);
  340. co.closePath();
  341. co.fill();
  342. } else {
  343. co.beginPath();
  344. co.fillStyle = colors[i];
  345. co.rect(pos, vpos, blob_size, blob_size);
  346. co.fill();
  347. }
  348. //////////////////////////////////////////////////////////////////////////////////////////////////////
  349. pos += blob_size;
  350. pos += hmargin;
  351. co.beginPath();
  352. co.fillStyle = typeof text_color == 'object' ? text_color[i] : text_color;;
  353. var ret = RG.Text2(obj, {'font':text_font,'size':text_size,'x':pos,'y':vpos + text_size + 3, 'text': key[i]});
  354. co.fill();
  355. pos += co.measureText(key[i]).width;
  356. obj.coords.key[i] = [ret.x, ret.y, ret.width, ret.height, key[i], colors[i], obj];
  357. }
  358. }
  359. if (keypos && keypos == 'gutter') {
  360. DrawKey_gutter(obj, key, colors);
  361. } else if (keypos && keypos == 'graph') {
  362. DrawKey_graph(obj, key, colors);
  363. } else {
  364. alert('[COMMON] (' + obj.id + ') Unknown key position: ' + keypos);
  365. }
  366. if (prop['chart.key.interactive']) {
  367. if (!RGraph.Drawing || !RGraph.Drawing.Rect) {
  368. alert('[INTERACTIVE KEY] The drawing API Rect library does not appear to have been included (which the interactive key uses)');
  369. }
  370. /**
  371. * Check that the RGraph.common.dynamic.js file has been included
  372. */
  373. if (!RGraph.InstallWindowMousedownListener) {
  374. alert('[INTERACTIVE KEY] The dynamic library does not appear to have been included');
  375. }
  376. // Determine the maximum width of the labels
  377. for (var i=0,len=obj.coords.key.length,maxlen=0; i<len; i+=1) {
  378. maxlen = Math.max(maxlen, obj.coords.key[i][2]);
  379. }
  380. obj.coords.key.forEach(function (value, index, arr)
  381. {
  382. var rect = new RGraph.Drawing.Rect(obj.id,value[0], value[1], prop['chart.key.position'] == 'gutter' ? value[2] : maxlen, value[3])
  383. .Set('fillstyle', 'rgba(0,0,0,0)')
  384. .Draw();
  385. rect.onclick = function (e, shape)
  386. {
  387. var co = rect.context;
  388. co.fillStyle = prop['chart.key.interactive.highlight.label'];
  389. co.fillRect(shape.x, shape.y, shape.width, shape.height);
  390. if (typeof obj.interactiveKeyHighlight == 'function') {
  391. obj.Set('chart.key.interactive.index', index);
  392. RG.FireCustomEvent(obj, 'onbeforeinteractivekey');
  393. obj.interactiveKeyHighlight(index);
  394. RG.FireCustomEvent(obj, 'onafterinteractivekey');
  395. }
  396. }
  397. rect.onmousemove = function (e, shape)
  398. {
  399. e.target.style.cursor = 'pointer';
  400. }
  401. });
  402. }
  403. }
  404. /**
  405. * Returns the key length, but accounts for null values
  406. *
  407. * @param array key The key elements
  408. */
  409. RGraph.getKeyLength = function (key)
  410. {
  411. var len = 0;
  412. for (var i=0; i<key.length; ++i) {
  413. if (key[i] != null) {
  414. ++len;
  415. }
  416. }
  417. return len;
  418. }