RGraph.common.tooltips.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. 0 /**
  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 = {isRGraph:true,type:'common'};
  15. /**
  16. * This is used in two functions, hence it's here
  17. */
  18. RGraph.tooltips = {};
  19. RGraph.tooltips.padding = '3px';
  20. RGraph.tooltips.font_face = 'Tahoma';
  21. RGraph.tooltips.font_size = '10pt';
  22. /**
  23. * Shows a tooltip next to the mouse pointer
  24. *
  25. * @param canvas object The canvas element object
  26. * @param text string The tooltip text
  27. * @param int x The X position that the tooltip should appear at. Combined with the canvases offsetLeft
  28. * gives the absolute X position
  29. * @param int y The Y position the tooltip should appear at. Combined with the canvases offsetTop
  30. * gives the absolute Y position
  31. * @param int idx The index of the tooltip in the graph objects tooltip array
  32. * @param object e The event object
  33. */
  34. RGraph.Tooltip = function (obj, text, x, y, idx, e)
  35. {
  36. /**
  37. * chart.tooltip.override allows you to totally take control of rendering the tooltip yourself
  38. */
  39. if (typeof(obj.Get('chart.tooltips.override')) == 'function') {
  40. return obj.Get('chart.tooltips.override')(obj, text, x, y, idx);
  41. }
  42. /**
  43. * Save the X/Y coords
  44. */
  45. var originalX = x;
  46. var originalY = y;
  47. /**
  48. * This facilitates the "id:xxx" format
  49. */
  50. text = RGraph.getTooltipTextFromDIV(text);
  51. /**
  52. * First clear any exising timers
  53. */
  54. var timers = RGraph.Registry.Get('chart.tooltip.timers');
  55. if (timers && timers.length) {
  56. for (i=0; i<timers.length; ++i) {
  57. clearTimeout(timers[i]);
  58. }
  59. }
  60. RGraph.Registry.Set('chart.tooltip.timers', []);
  61. /**
  62. * Hide the context menu if it's currently shown
  63. */
  64. if (obj.Get('chart.contextmenu')) {
  65. RGraph.HideContext();
  66. }
  67. var effect = obj.Get('chart.tooltips.effect') ? obj.Get('chart.tooltips.effect').toLowerCase() : 'fade';
  68. /**
  69. * Show a tool tip
  70. */
  71. var tooltipObj = document.createElement('DIV');
  72. tooltipObj.className = obj.Get('chart.tooltips.css.class');
  73. tooltipObj.style.display = 'none';
  74. tooltipObj.style.position = RGraph.isFixed(obj.canvas) ? 'fixed' : 'absolute';
  75. tooltipObj.style.left = 0;
  76. tooltipObj.style.top = 0;
  77. tooltipObj.style.backgroundColor = 'rgb(255,255,239)';
  78. tooltipObj.style.color = 'black';
  79. if (!document.all) tooltipObj.style.border = '';
  80. tooltipObj.style.visibility = 'visible';
  81. tooltipObj.style.paddingLeft = RGraph.tooltips.padding;
  82. tooltipObj.style.paddingRight = RGraph.tooltips.padding;
  83. tooltipObj.style.fontFamily = RGraph.tooltips.font_face;
  84. tooltipObj.style.fontSize = RGraph.tooltips.font_size;
  85. tooltipObj.style.zIndex = 3;
  86. // Only apply a border if there's content
  87. if (RGraph.trim(text).length > 0) {
  88. tooltipObj.style.border = '1px #bbb solid';
  89. }
  90. tooltipObj.style.borderRadius = '5px';
  91. tooltipObj.style.MozBorderRadius = '5px';
  92. tooltipObj.style.WebkitBorderRadius = '5px';
  93. tooltipObj.style.WebkitBoxShadow = 'rgba(96,96,96,0.5) 0 0 15px';
  94. tooltipObj.style.MozBoxShadow = 'rgba(96,96,96,0.5) 0 0 15px';
  95. tooltipObj.style.boxShadow = 'rgba(96,96,96,0.5) 0 0 15px';
  96. tooltipObj.style.filter = 'progid:DXImageTransform.Microsoft.Shadow(color=#666666,direction=135)';
  97. tooltipObj.style.opacity = 0;
  98. //tooltipObj.style.overflow = 'hidden';
  99. tooltipObj.innerHTML = text;
  100. tooltipObj.__text__ = text; // This is set because the innerHTML can change when it's set
  101. tooltipObj.__canvas__ = obj.canvas;
  102. tooltipObj.style.display = 'inline';
  103. tooltipObj.id = '__rgraph_tooltip_' + obj.canvas.id + '_' + obj.uid + '_'+ idx;
  104. tooltipObj.__event__ = obj.Get('chart.tooltips.event') || 'click';
  105. tooltipObj.__object__ = obj;
  106. if (typeof(idx) == 'number') {
  107. tooltipObj.__index__ = idx;
  108. origIdx = idx;
  109. }
  110. if (obj.type == 'line' || obj.type == 'radar') {
  111. for (var ds=0; ds<obj.data.length; ++ds) {
  112. if (idx >= obj.data[ds].length) {
  113. idx -= obj.data[ds].length;
  114. } else {
  115. break;
  116. }
  117. }
  118. tooltipObj.__dataset__ = ds;
  119. tooltipObj.__index2__ = idx;
  120. }
  121. document.body.appendChild(tooltipObj);
  122. var width = tooltipObj.offsetWidth;
  123. var height = tooltipObj.offsetHeight;
  124. /**
  125. * Set the width on the tooltip so it doesn't resize if the window is resized
  126. */
  127. tooltipObj.style.width = width + 'px';
  128. tooltipObj.style.top = (y - height - 2) + 'px';
  129. /**
  130. * If the function exists call the object specific tooltip positioning function
  131. */
  132. if (typeof(obj.positionTooltip) == 'function') {
  133. if (tooltipObj.innerHTML.length > 0) {
  134. obj.positionTooltip(obj, x, y, tooltipObj, origIdx ? origIdx : idx);
  135. if (obj.Get('chart.tooltips.coords.page')) {
  136. tooltipObj.style.left = e.pageX - (width / 2) - 4.25 + 'px';
  137. tooltipObj.style.top = e.pageY - height - 10 + 'px';
  138. document.getElementById('__rgraph_tooltip_pointer__').style.left = (parseInt(tooltipObj.offsetWidth) / 2) - 8.5 + 'px';
  139. }
  140. }
  141. } else {
  142. tooltipObj.style.left = e.pageX - (width / 2) - 4.25 + 'px';
  143. tooltipObj.style.top = e.pageY - height - 7 + 'px';
  144. }
  145. if (effect == 'fade' || effect == 'expand' || effect == 'contract' || effect == 'snap') {
  146. setTimeout(function () {tooltipObj.style.opacity = 0.1;}, 25);
  147. setTimeout(function () {tooltipObj.style.opacity = 0.2;}, 50);
  148. setTimeout(function () {tooltipObj.style.opacity = 0.3;}, 75);
  149. setTimeout(function () {tooltipObj.style.opacity = 0.4;}, 100);
  150. setTimeout(function () {tooltipObj.style.opacity = 0.5;}, 125);
  151. setTimeout(function () {tooltipObj.style.opacity = 0.6;}, 150);
  152. setTimeout(function () {tooltipObj.style.opacity = 0.7;}, 175);
  153. setTimeout(function () {tooltipObj.style.opacity = 0.8;}, 200);
  154. setTimeout(function () {tooltipObj.style.opacity = 0.9;}, 225);
  155. if (effect == 'expand' || effect == 'contract' || effect == 'snap') {
  156. console.log('[RGRAPH] The snap, expand and contract tooltip effects are deprecated. Available effects now are fade and none');
  157. }
  158. }
  159. setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.opacity = 1;}", effect == 'none' ? 50 : 250);
  160. /**
  161. * If the tooltip it self is clicked, cancel it
  162. */
  163. tooltipObj.onmousedown = function (e){e.stopPropagation();}
  164. tooltipObj.onmouseup = function (e){e.stopPropagation();}
  165. tooltipObj.onclick = function (e){if (e.button == 0) {e.stopPropagation();}}
  166. /**
  167. * Keep a reference to the tooltip in the registry
  168. */
  169. RGraph.Registry.Set('chart.tooltip', tooltipObj);
  170. /**
  171. * Fire the tooltip event
  172. */
  173. RGraph.FireCustomEvent(obj, 'ontooltip');
  174. }
  175. /**
  176. *
  177. */
  178. RGraph.getTooltipTextFromDIV = function (text)
  179. {
  180. // This regex is duplicated firher down on roughly line 888
  181. var result = /^id:(.*)/.exec(text);
  182. if (result && result[1] && document.getElementById(result[1])) {
  183. text = document.getElementById(result[1]).innerHTML;
  184. } else if (result && result[1]) {
  185. text = '';
  186. }
  187. return text;
  188. }
  189. /**
  190. *
  191. */
  192. RGraph.getTooltipWidth = function (text, obj)
  193. {
  194. var div = document.createElement('DIV');
  195. div.className = obj.Get('chart.tooltips.css.class');
  196. div.style.paddingLeft = RGraph.tooltips.padding;
  197. div.style.paddingRight = RGraph.tooltips.padding;
  198. div.style.fontFamily = RGraph.tooltips.font_face;
  199. div.style.fontSize = RGraph.tooltips.font_size;
  200. div.style.visibility = 'hidden';
  201. div.style.position = 'absolute';
  202. div.style.top = '300px';
  203. div.style.left = 0;
  204. div.style.display = 'inline';
  205. div.innerHTML = RGraph.getTooltipTextFromDIV(text);
  206. document.body.appendChild(div);
  207. return div.offsetWidth;
  208. }
  209. /**
  210. * Hides the currently shown tooltip
  211. */
  212. RGraph.HideTooltip = function ()
  213. {
  214. var tooltip = RGraph.Registry.Get('chart.tooltip');
  215. var uid = arguments[0] && arguments[0].uid ? arguments[0].uid : null;
  216. if (tooltip && tooltip.parentNode && (!uid || uid == tooltip.__canvas__.uid)) {
  217. tooltip.parentNode.removeChild(tooltip);
  218. tooltip.style.display = 'none';
  219. tooltip.style.visibility = 'hidden';
  220. RGraph.Registry.Set('chart.tooltip', null);
  221. }
  222. }
  223. /**
  224. * This installs the window mousedown event listener. It clears any highlight that may
  225. * be present.
  226. *
  227. * @param object obj The chart object
  228. *
  229. RGraph.InstallWindowMousedownTooltipListener = function (obj)
  230. {
  231. if (RGraph.Registry.Get('__rgraph_event_listeners__')['window_mousedown']) {
  232. return;
  233. }
  234. // When the canvas is cleared, reset this flag so that the event listener is installed again
  235. RGraph.AddCustomEventListener(obj, 'onclear', function (obj) {RGraph.Registry.Get('__rgraph_event_listeners__')['window_mousedown'] = false;})
  236. // NOTE: Global on purpose
  237. rgraph_window_mousedown = function (e)
  238. {
  239. if (RGraph.Registry.Get('chart.tooltip')) {
  240. var obj = RGraph.Registry.Get('chart.tooltip').__object__;
  241. var canvas = obj.canvas;
  242. /**
  243. * Get rid of the tooltip and redraw all canvases on the page
  244. *
  245. RGraph.HideTooltip();
  246. /**
  247. * No need to clear if highlighting is disabled
  248. *
  249. * TODO Really, need to check ALL of the pertinent objects that
  250. * are drawing on the canvas using the ObjectRegistry -
  251. * ie RGraph.ObjectRegistry.getObjectsByCanvasID()
  252. *
  253. if (obj.Get('chart.tooltips.highlight')) {
  254. RGraph.RedrawCanvas(canvas);
  255. }
  256. }
  257. }
  258. window.addEventListener('mousedown', rgraph_window_mousedown, false);
  259. RGraph.AddEventListener('window_' + obj.id, 'mousedown', rgraph_window_mousedown);
  260. }
  261. */
  262. /**
  263. * This installs the canvas mouseup event listener. This is the function that
  264. * actually shows the appropriate (if any) tooltip.
  265. *
  266. * @param object obj The chart object
  267. *
  268. RGraph.InstallCanvasMouseupTooltipListener = function (obj)
  269. {
  270. if (RGraph.Registry.Get('__rgraph_event_listeners__')[obj.canvas.id + '_mouseup']) {
  271. return;
  272. }
  273. RGraph.Registry.Get('__rgraph_event_listeners__')[obj.canvas.id + '_mouseup'] = true;
  274. // When the canvas is cleared, reset this flag so that the event listener is installed again
  275. RGraph.AddCustomEventListener(obj, 'onclear', function (obj) {RGraph.Registry.Get('__rgraph_event_listeners__')[obj.canvas.id + '_mouseup'] = false});
  276. // Install the onclick event handler for the tooltips
  277. //
  278. // // NOTE: Global on purpose
  279. rgraph_canvas_mouseup_func = function (e)
  280. {
  281. var x = arguments[1] ? arguments[1] : e.pageX;
  282. var y = arguments[2] ? arguments[2] : e.pageY;
  283. var objects = RGraph.ObjectRegistry.getObjectsByCanvasID(e.target.id);
  284. // It's important to go backwards through the array so that the front charts
  285. // are checked first, then the charts at the back
  286. for (var i=(objects.length - 1); i>=0; --i) {
  287. var shape = objects[i].getShape(e);
  288. if (shape && shape['object'] && !RGraph.Registry.Get('chart.tooltip')) {
  289. /**
  290. * This allows the Scatter chart funky tooltips style
  291. *
  292. if (objects[i].type == 'scatter' && shape['dataset'] > 0) {
  293. for (var j=0; j<(objects[i].data.length - 1); ++j) {
  294. shape['index'] += objects[i].data[j].length;
  295. }
  296. }
  297. var text = RGraph.parseTooltipText(objects[i].Get('chart.tooltips'), shape['index']);
  298. if (text) {
  299. if (shape['object'].Get('chart.tooltips.hotspot.xonly')) {
  300. var canvasXY = RGraph.getCanvasXY(objects[i].canvas);
  301. x = canvasXY[0] + shape[1];
  302. y = canvasXY[1] + shape[2];
  303. }
  304. RGraph.Tooltip(objects[i], text, x, y, shape['index']);
  305. objects[i].Highlight(shape);
  306. e.stopPropagation();
  307. e.cancelBubble = true;
  308. return false;
  309. }
  310. }
  311. }
  312. }
  313. obj.canvas.addEventListener('mouseup', rgraph_canvas_mouseup_func, false);
  314. RGraph.AddEventListener(obj.id, 'mouseup', rgraph_canvas_mouseup_func);
  315. }
  316. */
  317. /**
  318. * This installs the canvas mousemove event listener. This is the function that
  319. * changes the mouse pointer if need be.
  320. *
  321. * @param object obj The chart object
  322. *
  323. RGraph.InstallCanvasMousemoveTooltipListener = function (obj)
  324. {
  325. if (RGraph.Registry.Get('__rgraph_event_listeners__')[obj.canvas.id + '_mousemove']) {
  326. return;
  327. }
  328. RGraph.Registry.Get('__rgraph_event_listeners__')[obj.canvas.id + '_mousemove'] = true;
  329. // When the canvas is cleared, reset this flag so that the event listener is installed again
  330. RGraph.AddCustomEventListener(obj, 'onclear', function (obj) {RGraph.Registry.Get('__rgraph_event_listeners__')[obj.canvas.id + '_mousemove'] = false})
  331. // Install the mousemove event handler for the tooltips
  332. //
  333. // NOTE: Global on purpose
  334. rgraph_canvas_mousemove_func = function (e)
  335. {
  336. var objects = RGraph.ObjectRegistry.getObjectsByCanvasID(e.target.id);
  337. for (var i=0; i<objects.length; ++i) {
  338. var shape = objects[i].getShape(e);
  339. if (shape && shape['object']) {
  340. /**
  341. * This allows the Scatter chart funky tooltips style
  342. *
  343. if (objects[i].type == 'scatter' && shape['dataset'] > 0) {
  344. for (var j=0; j<(objects[i].data.length - 1); ++j) {
  345. shape['index'] += objects[i].data[j].length;
  346. }
  347. }
  348. var text = RGraph.parseTooltipText(objects[i].Get('chart.tooltips'), shape['index']);
  349. if (text) {
  350. e.target.style.cursor = 'pointer';
  351. /**
  352. * This facilitates the event triggering the tooltips being mousemove
  353. *
  354. if ( typeof(objects[i].Get('chart.tooltips.event')) == 'string'
  355. && objects[i].Get('chart.tooltips.event') == 'onmousemove'
  356. && (!RGraph.Registry.Get('chart.tooltip') || shape['index'] != RGraph.Registry.Get('chart.tooltip').__index__ || shape['object'].uid != RGraph.Registry.Get('chart.tooltip').__object__.uid)
  357. ) {
  358. // Hide any current tooltip
  359. rgraph_window_mousedown(e);
  360. rgraph_canvas_mouseup_func(e);
  361. }
  362. }
  363. }
  364. }
  365. }
  366. obj.canvas.addEventListener('mousemove', rgraph_canvas_mousemove_func, false);
  367. RGraph.AddEventListener(obj.id, 'mousemove', rgraph_canvas_mousemove_func);
  368. }
  369. */
  370. /**
  371. * This (as the name suggests preloads any images it can find in the tooltip text
  372. *
  373. * @param object obj The chart object
  374. */
  375. RGraph.PreLoadTooltipImages = function (obj)
  376. {
  377. var tooltips = obj.Get('chart.tooltips');
  378. if (RGraph.hasTooltips(obj)) {
  379. if (obj.type == 'rscatter') {
  380. tooltips = [];
  381. for (var i=0; i<obj.data.length; ++i) {
  382. tooltips.push(obj.data[3]);
  383. }
  384. }
  385. for (var i=0; i<tooltips.length; ++i) {
  386. // Add the text to an offscreen DIV tag
  387. var div = document.createElement('DIV');
  388. div.style.position = 'absolute';
  389. div.style.opacity = 0;
  390. div.style.top = '-100px';
  391. div.style.left = '-100px';
  392. div.innerHTML = tooltips[i];
  393. document.body.appendChild(div);
  394. // Now get the IMG tags and create them
  395. var img_tags = div.getElementsByTagName('IMG');
  396. // Create the image in an off-screen image tag
  397. for (var j=0; j<img_tags.length; ++j) {
  398. if (img_tags && img_tags[i]) {
  399. var img = document.createElement('IMG');
  400. img.style.position = 'absolute';
  401. img.style.opacity = 0;
  402. img.style.top = '-100px';
  403. img.style.left = '-100px';
  404. img.src = img_tags[i].src
  405. document.body.appendChild(img);
  406. setTimeout(function () {document.body.removeChild(img);}, 250);
  407. }
  408. }
  409. // Now remove the div
  410. document.body.removeChild(div);
  411. }
  412. }
  413. }
  414. /**
  415. * This is the tooltips canvas onmousemove listener
  416. */
  417. RGraph.Tooltips_mousemove = function (obj, e)
  418. {
  419. var shape = obj.getShape(e);
  420. var changeCursor_tooltips = false
  421. if ( shape
  422. && typeof(shape['index']) == 'number'
  423. && obj.Get('chart.tooltips')[shape['index']]
  424. ) {
  425. var text = RGraph.parseTooltipText(obj.Get('chart.tooltips'), shape['index']);
  426. if (text) {
  427. /**
  428. * Change the cursor
  429. */
  430. changeCursor_tooltips = true;
  431. if (obj.Get('chart.tooltips.event') == 'onmousemove') {
  432. // Show the tooltip if it's not the same as the one already visible
  433. if (
  434. !RGraph.Registry.Get('chart.tooltip')
  435. || RGraph.Registry.Get('chart.tooltip').__object__.uid != obj.uid
  436. || RGraph.Registry.Get('chart.tooltip').__index__ != shape['index']
  437. ) {
  438. RGraph.HideTooltip();
  439. RGraph.Clear(obj.canvas);
  440. RGraph.Redraw();
  441. RGraph.Tooltip(obj, text, e.pageX, e.pageY, shape['index']);
  442. obj.Highlight(shape);
  443. }
  444. }
  445. }
  446. /**
  447. * More highlighting
  448. */
  449. } else if (shape && typeof(shape['index']) == 'number') {
  450. var text = RGraph.parseTooltipText(obj.Get('chart.tooltips'), shape['index']);
  451. if (text) {
  452. changeCursor_tooltips = true
  453. }
  454. }
  455. return changeCursor_tooltips;
  456. }