// version: 2017-11-25 /** * o--------------------------------------------------------------------------------o * | This file is part of the RGraph package - you can learn more at: | * | | * | http://www.rgraph.net | * | | * | RGraph is licensed under the Open Source MIT license. That means that it's | * | totally free to use and there are no restrictions on what you can do with it! | * o--------------------------------------------------------------------------------o */ /** * Initialise the various objects */ RGraph = window.RGraph || {isRGraph: true}; // Module pattern (function (win, doc, undefined) { var RG = RGraph, ua = navigator.userAgent, ma = Math; /** * This is the window click event listener. It redraws all canvas tags on the page. */ RG.installWindowMousedownListener = RG.InstallWindowMousedownListener = function (obj) { if (!RG.window_mousedown_event_listener) { RG.window_mousedown_event_listener = function (e) { /** * For firefox add the window.event object */ if (navigator.userAgent.indexOf('Firefox') >= 0) win.event = e; e = RG.fixEventObject(e); if (RG.HideTooltip && RG.Registry.Get('chart.tooltip')) { RG.clear(RG.Registry.Get('chart.tooltip').__canvas__); RG.redraw(); RG.hideTooltip(); } }; win.addEventListener('mousedown', RG.window_mousedown_event_listener, false); } }; /** * This is the window click event listener. It redraws all canvas tags on the page. */ RG.installWindowMouseupListener = RG.InstallWindowMouseupListener = function (obj) { if (!RG.window_mouseup_event_listener) { RG.window_mouseup_event_listener = function (e) { /** * For firefox add the window.event object */ if (navigator.userAgent.indexOf('Firefox') >= 0) win.event = e; e = RG.fixEventObject(e); /** * Stop any annotating that may be going on */ if (RG.annotating_window_onmouseup) { RG.annotating_window_onmouseup(e); return; } /** * End adjusting */ if (RG.Registry.Get('chart.adjusting') || RG.Registry.Get('chart.adjusting.gantt')) { var obj = RG.Registry.Get('chart.adjusting'); // If it's a line chart update the data_arr variable if (obj && obj.type === 'line') { obj.data_arr = RG.arrayLinearize(obj.data); } RG.fireCustomEvent(RG.Registry.Get('chart.adjusting'), 'onadjustend'); } RG.Registry.set('chart.adjusting', null); RG.Registry.set('chart.adjusting.shape', null); RG.Registry.set('chart.adjusting.gantt', null); // ============================================== // Finally, redraw the chart // ============================================== var tags = document.getElementsByTagName('canvas'); for (var i=0; i= 0) window.event = e; e = RG.fixEventObject(e); // ************************************************************************* // Tooltips // ************************************************************************* // This causes things at the edge of the chart area - eg line chart hotspots - not to fire because the // cursor is out of the chart area var objects = RG.ObjectRegistry.getObjectsByXY(e); //var objects = RG.ObjectRegistry.getObjectsByCanvasID(e.target.id); if (objects) { for (var i=0,len=objects.length; i= 0) window.event = e; e = RG.fixEventObject(e); /** * Go through all the objects and check them to see if anything needs doing */ var objects = RG.OR.getObjectsByXY(e); // Necessary to track which objects have had the mouseover // triggered on them var uids = []; if (objects && objects.length > 0) { for (var i=0,len=objects.length; i= obj.coords.key[i][0] && mouseXY[0] <= (obj.coords.key[i][0] + obj.coords.key[i][2]) && mouseXY[1] >= obj.coords.key[i][1] && mouseXY[1] <= (obj.coords.key[i][1] + obj.coords.key[i][3]) ) { RG.Registry.set('key-element', obj.coords.key[i]); overkey = true; } if (!overkey) { RG.Registry.set('key-element', null); } } } // ================================================================================================ // // This facilitates the chart.events.mousemove option // ================================================================================================ // var func = obj.get('chart.events.mousemove'); if (!func && typeof obj.onmousemove == 'function') { var func = obj.onmousemove; } /** * */ if (shape) { var index = shape['object'].type == 'scatter' ? shape['index_adjusted'] : shape['index']; if (typeof(obj['$' + index]) == 'object' && typeof(obj['$' + index].onmousemove) == 'function') { var func2 = obj['$' + index].onmousemove; } } /** * This bit saves the current pointer style if there isn't one already saved */ if (shape && (typeof(func) == 'function' || typeof(func2) == 'function' || typeof obj.Get('link') === 'string')) { if (obj.Get('chart.events.mousemove.revertto') == null) { obj.Set('chart.events.mousemove.revertto', e.target.style.cursor); } if (typeof(func) == 'function') RGraph.custom_events_mousemove_pointer = func(e, shape); if (typeof(func2) == 'function') RGraph.custom_events_mousemove_pointer = RGraph.custom_events_mousemove_pointer || func2(e, shape); // Go through the RGraph.events array looking for more // event listeners if ( typeof RG.events === 'object' && typeof RG.events[obj.uid] === 'object') { for (i in RG.events[obj.uid]) { if ( typeof i === 'string' && typeof RG.events[obj.uid][i] === 'object' && RG.events[obj.uid][i][1] === 'onmousemove' && typeof RG.events[obj.uid][i][2] === 'function') { (RG.events[obj.uid][i][2])(obj); } } } //return; } else if (typeof(obj.Get('chart.events.mousemove.revertto')) == 'string') { RG.cursor.push('default'); obj.Set('chart.events.mousemove.revertto', null); } // ====================================================== // This bit of code facilitates the onmouseover event // ====================================================== var func = obj.properties['chart.events.mouseover']; if (!func && typeof obj.onmouseover === 'function') { func = obj.onmouseover; } // Allow for individually index functions to be specified if (shape) { var index = shape['object'].type == 'scatter' ? shape['index_adjusted'] : shape['index']; if (typeof(obj['$' + index]) == 'object' && typeof(obj['$' + index].onmouseover) == 'function') { var func2 = obj['$' + index].onmouseover; } } else { obj.__mouseover_shape_index__ = null; RG.__mouseover_objects__ = []; } if (typeof RG.__mouseover_objects__ === 'undefined') { RG.__mouseover_objects__ = []; } if (shape) { if ((obj.__mouseover_shape_index__ === shape.index) === false) { obj.__mouseover_shape_index__ = shape.index; RG.__mouseover_objects__.push(obj); if (func) func(e, shape); if (func2) func2(e, shape); // Go through the RGraph.events array looking for more // event listeners if ( typeof RG.events === 'object' && typeof RG.events[obj.uid] === 'object') { for (i in RG.events[obj.uid]) { if ( typeof i === 'string' && typeof RG.events[obj.uid][i] === 'object' && RG.events[obj.uid][i][1] === 'onmouseover' && typeof RG.events[obj.uid][i][2] === 'function') { (RG.events[obj.uid][i][2])(obj); } } } } } else { obj.__mouseover_shape_index__ = null; RG.__mouseover_objects__ = []; } // ================================================================================================ // // Tooltips // ================================================================================================ // var current_tooltip = RG.Registry.get('chart.tooltip'); var tooltips = obj.get('chart.tooltips'); var tooltips_event = obj.Get('chart.tooltips.event'); if ( shape && (tooltips && tooltips[shape['index']] || shape['tooltip']) && tooltips_event.indexOf('mousemove') !== -1 && ( RG.isNull(current_tooltip) // Is there a tooltip being shown? || obj.uid != current_tooltip.__object__.uid // Same object? || (current_tooltip.__index__ != shape['index']) // Same tooltip index? || (typeof shape['dataset'] === 'number' && shape['dataset'] != current_tooltip.__shape__['dataset']) ) ) { RG.clear(obj.canvas); RG.hideTooltip(); RG.redraw(); obj.canvas.rgraph_mouseup_event_listener(e); return; } // ================================================================================================ // // Adjusting // ================================================================================================ // if (obj && obj.get('chart.adjustable')) { obj.Adjusting_mousemove(e); } /** * This facilitates breaking out of the loop when a shape has been found - * ie the cursor is over a shape an upper chart */ if (shape || (obj.overChartArea && obj.overChartArea(e) )) { break; } } // // For all objects that are NOT mouseover'ed, reset the // mouseover flag back to null // var objects = RG.OR.getObjectsByCanvasID(e.target.id); for (var i=0; i= 0) window.event = e; e = RG.fixEventObject(e); /** * Annotating */ if (e.target.__object__ && e.target.__object__.get('chart.annotatable') && RG.annotating_canvas_onmousedown) { RG.annotating_canvas_onmousedown(e); return; } var obj = RG.ObjectRegistry.getObjectByXY(e); if (obj) { var id = obj.id; /************************************************************* * Handle adjusting for all object types *************************************************************/ if (obj && obj.isRGraph && obj.get('chart.adjustable')) { /** * Check the cursor is in the correct area */ var obj = RG.OR.getObjectByXY(e); if (obj && obj.isRGraph) { // If applicable, get the appropriate shape and store it in the registry switch (obj.type) { case 'bar': var shape = obj.getShapeByX(e); break; case 'gantt': var shape = obj.getShape(e); var data = typeof shape.subindex === 'number' ? obj.data[shape.index][shape.subindex] : obj.data[shape.index]; if (shape) { var mouseXY = RG.getMouseXY(e); RG.Registry.set('chart.adjusting.gantt', { index: shape.index, subindex: shape.subindex, object: obj, mousex: mouseXY[0], mousey: mouseXY[1], event: data, event_start: data[0], event_duration: data[1], mode: (mouseXY[0] > (shape['x'] + shape['width'] - 5) ? 'resize' : 'move'), shape: shape }); } break; case 'line': var shape = obj.getShape(e); break; case 'hbar': var shape = obj.getShapeByY(e); break; default: var shape = null; } // // Added 30/9/2016 // Now check the index in the chart.adjusting.limitto property // If that property is an object and the appropriate index is // truthy then allow adjusting, otherwise don't. // if ( RG.isNull(obj.properties['chart.adjustable.only']) || typeof obj.properties['chart.adjustable.only'] === 'undefined' || ( RG.isArray(obj.properties['chart.adjustable.only']) && obj.isAdjustable && obj.isAdjustable(shape) ) ) { RG.Registry.set('chart.adjusting.shape', shape); // Fire the onadjustbegin event RG.fireCustomEvent(obj, 'onadjustbegin'); RG.Registry.set('chart.adjusting', obj); // Liberally redraw the canvas RG.clear(obj.canvas); RG.redraw(); // Call the mousemove event listener so that the canvas // is adjusted even though the mouse isn't moved obj.canvas.rgraph_mousemove_event_listener(e); } } } RG.clear(obj.canvas); RG.redraw(); } }; obj.canvas.addEventListener('mousedown', obj.canvas.rgraph_mousedown_event_listener, false); } }; /** * This is the canvas click event listener. Used by the pseudo event listener * * @param object obj The chart object */ RG.installCanvasClickListener = RG.InstallCanvasClickListener = function (obj) { if (!obj.canvas.rgraph_click_event_listener) { obj.canvas.rgraph_click_event_listener = function (e) { /** * For firefox add the window.event object */ if (navigator.userAgent.indexOf('Firefox') >= 0) window.event = e; e = RG.fixEventObject(e); var objects = RG.ObjectRegistry.getObjectsByXY(e); for (var i=0,len=objects.length; i obj.coords.key[j][0] && mouseX < (obj.coords.key[j][0] + obj.coords.key[j][2]) && mouseY > obj.coords.key[j][1] && mouseY < (obj.coords.key[j][1] + obj.coords.key[j][3])) { var pointer = true; } } } } /** * It can be specified in the user mousemove event - remember it can now * be specified in THREE ways */ if (RGraph.custom_events_mousemove_pointer) { var pointer = true; RGraph.custom_events_mousemove_pointer = false; } /* var index = shape['object'].type == 'scatter' ? shape['index_adjusted'] : shape['index']; if (!RG.isNull(obj['$' + index]) && typeof(obj['$' + index].onmousemove) == 'function') { var str = (obj['$' + index].onmousemove).toString(); if (str.match(/pointer/) && str.match(/cursor/) && str.match(/style/)) { var pointer = true; } } } */ /** * Is the chart resizable? Go through all the objects again */ var objects = RG.OR.objects.byCanvasID; for (var i=0,len=objects.length; i (e.target.width - 32) && mouseY > (e.target.height - 16)) { pointer = true; } if (pointer) { e.target.style.cursor = 'pointer'; } else if (e.target.style.cursor == 'pointer') { e.target.style.cursor = 'default'; } else { e.target.style.cursor = null; } // ========================================================================= // Resize cursor - check mouseis in bottom left corner and if it is change it // ========================================================================= if (resizable && mouseX >= (e.target.width - 15) && mouseY >= (e.target.height - 15)) { e.target.style.cursor = 'move'; } else if (e.target.style.cursor === 'move') { e.target.style.cursor = 'default'; } // ========================================================================= // Interactive key // ========================================================================= if (typeof mouse_over_key == 'boolean' && mouse_over_key) { e.target.style.cursor = 'pointer'; } // ========================================================================= // Gantt chart adjusting // ========================================================================= //if (obj && obj.type == 'gantt' && obj.get('chart.adjustable')) { // if (obj.getShape && obj.getShape(e)) { // e.target.style.cursor = 'ew-resize'; // } else { // e.target.style.cursor = 'default'; // } //} else if (!obj || !obj.type) { // e.target.style.cursor = cursor; //} // ========================================================================= // Line chart adjusting // ========================================================================= if (obj && obj.type == 'line' && obj.get('chart.adjustable')) { if (obj.getShape) { var shape = obj.getShape(e); if (shape && obj.isAdjustable(shape)) { e.target.style.cursor = 'ns-resize'; } } else { e.target.style.cursor = 'default'; } } // ========================================================================= // Annotatable // ========================================================================= if (e.target.__object__ && e.target.__object__.get('chart.annotatable')) { e.target.style.cursor = 'crosshair'; } // ========================================================================= // Drawing API link // ========================================================================= if (obj && obj.type === 'drawing.text' && shape && typeof obj.get('link') === 'string') { e.target.style.cursor = 'pointer'; } }; /** * This function handles the tooltip text being a string, function * * @param mixed tooltip This could be a string or a function. If it's a function it's called and * the return value is used as the tooltip text * @param numbr idx The index of the tooltip. */ RG.parseTooltipText = function (tooltips, idx) { // No tooltips if (!tooltips) { return null; } // Get the tooltip text if (typeof tooltips == 'function') { var text = tooltips(idx); // A single tooltip. Only supported by the Scatter chart } else if (typeof tooltips == 'string') { var text = tooltips; } else if (typeof tooltips == 'object' && typeof tooltips[idx] == 'function') { var text = tooltips[idx](idx); } else if (typeof tooltips[idx] == 'string' && tooltips[idx]) { var text = tooltips[idx]; } else { var text = ''; } if (text == 'undefined') { text = ''; } else if (text == 'null') { text = ''; } // Conditional in case the tooltip file isn't included return RG.getTooltipTextFromDIV ? RG.getTooltipTextFromDIV(text) : text; }; /** * Draw crosshairs if enabled * * @param object obj The graph object (from which we can get the context and canvas as required) */ RG.drawCrosshairs = RG.DrawCrosshairs = function (e, obj) { var e = RG.fixEventObject(e), width = obj.canvas.width, height = obj.canvas.height, mouseXY = RG.getMouseXY(e), x = mouseXY[0], y = mouseXY[1], gutterLeft = obj.gutterLeft, gutterRight = obj.gutterRight, gutterTop = obj.gutterTop, gutterBottom = obj.gutterBottom, Mathround = Math.round, prop = obj.properties, co = obj.context, ca = obj.canvas RG.redrawCanvas(ca); if ( x >= gutterLeft && y >= gutterTop && x <= (width - gutterRight) && y <= (height - gutterBottom) ) { var linewidth = prop['chart.crosshairs.linewidth'] ? prop['chart.crosshairs.linewidth'] : 1; co.lineWidth = linewidth ? linewidth : 1; co.beginPath(); co.strokeStyle = prop['chart.crosshairs.color']; /** * The chart.crosshairs.snap option */ if (prop['chart.crosshairs.snap']) { // Linear search for the closest point var point = null; var dist = null; var len = null; if (obj.type == 'line') { for (var i=0; i ' + xCoord + '
' + prop['chart.crosshairs.coords.labels.y'] + ': ' + yCoord; obj.canvas.addEventListener('mouseout', RG.hideCrosshairCoords, false); ca.__crosshairs_labels__ = div; ca.__crosshairs_x__ = xCoord; ca.__crosshairs_y__ = yCoord; } else if (prop['chart.crosshairs.coords']) { alert('[RGRAPH] Showing crosshair coordinates is only supported on the Scatter chart'); } /** * Fire the oncrosshairs custom event */ RG.fireCustomEvent(obj, 'oncrosshairs'); } else { RG.hideCrosshairCoords(); } }; // // Adds a mousemove event listener that highlights a segment based on th // mousemove event. Used in the Rose and the RScatter charts // //@param int segments The number of segments to allow // RG.allowSegmentHighlight = function (opt) { var obj = opt.object, count = opt.count, fill = opt.fill, stroke = opt.stroke if (!RG.segmentHighlightFunction) { RG.segmentHighlightFunction = function (e) { var mouseXY = RG.getMouseXY(e); var angle = RG.getAngleByXY(obj.centerx, obj.centery, mouseXY[0], mouseXY[1]); angle += RG.HALFPI; if (angle > RG.TWOPI) { angle -= RG.TWOPI; } RG.redraw(); var start = 0; var end = 0; var a = (ma.PI * 2) / count; // // Radius // var r = obj.radius; (function () { for (i=0; i