RGraph.common.key.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716
  1. // version: 2014-06-26
  2. /**
  3. * o--------------------------------------------------------------------------------o
  4. * | This file is part of the RGraph package. RGraph is Free Software, licensed |
  5. * | under the MIT license - so it's free to use for all purposes. If you want to |
  6. * | donate to help keep the project going then you can do so here: |
  7. * | |
  8. * | http://www.rgraph.net/donate |
  9. * o--------------------------------------------------------------------------------o
  10. */
  11. RGraph = window.RGraph || {isRGraph: true};
  12. RGraph.HTML = RGraph.HTML || {};
  13. // Module pattern
  14. (function (win, doc, undefined)
  15. {
  16. var RG = RGraph,
  17. ua = navigator.userAgent,
  18. ma = Math;
  19. /**
  20. * Draws the graph key (used by various graphs)
  21. *
  22. * @param object obj The graph object
  23. * @param array key An array of the texts to be listed in the key
  24. * @param colors An array of the colors to be used
  25. */
  26. RG.drawKey =
  27. RG.DrawKey = function (obj, key, colors)
  28. {
  29. if (!key) {
  30. return;
  31. }
  32. var ca = obj.canvas,
  33. co = obj.context,
  34. prop = obj.properties,
  35. // Key positioned in the gutter
  36. keypos = prop['chart.key.position'],
  37. textsize = prop['chart.text.size'],
  38. key_non_null = [],
  39. colors_non_null = [];
  40. co.lineWidth = 1;
  41. co.beginPath();
  42. /**
  43. * Change the older chart.key.vpos to chart.key.position.y
  44. */
  45. if (typeof(prop['chart.key.vpos']) == 'number') {
  46. obj.Set('chart.key.position.y', prop['chart.key.vpos'] * prop['chart.gutter.top']);
  47. }
  48. /**
  49. * Account for null values in the key
  50. */
  51. for (var i=0; i<key.length; ++i) {
  52. if (key[i] != null) {
  53. colors_non_null.push(colors[i]);
  54. key_non_null.push(key[i]);
  55. }
  56. }
  57. key = key_non_null;
  58. colors = colors_non_null;
  59. /**
  60. * This does the actual drawing of the key when it's in the graph
  61. *
  62. * @param object obj The graph object
  63. * @param array key The key items to draw
  64. * @param array colors An aray of colors that the key will use
  65. */
  66. function DrawKey_graph (obj, key, colors)
  67. {
  68. var text_size = typeof(prop['chart.key.text.size']) == 'number' ? prop['chart.key.text.size'] : prop['chart.text.size'];
  69. var text_font = prop['chart.text.font'];
  70. var gutterLeft = obj.gutterLeft;
  71. var gutterRight = obj.gutterRight;
  72. var gutterTop = obj.gutterTop;
  73. var gutterBottom = obj.gutterBottom;
  74. var hpos = prop['chart.yaxispos'] == 'right' ? gutterLeft + 10 : ca.width - gutterRight - 10;
  75. var vpos = gutterTop + 10;
  76. var title = prop['chart.title'];
  77. var blob_size = text_size; // The blob of color
  78. var hmargin = 8; // This is the size of the gaps between the blob of color and the text
  79. var vmargin = 4; // This is the vertical margin of the key
  80. var fillstyle = prop['chart.key.background'];
  81. var text_color = prop['chart.key.text.color'];
  82. var strokestyle = '#333';
  83. var height = 0;
  84. var width = 0;
  85. if (!obj.coords) obj.coords = {};
  86. obj.coords.key = [];
  87. // Need to set this so that measuring the text works out OK
  88. co.font = text_size + 'pt ' + prop['chart.text.font'];
  89. // Work out the longest bit of text
  90. for (i=0; i<key.length; ++i) {
  91. width = Math.max(width, co.measureText(key[i]).width);
  92. }
  93. width += 5;
  94. width += blob_size;
  95. width += 5;
  96. width += 5;
  97. width += 5;
  98. /**
  99. * Now we know the width, we can move the key left more accurately
  100. */
  101. if ( prop['chart.yaxispos'] == 'left'
  102. || (obj.type === 'pie' && !prop['chart.yaxispos'])
  103. || (obj.type === 'hbar' && !prop['chart.yaxispos'])
  104. || (obj.type === 'hbar' && prop['chart.yaxispos'] === 'center')
  105. || (obj.type === 'hbar' && prop['chart.yaxispos'] === 'right')
  106. || (obj.type === 'rscatter' && !prop['chart.yaxispos'])
  107. || (obj.type === 'radar' && !prop['chart.yaxispos'])
  108. || (obj.type === 'rose' && !prop['chart.yaxispos'])
  109. || (obj.type === 'funnel' && !prop['chart.yaxispos'])
  110. || (obj.type === 'vprogress' && !prop['chart.yaxispos'])
  111. || (obj.type === 'hprogress' && !prop['chart.yaxispos'])
  112. ) {
  113. hpos -= width;
  114. }
  115. /**
  116. * Horizontal alignment
  117. */
  118. if (typeof(prop['chart.key.halign']) == 'string') {
  119. if (prop['chart.key.halign'] == 'left') {
  120. hpos = gutterLeft + 10;
  121. } else if (prop['chart.key.halign'] == 'right') {
  122. hpos = ca.width - gutterRight - width;
  123. }
  124. }
  125. /**
  126. * Specific location coordinates
  127. */
  128. if (typeof(prop['chart.key.position.x']) == 'number') {
  129. hpos = prop['chart.key.position.x'];
  130. }
  131. if (typeof(prop['chart.key.position.y']) == 'number') {
  132. vpos = prop['chart.key.position.y'];
  133. }
  134. // Stipulate the shadow for the key box
  135. if (prop['chart.key.shadow']) {
  136. co.shadowColor = prop['chart.key.shadow.color'];
  137. co.shadowBlur = prop['chart.key.shadow.blur'];
  138. co.shadowOffsetX = prop['chart.key.shadow.offsetx'];
  139. co.shadowOffsetY = prop['chart.key.shadow.offsety'];
  140. }
  141. // Draw the box that the key resides in
  142. co.beginPath();
  143. co.fillStyle = prop['chart.key.background'];
  144. co.strokeStyle = 'black';
  145. if (typeof(prop['chart.key.position.graph.boxed']) == 'undefined' || (typeof(prop['chart.key.position.graph.boxed']) == 'boolean' && prop['chart.key.position.graph.boxed']) ) {
  146. if (arguments[3] != false) {
  147. co.lineWidth = typeof(prop['chart.key.linewidth']) == 'number' ? prop['chart.key.linewidth'] : 1;
  148. // The older square rectangled key
  149. if (prop['chart.key.rounded'] == true) {
  150. co.beginPath();
  151. co.strokeStyle = strokestyle;
  152. RG.strokedCurvyRect(co, Math.round(hpos), Math.round(vpos), width - 5, 5 + ( (text_size + 5) * RG.getKeyLength(key)),4);
  153. co.stroke();
  154. co.fill();
  155. RG.NoShadow(obj);
  156. } else {
  157. co.strokeRect(Math.round(hpos), Math.round(vpos), width - 5, 5 + ( (text_size + 5) * RG.getKeyLength(key)));
  158. co.fillRect(Math.round(hpos), Math.round(vpos), width - 5, 5 + ( (text_size + 5) * RG.getKeyLength(key)));
  159. }
  160. }
  161. }
  162. RG.NoShadow(obj);
  163. co.beginPath();
  164. /**
  165. * Custom colors for the key
  166. */
  167. if (prop['chart.key.colors']) {
  168. colors = prop['chart.key.colors'];
  169. }
  170. ////////////////////////////////////////////////////////////////////////////////////////////
  171. // Draw the labels given
  172. for (var i=key.length - 1; i>=0; i--) {
  173. var j = Number(i) + 1;
  174. /**
  175. * Draw the blob of color
  176. */
  177. if (typeof(prop['chart.key.color.shape']) == 'object' && typeof(prop['chart.key.color.shape'][i]) == 'string') {
  178. var blob_shape = prop['chart.key.color.shape'][i];
  179. } else if (typeof(prop['chart.key.color.shape']) == 'string') {
  180. var blob_shape = prop['chart.key.color.shape'];
  181. } else {
  182. var blob_shape = 'square';
  183. }
  184. if (blob_shape == 'circle') {
  185. co.beginPath();
  186. co.fillStyle = colors[i];
  187. 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);
  188. co.fill();
  189. } else if (blob_shape == 'line') {
  190. co.beginPath();
  191. co.strokeStyle = colors[i];
  192. co.moveTo(hpos + 5, vpos + (5 * j) + (text_size * j) - text_size + (blob_size / 2));
  193. co.lineTo(hpos + blob_size + 5, vpos + (5 * j) + (text_size * j) - text_size + (blob_size / 2));
  194. co.stroke();
  195. } else if (blob_shape == 'triangle') {
  196. co.beginPath();
  197. co.strokeStyle = colors[i];
  198. co.moveTo(hpos + 5, vpos + (5 * j) + (text_size * j) - text_size + blob_size);
  199. co.lineTo(hpos + (blob_size / 2) + 5, vpos + (5 * j) + (text_size * j) - text_size );
  200. co.lineTo(hpos + blob_size + 5, vpos + (5 * j) + (text_size * j) - text_size + blob_size);
  201. co.closePath();
  202. co.fillStyle = colors[i];
  203. co.fill();
  204. } else {
  205. co.fillStyle = colors[i];
  206. co.fillRect(hpos + 5, vpos + (5 * j) + (text_size * j) - text_size, text_size, text_size + 1);
  207. }
  208. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  209. co.beginPath();
  210. co.fillStyle = typeof text_color == 'object' ? text_color[i] : text_color;
  211. ret = RG.Text2(obj, {'font': text_font,
  212. 'size': text_size,
  213. 'x': hpos + blob_size + 5 + 5,
  214. 'y': vpos + (5 * j) + (text_size * j) + 3,
  215. 'text': key[i]});
  216. obj.coords.key[i] = [ret.x, ret.y, ret.width, ret.height, key[i], colors[i], obj];
  217. }
  218. co.fill();
  219. }
  220. /**
  221. * This does the actual drawing of the key when it's in the gutter
  222. *
  223. * @param object obj The graph object
  224. * @param array key The key items to draw
  225. * @param array colors An aray of colors that the key will use
  226. */
  227. function DrawKey_gutter (obj, key, colors)
  228. {
  229. var text_size = typeof(prop['chart.key.text.size']) == 'number' ? prop['chart.key.text.size'] : prop['chart.text.size'],
  230. text_font = prop['chart.text.font'],
  231. text_color = prop['chart.key.text.color'],
  232. gutterLeft = obj.gutterLeft,
  233. gutterRight = obj.gutterRight,
  234. gutterTop = obj.gutterTop,
  235. gutterBottom = obj.gutterBottom,
  236. hpos = ((ca.width - gutterLeft - gutterRight) / 2) + obj.gutterLeft,
  237. vpos = gutterTop - text_size - 5,
  238. title = prop['chart.title'],
  239. blob_size = text_size, // The blob of color
  240. hmargin = 8, // This is the size of the gaps between the blob of color and the text
  241. vmargin = 4, // This is the vertical margin of the key
  242. fillstyle = prop['chart.key.background'],
  243. strokestyle = '#999',
  244. length = 0;
  245. if (!obj.coords) obj.coords = {};
  246. obj.coords.key = [];
  247. // Need to work out the length of the key first
  248. co.font = text_size + 'pt ' + text_font;
  249. for (i=0; i<key.length; ++i) {
  250. length += hmargin;
  251. length += blob_size;
  252. length += hmargin;
  253. length += co.measureText(key[i]).width;
  254. }
  255. length += hmargin;
  256. /**
  257. * Work out hpos since in the Pie it isn't necessarily dead center
  258. */
  259. if (obj.type == 'pie') {
  260. if (prop['chart.align'] == 'left') {
  261. var hpos = obj.radius + gutterLeft;
  262. } else if (prop['chart.align'] == 'right') {
  263. var hpos = ca.width - obj.radius - gutterRight;
  264. } else {
  265. hpos = ca.width / 2;
  266. }
  267. }
  268. /**
  269. * This makes the key centered
  270. */
  271. hpos -= (length / 2);
  272. /**
  273. * Override the horizontal/vertical positioning
  274. */
  275. if (typeof(prop['chart.key.position.x']) == 'number') {
  276. hpos = prop['chart.key.position.x'];
  277. }
  278. if (typeof(prop['chart.key.position.y']) == 'number') {
  279. vpos = prop['chart.key.position.y'];
  280. }
  281. /**
  282. * Draw the box that the key sits in
  283. */
  284. if (obj.Get('chart.key.position.gutter.boxed')) {
  285. if (prop['chart.key.shadow']) {
  286. co.shadowColor = prop['chart.key.shadow.color'];
  287. co.shadowBlur = prop['chart.key.shadow.blur'];
  288. co.shadowOffsetX = prop['chart.key.shadow.offsetx'];
  289. co.shadowOffsetY = prop['chart.key.shadow.offsety'];
  290. }
  291. co.beginPath();
  292. co.fillStyle = fillstyle;
  293. co.strokeStyle = strokestyle;
  294. if (prop['chart.key.rounded']) {
  295. RG.strokedCurvyRect(co, hpos, vpos - vmargin, length, text_size + vmargin + vmargin)
  296. // Odd... RG.filledCurvyRect(co, hpos, vpos - vmargin, length, text_size + vmargin + vmargin);
  297. } else {
  298. co.rect(hpos, vpos - vmargin, length, text_size + vmargin + vmargin);
  299. }
  300. co.stroke();
  301. co.fill();
  302. RG.NoShadow(obj);
  303. }
  304. /**
  305. * Draw the blobs of color and the text
  306. */
  307. // Custom colors for the key
  308. if (prop['chart.key.colors']) {
  309. colors = prop['chart.key.colors'];
  310. }
  311. for (var i=0, pos=hpos; i<key.length; ++i) {
  312. pos += hmargin;
  313. //////////////////////////////////////////////////////////////////////////////////////////////////////
  314. // Draw the blob of color
  315. if (typeof(prop['chart.key.color.shape']) == 'object' && typeof(prop['chart.key.color.shape'][i]) == 'string') {
  316. var blob_shape = prop['chart.key.color.shape'][i];
  317. } else if (typeof(prop['chart.key.color.shape']) == 'string') {
  318. var blob_shape = prop['chart.key.color.shape'];
  319. } else {
  320. var blob_shape = 'square';
  321. }
  322. /**
  323. * Draw the blob of color - line
  324. */
  325. if (blob_shape =='line') {
  326. co.beginPath();
  327. co.strokeStyle = colors[i];
  328. co.moveTo(pos, vpos + (blob_size / 2));
  329. co.lineTo(pos + blob_size, vpos + (blob_size / 2));
  330. co.stroke();
  331. // Circle
  332. } else if (blob_shape == 'circle') {
  333. co.beginPath();
  334. co.fillStyle = colors[i];
  335. co.moveTo(pos, vpos + (blob_size / 2));
  336. co.arc(pos + (blob_size / 2), vpos + (blob_size / 2), (blob_size / 2), 0, 6.28, 0);
  337. co.fill();
  338. } else if (blob_shape == 'triangle') {
  339. co.fillStyle = colors[i];
  340. co.beginPath();
  341. co.strokeStyle = colors[i];
  342. co.moveTo(pos, vpos + blob_size);
  343. co.lineTo(pos + (blob_size / 2), vpos);
  344. co.lineTo(pos + blob_size, vpos + blob_size);
  345. co.closePath();
  346. co.fill();
  347. } else {
  348. co.beginPath();
  349. co.fillStyle = colors[i];
  350. co.rect(pos, vpos, blob_size, blob_size);
  351. co.fill();
  352. }
  353. //////////////////////////////////////////////////////////////////////////////////////////////////////
  354. pos += blob_size;
  355. pos += hmargin;
  356. co.beginPath();
  357. co.fillStyle = typeof text_color == 'object' ? text_color[i] : text_color;;
  358. var ret = RG.Text2(obj, {'font':text_font,'size':text_size,'x':pos,'y':vpos + text_size + 3, 'text': key[i]});
  359. co.fill();
  360. pos += co.measureText(key[i]).width;
  361. obj.coords.key[i] = [ret.x, ret.y, ret.width, ret.height, key[i], colors[i], obj];
  362. }
  363. }
  364. if (keypos && keypos == 'gutter') {
  365. DrawKey_gutter(obj, key, colors);
  366. } else if (keypos && keypos == 'graph') {
  367. DrawKey_graph(obj, key, colors);
  368. } else {
  369. alert('[COMMON] (' + obj.id + ') Unknown key position: ' + keypos);
  370. }
  371. if (prop['chart.key.interactive']) {
  372. if (!RGraph.Drawing || !RGraph.Drawing.Rect) {
  373. alert('[INTERACTIVE KEY] The drawing API Rect library does not appear to have been included (which the interactive key uses)');
  374. }
  375. /**
  376. * Check that the RGraph.common.dynamic.js file has been included
  377. */
  378. if (!RGraph.InstallWindowMousedownListener) {
  379. alert('[INTERACTIVE KEY] The dynamic library does not appear to have been included');
  380. }
  381. // Determine the maximum width of the labels
  382. for (var i=0,len=obj.coords.key.length,maxlen=0; i<len; i+=1) {
  383. maxlen = Math.max(maxlen, obj.coords.key[i][2]);
  384. }
  385. //obj.coords.key.forEach(function (value, index, arr)
  386. //{
  387. for (var i=0,len=obj.coords.key.length; i<len; i+=1) {
  388. // Because the loop would have finished when the i variable is needed - put
  389. // the onclick function inside a new context so that the value of the i
  390. // variable is what we expect when the key has been clicked
  391. (function (idx)
  392. {
  393. var arr = obj.coords.key;
  394. var value = obj.coords.key[idx];
  395. var index = idx;
  396. var rect = new RGraph.Drawing.Rect(obj.id,value[0], value[1], prop['chart.key.position'] == 'gutter' ? value[2] : maxlen, value[3])
  397. .Set('fillstyle', 'rgba(0,0,0,0)')
  398. .Draw();
  399. rect.onclick = function (e, shape)
  400. {
  401. var co = rect.context;
  402. co.fillStyle = prop['chart.key.interactive.highlight.label'];
  403. co.fillRect(shape.x, shape.y, shape.width, shape.height);
  404. if (typeof obj.interactiveKeyHighlight == 'function') {
  405. obj.Set('chart.key.interactive.index', idx);
  406. RG.FireCustomEvent(obj, 'onbeforeinteractivekey');
  407. obj.interactiveKeyHighlight(index);
  408. RG.FireCustomEvent(obj, 'onafterinteractivekey');
  409. }
  410. }
  411. rect.onmousemove = function (e, shape)
  412. {
  413. e.target.style.cursor = 'pointer';
  414. }
  415. })(i);
  416. }
  417. }
  418. };
  419. /**
  420. * Returns the key length, but accounts for null values
  421. *
  422. * @param array key The key elements
  423. */
  424. RG.getKeyLength = function (key)
  425. {
  426. var length = 0;
  427. for (var i=0,len=key.length; i<len; i+=1) {
  428. if (key[i] != null) {
  429. ++length;
  430. }
  431. }
  432. return length;
  433. };
  434. /**
  435. * Create a TABLE based HTML key. There's lots of options so it's
  436. * suggested that you consult the documentation page
  437. *
  438. * @param mixed id This should be a string consisting of the ID of the container
  439. * @param object prop An object map of the various properties that you can use to
  440. * configure the key. See the documentation page for a list.
  441. */
  442. RGraph.HTML.key =
  443. RGraph.HTML.Key = function (id, prop)
  444. {
  445. var div = doc.getElementById(id);
  446. /**
  447. * Create the table that becomes the key
  448. */
  449. var str = '<table border="0" cellspacing="0" cellpadding="0" id="rgraph_key" style="display: inline;' + (function ()
  450. {
  451. var style = ''
  452. for (i in prop.tableCss) {
  453. if (typeof i === 'string') {
  454. style = style + i + ': ' + prop.tableCss[i] + ';';
  455. }
  456. }
  457. return style;
  458. })() + '" ' + (prop.tableClass ? 'class="' + prop.tableClass + '"' : '') + '>';
  459. /**
  460. * Add the individual key elements
  461. */
  462. for (var i=0; i<prop.labels.length; i+=1) {
  463. str += '<tr><td><div style="' + (function ()
  464. {
  465. var style = '';
  466. for (var j in prop.blobCss) {
  467. if (typeof j === 'string') {
  468. style = style + j + ': ' + prop.blobCss[j] + ';';
  469. }
  470. }
  471. return style;
  472. })() + 'display: inline-block; margin-right: 5px; margin-top: 4px; width: 15px; height: 15px; background-color: ' + prop.colors[i] + '"' + (prop.blobClass ? 'class="' + prop.blobClass + '"' : '') + '>&nbsp;</div><td>' + (prop.links && prop.links[i] ? '<a href="' + prop.links[i] + '">' : '') + '<span ' + (prop.labelClass ? 'class="' + prop.labelClass + '"' : '') + '" ' + (function ()
  473. {
  474. var style = '';
  475. for (var j in prop.labelCss) {
  476. if (typeof j === 'string') {
  477. style = style + j + ': ' + prop.labelCss[j] + ';';
  478. }
  479. }
  480. return style;
  481. })() + (function ()
  482. {
  483. var style = '';
  484. if (prop['labelCss_' + i]) {
  485. for (var j in prop['labelCss_' + i]) {
  486. style = style + j + ': ' + prop['labelCss_' + i][j] + ';';
  487. }
  488. }
  489. return style ? 'style="' + style + '"' : '';
  490. })() + '>' + prop.labels[i] + '</span>' + (prop.links && prop.links[i] ? '</a>' : '') + '</td></tr>';
  491. }
  492. div.innerHTML += (str + '</table>');
  493. // Return the TABLE object that is the HTML key
  494. return doc.getElementById('rgraph_key');
  495. };
  496. // End module pattern
  497. })(window, document);