RGraph.common.tooltips.js 21 KB

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