Commits

Chris Leonello committed 79738c2

Added highlighting, left/right click to bars. Working out disabling mouse over in favor of click when click enabled.

Comments (0)

Files changed (4)

examples/barTest.html

+<!DOCTYPE html>
+
+<html lang="en">
+<head>
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+  <title>Simple Test</title>
+  <!--[if IE]><script language="javascript" type="text/javascript" src="../src/excanvas.js"></script><![endif]-->
+  
+  <link rel="stylesheet" type="text/css" href="../src/jquery.jqplot.css" />
+  <link rel="stylesheet" type="text/css" href="examples.css" />
+  
+  <!-- BEGIN: load jquery -->
+  <script language="javascript" type="text/javascript" src="../src/jquery-1.4.2.min.js"></script>
+  <!-- END: load jquery -->
+  
+  <link type="text/css" href="jquery-ui/css/ui-lightness/jquery-ui-1.8.1.custom.css" rel="Stylesheet" />	
+  <script type="text/javascript" src="jquery-ui/js/jquery-ui-1.8.1.custom.min.js"></script>
+
+  
+  
+  <!-- BEGIN: load jqplot -->
+  <script language="javascript" type="text/javascript" src="../src/jquery.jqplot.js"></script>
+  <script language="javascript" type="text/javascript" src="../src/plugins/jqplot.barRenderer.js"></script>
+  <script language="javascript" type="text/javascript" src="../src/plugins/jqplot.categoryAxisRenderer.js"></script>
+  <!-- END: load jqplot -->
+
+  <script type="text/javascript" src="barTest.js"></script>
+  <style type="text/css">
+    #code {
+        font: 10pt "Andale Mono", Monaco, "Courier New", sans-serif ;
+        white-space: pre;
+    }
+  </style>
+  
+    <script type="text/javascript">    
+    $(document).ready(function(){
+        $('#code').load('barTest.js');
+        $('#code').dialog({
+			height: 'auto',
+			width: 600,
+			modal: true,
+			autoOpen: false,
+			resizable: true,
+			draggable: true,
+			show: 'fold'
+		});
+
+    });
+    </script> 
+    
+  </head>
+  <body>
+<?php include "nav.inc"; ?>
+<div id="code"></div>
+    <p>To create bar plots, you have to include the "jqplot.barRenderer.js" file in your source.  You can view the javascript that generated these plots by clicking the "View Code" button below.</p>
+    <button id="seecode" onclick="$('#code').dialog('open');">View Code</button>
+
+    <p>Below is a default bar plot.  Bars will highlight on mouseover.  Events are triggered when you mouseover a bar and also when you click on a bar.  Here We capture the 'jqplotDataClick' event and display the clicked series index, point index and data values. When series data is assigned as a 1-dimensional array as in this example, jqPlot automatically converts it into a 2-dimensional array for plotting.  So a series defined as [2, 6, 7, 10] will become [[1,2], [2,6], [3,7], [4,10]].<p>
+    
+    <div><span>You Clicked: </span><span id="info1">Nothing yet</span></div>
+        
+    <div id="chart1" style="margin-top:20px; margin-left:20px; width:300px; height:300px;"></div>
+
+    <p>The plot target also fires a 'jqplotDataMouseOver' when the cursor is moused over a bar even if highlighting is turned off.  This event will fire continuously as the user mouses over the bar.  'jqplotDataHighlight' fires only once when the user first passes over the bar.  Additionally, a 'jqplotDataUnhighlight' event is fired when the user moves out of a bar (if highlighting is enabled).<p>
+
+    <div><span>Moused Over: </span><span id="info2">Nothing</span></div>
+    
+    <div id="chart2" style="margin-top:20px; margin-left:20px; width:300px; height:300px;"></div>
+    
+    <p>The next example has the plot's 'captureRightClick' option set to true.  This causes the plot to fire a 'jqplotRightClick' event the the user clicks the right mouse button over a bar.  Here, the 'highlightMouseDown' option is also set to true.  This will highlight a slice on mouse down instead of on move over.  Highlighting will occur for either left or right click.</p>
+
+    <div><span>You Right Clicked: </span><span id="info3">Nothing yet</span></div>
+    
+    <div id="chart3" style="margin-top:20px; margin-left:20px; width:300px; height:300px;"></div>
+        
+<p>A simple line chart is added to test for imcompatabilities.</p>
+        <div id="chart6" style="margin-top:20px; margin-left:20px; width:160px; height:100px;"></div>
+  </body>
+</html>
+$(document).ready(function(){
+
+    $.jqplot.config.enablePlugins = true;
+
+    s1 = [2, 6, 7, 10];
+    s2 = [7, 5, 3, 2];
+    s3 = [14, 9, 3, 8];
+    
+    ticks = ['a', 'b', 'c', 'd'];
+    
+    s5 = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1];
+    
+    plot1 = $.jqplot('chart1', [s1], {
+        seriesDefaults:{
+            renderer:$.jqplot.BarRenderer
+        },
+        axes: {
+            xaxis: {
+                renderer: $.jqplot.CategoryAxisRenderer,
+                ticks: ticks
+            }
+        }
+    });
+    
+    plot2 = $.jqplot('chart2', [s1, s2], {
+        seriesDefaults: {
+            renderer:$.jqplot.BarRenderer
+        },
+        axes: {
+            xaxis: {
+                renderer: $.jqplot.CategoryAxisRenderer,
+                ticks: ticks
+            }
+        }
+    });
+    
+    plot3 = $.jqplot('chart3', [s1, s2, s3], {
+        stackSeries: true,
+        captureRightClick: true,
+        seriesDefaults:{
+            renderer:$.jqplot.BarRenderer,
+            highlightMouseDown: true
+        },
+        legend: {
+            show: true,
+            location: 'e',
+            placement: 'outside'
+        }      
+    });
+
+    plot6 = $.jqplot('chart6', [[1,2,3,4]]);
+    
+    $('#chart1').bind('jqplotDataClick', 
+        function (ev, seriesIndex, pointIndex, data) {
+            $('#info1').html('series: '+seriesIndex+', point: '+pointIndex+', data: '+data);
+        }
+    );
+    
+    $('#chart2').bind('jqplotDataHighlight', 
+        function (ev, seriesIndex, pointIndex, data) {
+            $('#info2').html('series: '+seriesIndex+', point: '+pointIndex+', data: '+data);
+        }
+    );
+    
+    $('#chart2').bind('jqplotDataUnhighlight', 
+        function (ev) {
+            $('#info2').html('Nothing');
+        }
+    ); 
+    
+    $('#chart3').bind('jqplotDataRightClick', 
+        function (ev, seriesIndex, pointIndex, data) {
+            $('#info3').html('series: '+seriesIndex+', point: '+pointIndex+', data: '+data);
+        }
+    );  
+    
+    $(document).unload(function() {$('*').unbind(); });
+});
         function checkIntersection(gridpos, plot) {
             var series = plot.series;
             var i, j, k, s, r, x, y, theta, sm, sa, minang, maxang;
-            var d0, d;
+            var d0, d, p, pp, points, bw;
             var threshold, t;
             for (k=plot.seriesStack.length-1; k>=0; k--) {
                 i = plot.seriesStack[k];
                 s = series[i];
                 switch (s.renderer.constructor) {
+                    case $.jqplot.BarRenderer:
+                        switch (s.barDirection) {
+                            case 'vertical':
+                                // is it a single series or a stacked series
+                                if (true) {
+                                    x = gridpos.x;
+                                    y = gridpos.y;
+                                    bw = s.barWidth/2;
+                                    for (j=s.gridData.length-1; j>=0; j--) {
+                                        // p = s.gridData[j];
+                                        // pp = s._prevGridData[j] || [p[0], s.canvas._ctx.canvas.height];
+                                        // points = [[p[0]-bw, p[1]], [p[0]+bw, p[1]], [p[0]+bw, pp[1]], [p[0]-bw, pp[1]]];
+                                        // points = [p[0]-bw, p[1], bw, pp[1]-p[1]];
+                                        points = s._barPoints[j];
+                                        // if (x<p[0]+bw && x>p[0]-bw && y<pp[1] && y>p[1]) {
+                                        if (x>points[0][0] && x<points[2][0] && y>points[2][1] && y<points[0][1]) {
+                                            return {seriesIndex:s.index, pointIndex:j, gridData:p, data:s.data[j], points:s._barPoints[j]};
+                                        }
+                                    }
+                                }
+                                // else side by side series
+                                else {
+                                    
+                                }
+                                break;
+                                
+                            case 'horizontal':
+                                //
+                                break;
+                        }
+                        break;
+                    
                     case $.jqplot.DonutRenderer:
                         sa = s.startAngle/180*Math.PI;
                         x = gridpos.x - s._center[0];

src/plugins/jqplot.barRenderer.js

         // prop: varyBarColor
         // true to color each bar separately.
         this.varyBarColor = false;
+        // prop: highlightMouseOver
+        // True to highlight slice when moused over.
+        // This must be false to enable highlightMouseDown to highlight when clicking on a slice.
+        this.highlightMouseOver = true;
+        // prop: highlightMouseDown
+        // True to highlight when a mouse button is pressed over a slice.
+        // This will be disabled if highlightMouseOver is true.
+        this.highlightMouseDown = false;
+        // prop: highlightColors
+        // an array of colors to use when highlighting a slice.
+        this.highlightColors = [];
+        
+        // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver
+        if (options.highlightMouseDown && options.highlightMouseOver == null) {
+            options.highlightMouseOver = false;
+        }
+        
         $.extend(true, this, options);
         // fill is still needed to properly draw the legend.
         // bars have to be filled.
             this._stackAxis = 'x';
             this.fillAxis = 'x';
         }
+        // index of the currenty highlighted point, if any
+        this._highlightedPoint = null;
+        // total number of values for all bar series, total number of bar series, and position of this series
+        this._plotSeriesInfo = null;
+        // Array of actual data colors used for each data point.
+        this._dataColors = [];
+        this._barPoints = [];
+        
         // set the shape renderer options
         var opts = {lineJoin:'miter', lineCap:'round', fill:true, isarc:false, strokeStyle:this.color, fillStyle:this.color, closePath:this.fill};
         this.renderer.shapeRenderer.init(opts);
                 nseries += 1;
             }
         }
+        // return total number of values for all bar series, total number of bar series, and position of this series
         return [nvals, nseries, pos];
     };
 
         var nseries = 0;
         var paxis = this[this._primaryAxis];
         var s, series, pos;
-        var temp = this.renderer.calcSeriesNumbers.call(this);
+        var temp = this._plotSeriesInfo = this.renderer.calcSeriesNumbers.call(this);
         nvals = temp[0];
         nseries = temp[1];
         var nticks = paxis.numberTicks;
         return [nvals, nseries];
     };
     
+
+    function computeHighlightColors (colors) {
+        var ret = [];
+        for (var i=0; i<colors.length; i++){
+            var rgba = $.jqplot.getColorComponents(colors[i]);
+            var newrgb = [rgba[0], rgba[1], rgba[2]];
+            var sum = newrgb[0] + newrgb[1] + newrgb[2];
+            for (var j=0; j<3; j++) {
+                // when darkening, lowest color component can be is 60.
+                newrgb[j] = (sum > 570) ?  newrgb[j] * 0.8 : newrgb[j] + 0.3 * (255 - newrgb[j]);
+                newrgb[j] = parseInt(newrgb[j], 10);
+            }
+            ret.push('rgb('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+')');
+        }
+        return ret;
+    }
+    
     $.jqplot.BarRenderer.prototype.draw = function(ctx, gridData, options) {
         var i;
         var opts = (options != undefined) ? options : {};
             this.renderer.setBarWidth.call(this);
         }
         
-        var temp = this.renderer.calcSeriesNumbers.call(this);
+        var temp = this._plotSeriesInfo = this.renderer.calcSeriesNumbers.call(this);
         nvals = temp[0];
         nseries = temp[1];
         pos = temp[2];
                     points.push([base-this.barWidth/2, gridData[i][1]]);
                     points.push([base+this.barWidth/2, gridData[i][1]]);
                     points.push([base+this.barWidth/2, ystart]);
+                    this._barPoints.push(points);
                     // now draw the shadows if not stacked.
                     // for stacked plots, they are predrawn by drawShadow
                     if (shadow && !this._stack) {
                         delete sopts.fillStyle;
                         this.renderer.shadowRenderer.draw(ctx, points, sopts);
                     }
+                    var clr = opts.fillStyle || this.color;
+                    this._dataColors.push(clr);
                     this.renderer.shapeRenderer.draw(ctx, points, opts); 
                 }
             }
                     points.push([gridData[i][0], base+this.barWidth/2]);
                     points.push([gridData[i][0], base-this.barWidth/2]);
                     points.push([xstart, base-this.barWidth/2]);
+                    this._barPoints.push(points);
                     // now draw the shadows if not stacked.
                     // for stacked plots, they are predrawn by drawShadow
                     if (shadow && !this._stack) {
                         delete sopts.fillStyle;
                         this.renderer.shadowRenderer.draw(ctx, points, sopts);
                     }
+                    var clr = opts.fillStyle || this.color;
+                    this._dataColors.push(clr);
                     this.renderer.shapeRenderer.draw(ctx, points, opts); 
                 }  
             }
         }                
-
+        
+        if (this.highlightColors.length == 0) {
+            this.highlightColors = computeHighlightColors(this._dataColors);
+        }
     };
     
      
                 this.renderer.setBarWidth.call(this);
             }
         
-            var temp = this.renderer.calcSeriesNumbers.call(this);
+            var temp = this._plotSeriesInfo = this.renderer.calcSeriesNumbers.call(this);
             nvals = temp[0];
             nseries = temp[1];
             pos = temp[2];
             }   
             
         }
-                     
-
     };
+    
+    function postInit(target, data, options) {
+        for (i=0; i<this.series.length; i++) {
+            if (this.series[i].renderer.constructor == $.jqplot.BarRenderer) {
+                // don't allow mouseover and mousedown at same time.
+                if (this.series[i].highlightMouseOver) {
+                    this.series[i].highlightMouseDown = false;
+                }
+            }
+        }
+        this.target.bind('mouseout', {plot:this}, function (ev) { unhighlight(ev.data.plot); });
+    }
+    
+    // called within context of plot
+    // create a canvas which we can draw on.
+    // insert it before the eventCanvas, so eventCanvas will still capture events.
+    function postPlotDraw() {
+        this.plugins.barRenderer = {highlightedSeriesIndex:null};
+        this.plugins.barRenderer.highlightCanvas = new $.jqplot.GenericCanvas();
+        
+        this.eventCanvas._elem.before(this.plugins.barRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-barRenderer-highlight-canvas', this._plotDimensions));
+        var hctx = this.plugins.barRenderer.highlightCanvas.setContext();
+    }   
+    
+    $.jqplot.BarRenderer.prototype.highlightBar = function(ctx, points, options) {
+        
+    }
+    
+    function highlight (plot, sidx, pidx, points) {
+        var s = plot.series[sidx];
+        var canvas = plot.plugins.barRenderer.highlightCanvas;
+        canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height);
+        s._highlightedPoint = pidx;
+        plot.plugins.barRenderer.highlightedSeriesIndex = sidx;
+        var opts = {fillStyle: s.highlightColors[pidx]};
+        s.renderer.shapeRenderer.draw(canvas._ctx, points, opts);
+    }
+    
+    function unhighlight (plot) {
+        var canvas = plot.plugins.barRenderer.highlightCanvas;
+        canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height);
+        for (var i=0; i<plot.series.length; i++) {
+            plot.series[i]._highlightedPoint = null;
+        }
+        plot.plugins.barRenderer.highlightedSeriesIndex = null;
+        plot.target.trigger('jqplotDataUnhighlight');
+    }
+    
+    
+    function handleMove(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
+            plot.target.trigger('jqplotDataMouseOver', ins);
+            if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.barRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) {
+                plot.target.trigger('jqplotDataHighlight', ins);
+                highlight (plot, neighbor.seriesIndex, neighbor.pointIndex, neighbor.points);
+            }
+        }
+        else if (neighbor == null) {
+            unhighlight (plot);
+        }
+    }
+    
+    function handleMouseDown(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
+            if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.barRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) {
+                plot.target.trigger('jqplotDataHighlight', ins);
+                highlight (plot, neighbor.seriesIndex, neighbor.pointIndex, neighbor.points);
+            }
+        }
+        else if (neighbor == null) {
+            unhighlight (plot);
+        }
+    }
+    
+    function handleMouseUp(ev, gridpos, datapos, neighbor, plot) {
+        var idx = plot.plugins.barRenderer.highlightedSeriesIndex;
+        if (idx != null && plot.series[idx].highlightMouseDown) {
+            unhighlight(plot);
+        }
+    }
+    
+    function handleClick(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
+            plot.target.trigger('jqplotDataClick', ins);
+        }
+    }
+    
+    function handleRightClick(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
+            var idx = plot.plugins.barRenderer.highlightedSeriesIndex;
+            if (idx != null && plot.series[idx].highlightMouseDown) {
+                unhighlight(plot);
+            }
+            plot.target.trigger('jqplotDataRightClick', ins);
+        }
+    }
+    
+    $.jqplot.postInitHooks.push(postInit);
+    $.jqplot.postDrawHooks.push(postPlotDraw);
+    $.jqplot.eventListenerHooks.push(['jqplotMouseMove', handleMove]);
+    $.jqplot.eventListenerHooks.push(['jqplotMouseDown', handleMouseDown]);
+    $.jqplot.eventListenerHooks.push(['jqplotMouseUp', handleMouseUp]);
+    $.jqplot.eventListenerHooks.push(['jqplotClick', handleClick]);
+    $.jqplot.eventListenerHooks.push(['jqplotRightClick', handleRightClick]); 
+    
+    
 })(jQuery);