/** * o------------------------------------------------------------------------------o * | This file is part of the RGraph package - you can learn more at: | * | | * | http://www.rgraph.net | * | | * | This package is licensed under the RGraph license. For all kinds of business | * | purposes there is a small one-time licensing fee to pay and for non | * | commercial purposes it is free to use. You can read the full license here: | * | | * | http://www.rgraph.net/LICENSE.txt | * o------------------------------------------------------------------------------o */ if (typeof(RGraph) == 'undefined') RGraph = {}; /** * The chart constuctor * * @param object canvas * @param array data */ RGraph.RScatter = RGraph.Rscatter = function (id) { this.id = id; this.canvas = document.getElementById(typeof id === 'object' ? id.id : id); this.context = this.canvas.getContext('2d'); this.canvas.__object__ = this; this.type = 'rscatter'; this.hasTooltips = false; this.isRGraph = true; this.uid = RGraph.CreateUID(); this.canvas.uid = this.canvas.uid ? this.canvas.uid : RGraph.CreateUID(); this.colorsParsed = false; this.coordsText = []; /** * Compatibility with older browsers */ RGraph.OldBrowserCompat(this.context); this.centerx = 0; this.centery = 0; this.radius = 0; this.max = 0; this.properties = { 'chart.radius': null, 'chart.colors': [], // This is used internally for the key 'chart.colors.default': 'black', 'chart.gutter.left': 25, 'chart.gutter.right': 25, 'chart.gutter.top': 25, 'chart.gutter.bottom': 25, 'chart.title': '', 'chart.title.background': null, 'chart.title.hpos': null, 'chart.title.vpos': null, 'chart.title.bold': true, 'chart.title.font': null, 'chart.title.x': null, 'chart.title.y': null, 'chart.title.halign': null, 'chart.title.valign': null, 'chart.labels': null, 'chart.labels.position': 'center', 'chart.labels.axes': 'nsew', 'chart.text.color': 'black', 'chart.text.font': 'Arial', 'chart.text.size': 10, 'chart.key': null, 'chart.key.background': 'white', 'chart.key.position': 'graph', 'chart.key.halign': 'right', 'chart.key.shadow': false, 'chart.key.shadow.color': '#666', 'chart.key.shadow.blur': 3, 'chart.key.shadow.offsetx': 2, 'chart.key.shadow.offsety': 2, 'chart.key.position.gutter.boxed':false, 'chart.key.position.x': null, 'chart.key.position.y': null, 'chart.key.color.shape': 'square', 'chart.key.rounded': true, 'chart.key.linewidth': 1, 'chart.key.colors': null, 'chart.key.interactive': false, 'chart.key.interactive.highlight.chart.fill':'rgba(255,0,0,0.9)', 'chart.key.interactive.highlight.label':'rgba(255,0,0,0.2)', 'chart.key.text.color': 'black', 'chart.contextmenu': null, 'chart.tooltips': null, 'chart.tooltips.event': 'onmousemove', 'chart.tooltips.effect': 'fade', 'chart.tooltips.css.class': 'RGraph_tooltip', 'chart.tooltips.highlight': true, 'chart.tooltips.hotspot': 3, 'chart.tooltips.coords.page': false, 'chart.annotatable': false, 'chart.annotate.color': 'black', 'chart.zoom.factor': 1.5, 'chart.zoom.fade.in': true, 'chart.zoom.fade.out': true, 'chart.zoom.hdir': 'right', 'chart.zoom.vdir': 'down', 'chart.zoom.frames': 25, 'chart.zoom.delay': 16.666, 'chart.zoom.shadow': true, 'chart.zoom.background': true, 'chart.zoom.action': 'zoom', 'chart.resizable': false, 'chart.resize.handle.background': null, 'chart.ymax': null, 'chart.ymin': 0, 'chart.tickmarks': 'cross', 'chart.ticksize': 3, 'chart.scale.decimals': null, 'chart.scale.point': '.', 'chart.scale.thousand': ',', 'chart.scale.round': false, 'chart.units.pre': '', 'chart.units.post': '', 'chart.events.mousemove': null, 'chart.events.click': null, 'chart.highlight.stroke': 'transparent', 'chart.highlight.fill': 'rgba(255,255,255,0.7)', 'chart.highlight.point.radius': 3, 'chart.labels.count': 5 } this.data = []; // Handle multiple datasets being given as one argument if (arguments[1][0] && arguments[1][0][0] && typeof(arguments[1][0][0]) == 'object') { // Store the data set(s) for (var i=0; i 0 && prop['chart.key'].length >= 3) { this.centerx = this.centerx - prop['chart.gutter.right'] + 5; } /** * Populate the colors array for the purposes of generating the key */ if (typeof(prop['chart.key']) == 'object' && RG.is_array(prop['chart.key']) && prop['chart.key'][0]) { // Reset the colors array prop['chart.colors'] = []; for (var i=0; i -1) RG.Text2(this, {'tag': 'scale','font':font,'size':size,'x':centerx,'y':centery - (r * ((i+1) / len)),'text':this.scale2.labels[i],'valign':'center','halign':'center','bounding':true,'boundingFill':color}); if (axes.indexOf('s') > -1) RG.Text2(this, {'tag': 'scale','font':font,'size':size,'x':centerx,'y':centery + (r * ((i+1) / len)),'text':this.scale2.labels[i],'valign':'center','halign':'center','bounding':true,'boundingFill':color}); if (axes.indexOf('e') > -1) RG.Text2(this, {'tag': 'scale','font':font,'size':size,'x':centerx + (r * ((i+1) / len)),'y':centery,'text':this.scale2.labels[i],'valign':'center','halign':'center','bounding':true,'boundingFill':color}); if (axes.indexOf('w') > -1) RG.Text2(this, {'tag': 'scale','font':font,'size':size,'x':centerx - (r * ((i+1) / len)),'y':centery,'text':this.scale2.labels[i],'valign':'center','halign':'center','bounding':true,'boundingFill':color}); } // Draw the center minimum value (but only if there's at least one axes labels stipulated) if (prop['chart.labels.axes'].length > 0) { RG.Text2(this, {'font':font, 'size':size, 'x':centerx, 'y':centery, 'text':RG.number_format(this, Number(this.scale2.min).toFixed(this.scale2.decimals), this.scale2.units_pre, this.scale2.units_post), 'valign':'center', 'halign':'center', 'bounding':true, 'boundingFill':color, 'tag': 'scale' }); } /** * Draw the key */ if (key && key.length) { RG.DrawKey(this, key, prop['chart.colors']); } } /** * Draws the circular labels that go around the charts * * @param labels array The labels that go around the chart */ this.DrawCircularLabels = function (context, labels, font_face, font_size, r) { var position = prop['chart.labels.position']; var r = r + 10; for (var i=0; i (x - offset) && mouseY < (y + offset) && mouseY > (y - offset) ) { var tooltip = RG.parseTooltipText(prop['chart.tooltips'], i); return {0:this,1:x,2:y,3:i,'object':this, 'x':x, 'y':y, 'index':i, 'tooltip': tooltip}; } } } /** * This function facilitates the installation of tooltip event listeners if * tooltips are defined. */ this.AllowTooltips = function () { // Preload any tooltip images that are used in the tooltips RG.PreLoadTooltipImages(this); /** * This installs the window mousedown event listener that lears any * highlight that may be visible. */ RG.InstallWindowMousedownTooltipListener(this); /** * This installs the canvas mousemove event listener. This function * controls the pointer shape. */ RG.InstallCanvasMousemoveTooltipListener(this); /** * This installs the canvas mouseup event listener. This is the * function that actually shows the appropriate tooltip (if any). */ RG.InstallCanvasMouseupTooltipListener(this); } /** * Each object type has its own Highlight() function which highlights the appropriate shape * * @param object shape The shape to highlight */ this.Highlight = function (shape) { // Add the new highlight RG.Highlight.Point(this, shape); } /** * The getObjectByXY() worker method. Don't call this call: * * RGraph.ObjectRegistry.getObjectByXY(e) * * @param object e The event object */ this.getObjectByXY = function (e) { var mouseXY = RG.getMouseXY(e); if ( mouseXY[0] > (this.centerx - this.radius) && mouseXY[0] < (this.centerx + this.radius) && mouseXY[1] > (this.centery - this.radius) && mouseXY[1] < (this.centery + this.radius) ) { return this; } } /** * This function positions a tooltip when it is displayed * * @param obj object The chart object * @param int x The X coordinate specified for the tooltip * @param int y The Y coordinate specified for the tooltip * @param objec tooltip The tooltips DIV element */ this.positionTooltip = function (obj, x, y, tooltip, idx) { var coordX = obj.coords[tooltip.__index__][0]; var coordY = obj.coords[tooltip.__index__][1]; var canvasXY = RG.getCanvasXY(obj.canvas); var gutterLeft = obj.gutterLeft; var gutterTop = obj.gutterTop; var width = tooltip.offsetWidth; // Set the top position tooltip.style.left = 0; tooltip.style.top = parseInt(tooltip.style.top) - 7 + 'px'; // By default any overflow is hidden tooltip.style.overflow = ''; // The arrow var img = new Image(); img.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAFCAYAAACjKgd3AAAARUlEQVQYV2NkQAN79+797+RkhC4M5+/bd47B2dmZEVkBCgcmgcsgbAaA9GA1BCSBbhAuA/AagmwQPgMIGgIzCD0M0AMMAEFVIAa6UQgcAAAAAElFTkSuQmCC'; img.style.position = 'absolute'; img.id = '__rgraph_tooltip_pointer__'; img.style.top = (tooltip.offsetHeight - 2) + 'px'; tooltip.appendChild(img); // Reposition the tooltip if at the edges: // LEFT edge if ((canvasXY[0] + coordX - (width / 2)) < 10) { tooltip.style.left = (canvasXY[0] + coordX - (width * 0.1)) + 'px'; img.style.left = ((width * 0.1) - 8.5) + 'px'; // RIGHT edge } else if ((canvasXY[0] + coordX + (width / 2)) > document.body.offsetWidth) { tooltip.style.left = canvasXY[0] + coordX - (width * 0.9) + 'px'; img.style.left = ((width * 0.9) - 8.5) + 'px'; // Default positioning - CENTERED } else { tooltip.style.left = (canvasXY[0] + coordX - (width * 0.5)) + 'px'; img.style.left = ((width * 0.5) - 8.5) + 'px'; } } /** * This function returns the radius (ie the distance from the center) for a particular * value. * * @param number value The value you want the radius for */ this.getRadius = function (value) { if (value < 0 || value > this.max) { return null; } var r = (value / this.max) * this.radius; return r; } /** * This allows for easy specification of gradients */ this.parseColors = function () { // Go through the data for (var i=0; i