Commits

Chris Leonello committed df0e4d9

Added tick interval support to axes.
Added user option to change default grid padding.
Added unit tests.

Comments (0)

Files changed (6)

jqplot.categoryAxisRenderer.js

             else {
                 dim = this._plotDimensions.height;
             }
+            
+            // if min, max and number of ticks specified, user can't specify interval.
+            if (this.min != null && this.max != null && this.numberTicks != null) {
+                this.tickInterval = null;
+            }
+            
+            // if max, min, and interval specified and interval won't fit, ignore interval.
+            if (this.min != null && this.max != null && this.tickInterval != null) {
+                if (parseInt((this.max-this.min)/this.tickInterval) != (this.max-this.min)/this.tickInterval) {
+                    this.tickInterval = null;
+                }
+            }
         
             // find out how many categories are in the lines and collect labels
             var labels = [];
             var skip = parseInt(numcats/maxVisibleTicks);
 
 
-            this.tickInterval = range / (this.numberTicks-1);
+            if (this.tickInterval == null) this.tickInterval = range / (this.numberTicks-1);
+            // if tickInterval is specified, we will ignore any computed maximum.
             for (var i=0; i<this.numberTicks; i++){
-                tt = this.min + i * range / (this.numberTicks-1);
+                tt = this.min + i * this.tickInterval;
                 var t = new this.tickRenderer(this.tickOptions);
                 // if even tick, it isn't a category, it's a divider
                 if (i/2 == parseInt(i/2)) {

jqplot.dateAxisRenderer.js

     
     $.jqplot.DateAxisRenderer.prototype.init = function(options){
         this.tickOptions.formatter = $.jqplot.DateTickFormatter;
+        this._tickInterval = null;
         $.extend(true, this, options);
         var db = this._dataBounds;
         // Go through all the series attached to this axis and find
             this.numberTicks = userTicks.length;
             this.min = this._ticks[0].value;
             this.max = this._ticks[this.numberTicks-1].value;
-            this.tickInterval = (this.max - this.min) / (this.numberTicks - 1);
+            this._tickInterval = [(this.max - this.min) / (this.numberTicks - 1)/1000, 'seconds'];
         }
         
         // we don't have any ticks yet, let's make some!
             else {
                 dim = this._plotDimensions.height;
             }
+            
+            // if min, max and number of ticks specified, user can't specify interval.
+            if (this.min != null && this.max != null && this.numberTicks != null) {
+                this.tickInterval = null;
+            }
+            
+            // if user specified a tick interval, convert to usable.
+            if (this.tickInterval != null)
+            {
+                // if interval is a number or can be converted to one, use it.
+                // Assume it is in SECONDS!!!
+                if (Number(this.tickInterval)) {
+                    this._tickInterval = [Number(this.tickInterval), 'seconds'];
+                }
+                // else, parse out something we can build from.
+                else if (typeof this.tickInterval == "string") {
+                    var parts = this.tickInterval.split(' ');
+                    if (parts.length == 1) {
+                        this._tickInterval = [1, parts[0]];
+                    }
+                    else if (parts.length == 2) {
+                        this._tickInterval = [parts[0], parts[1]]
+                    }
+                }
+            }
         
             min = ((this.min != null) ? Date.create(this.min).getTime() : db.min);
             max = ((this.max != null) ? Date.create(this.max).getTime() : db.max);
             range = this.max - this.min;
     
             if (this.numberTicks == null){
-                if (dim > 100) {
+                // if tickInterval is specified by user, we will ignore computed maximum.
+                // max will be equal or greater to fit even # of ticks.
+                if (this._tickInterval != null) {
+                    var nc = Date.create(this.max).diff(this.min, this._tickInterval[1], true);
+                    this.numberTicks = Math.ceil(nc/this._tickInterval[0]) +1;
+                    //log(this._tickInterval, nc, this.numberTicks);
+                    this.max = Date.create(this.min).add(this.numberTicks-1, this._tickInterval[1]).getTime();
+                }
+                else if (dim > 100) {
                     this.numberTicks = parseInt(3+(dim-100)/75);
                 }
                 else this.numberTicks = 2;
             }
     
-            this.tickInterval = range / (this.numberTicks-1);
+            if (this._tickInterval == null) this._tickInterval = [range / (this.numberTicks-1)/1000, 'seconds'];
             for (var i=0; i<this.numberTicks; i++){
-                tt = this.min + i * range / (this.numberTicks-1);
+                var min = Date.create(this.min);
+                tt = min.add(i*this._tickInterval[0], this._tickInterval[1]).getTime();
                 var t = new this.tickRenderer(this.tickOptions);
                 // var t = new $.jqplot.AxisTickRenderer(this.tickOptions);
                 if (!this.showTicks) {
      *
      * @author Ken Snyder (ken d snyder at gmail dot com)
      * @date 2008-09-10
-     * @version 2.0.2 (http://kendsnyder.com/sandbox/date/)
-     * @license Creative Commons Attribution License 3.0 (http://creativecommons.org/licenses/by/3.0/)
+     * @version 2.0.2 (
+http://kendsnyder.com/sandbox/date/)     * @license Creative Commons Attribution License 3.0 (http://creativecommons.org/licenses/by/3.0/)
      *
      * @contributions Chris Leonello
      * @comment Bug fix to 12 hour time and additions to handle milliseconds and 

jqplot.linearAxisRenderer.js

             else {
                 dim = this._plotDimensions.height;
             }
+            
+            // if min, max and number of ticks specified, user can't specify interval.
+            if (this.min != null && this.max != null && this.numberTicks != null) {
+                this.tickInterval = null;
+            }
+            
+            // if max, min, and interval specified and interval won't fit, ignore interval.
+            if (this.min != null && this.max != null && this.tickInterval != null) {
+                if (parseInt((this.max-this.min)/this.tickInterval) != (this.max-this.min)/this.tickInterval) {
+                    this.tickInterval = null;
+                }
+            }
         
             min = ((this.min != null) ? this.min : db.min);
             max = ((this.max != null) ? this.max : db.max);
             range = this.max - this.min;
     
             if (this.numberTicks == null){
+                // if tickInterval is specified by user, we will ignore computed maximum.
+                // max will be equal or greater to fit even # of ticks.
+                if (this.tickInterval != null) {
+                    this.numberTicks = Math.ceil((this.max - this.min)/this.tickInterval);
+                    this.max = this.min + this.tickInterval*this.numberTicks;
+                }
                 if (dim > 100) {
                     this.numberTicks = parseInt(3+(dim-100)/75);
                 }
                 else this.numberTicks = 2;
             }
-    
-            this.tickInterval = range / (this.numberTicks-1);
+            
+            if (this.tickInterval == null) this.tickInterval = range / (this.numberTicks-1);
             for (var i=0; i<this.numberTicks; i++){
-                tt = this.min + i * range / (this.numberTicks-1);
+                tt = this.min + i * this.tickInterval;
                 var t = new this.tickRenderer(this.tickOptions);
                 // var t = new $.jqplot.AxisTickRenderer(this.tickOptions);
                 if (!this.showTicks) {
             axesDefaults: {},
             axes: {xaxis:{}, yaxis:[], x2axis:{}, y2axis:{}},
             seriesDefaults: {},
+            gridPadding: {top:10, right:10, bottom:10, left:10},
             series:[]
         };
         // prop: series
         this._width = null;
         this._height = null; 
         this._plotDimensions = {height:null, width:null};
-        // default padding for the grid context
-        this._gridOffsets = {top:10, right:10, bottom:10, left:10};
+        // prop: _gridPadding
+        // default padding around the grid element.  Overriden if there is
+        // an element (title, axis) beside that edge of the grid.
+        this._gridPadding = {top:10, right:10, bottom:10, left:10};
         // prop: equalXTicks
         // for dual axes, wether to space ticks the same on both sides.
         this.equalXTicks = true;
     
         this.parseOptions = function(options){
             this.options = $.extend(true, {}, this.defaults, options);
+            this._gridPadding = this.options.gridPadding;
             for (var n in this.axes) {
                 var axis = this.axes[n];
                 $.extend(true, axis, this.options.axesDefaults, this.options.axes[n]);
                 this.target.append(this.axes[name].draw());
                 this.axes[name].set();
             }
-            if (this.axes.yaxis.show) this._gridOffsets.left = this.axes.yaxis.getWidth();
-            if (this.axes.y2axis.show) this._gridOffsets.right = this.axes.y2axis.getWidth();
-            if (this.title.show && this.axes.x2axis.show) this._gridOffsets.top = this.title.getHeight() + this.axes.x2axis.getHeight();
-            else if (this.title.show) this._gridOffsets.top = this.title.getHeight();
-            else if (this.axes.x2axis.show) this._gridOffsets.top = this.axes.x2axis.getHeight();
-            if (this.axes.xaxis.show) this._gridOffsets.bottom = this.axes.xaxis.getHeight();
+            if (this.axes.yaxis.show) this._gridPadding.left = this.axes.yaxis.getWidth();
+            if (this.axes.y2axis.show) this._gridPadding.right = this.axes.y2axis.getWidth();
+            if (this.title.show && this.axes.x2axis.show) this._gridPadding.top = this.title.getHeight() + this.axes.x2axis.getHeight();
+            else if (this.title.show) this._gridPadding.top = this.title.getHeight();
+            else if (this.axes.x2axis.show) this._gridPadding.top = this.axes.x2axis.getHeight();
+            if (this.axes.xaxis.show) this._gridPadding.bottom = this.axes.xaxis.getHeight();
             
-            this.axes.yaxis.pack({position:'absolute', top:0, left:0, height:this._height}, {min:this._height - this._gridOffsets.bottom, max: this._gridOffsets.top});
-            this.axes.x2axis.pack({position:'absolute', top:this.title.getHeight(), left:0, width:this._width}, {min:this._gridOffsets.left, max:this._width - this._gridOffsets.right});
-            this.axes.xaxis.pack({position:'absolute', bottom:0, left:0, width:this._width}, {min:this._gridOffsets.left, max:this._width - this._gridOffsets.right});
-            this.axes.y2axis.pack({position:'absolute', top:0, right:0}, {min:this._height - this._gridOffsets.bottom, max: this._gridOffsets.top});
+            this.axes.yaxis.pack({position:'absolute', top:0, left:0, height:this._height}, {min:this._height - this._gridPadding.bottom, max: this._gridPadding.top});
+            this.axes.x2axis.pack({position:'absolute', top:this.title.getHeight(), left:0, width:this._width}, {min:this._gridPadding.left, max:this._width - this._gridPadding.right});
+            this.axes.xaxis.pack({position:'absolute', bottom:0, left:0, width:this._width}, {min:this._gridPadding.left, max:this._width - this._gridPadding.right});
+            this.axes.y2axis.pack({position:'absolute', top:0, right:0}, {min:this._height - this._gridPadding.bottom, max: this._gridPadding.top});
             
-            this.target.append(this.grid.createElement(this._gridOffsets));
+            this.target.append(this.grid.createElement(this._gridPadding));
             this.grid.draw();
-            this.target.append(this.seriesCanvas.createElement(this._gridOffsets));
+            this.target.append(this.seriesCanvas.createElement(this._gridPadding));
             var sctx = this.seriesCanvas.setContext();
             
             this.drawSeries(sctx);
 
             // finally, draw and pack the legend
             this.target.append(this.legend.draw());
-            this.legend.pack(this._gridOffsets);
+            this.legend.pack(this._gridPadding);
 
             // for (var i=0; i<$.jqplot.postDrawHooks.length; i++) {
             //     $.jqplot.postDrawHooks[i].call(this);
       line2=[25, 17.5, 12.25, 8.6, 6.0, 4.2, 2.9]; \
       line3=[4, 25, 13, 22, 14, 17, 15]; \
       plot2 = $.jqplot('_target_', [line1, line2, line3], \
-      {legend:{show:true}, title:'Mixed Data Lines', \
+      {legend:{show:true}, title:'Mixed Data Input Formats', \
       series:[{label:'Rising line', showLine:false, markerOptions:{style:'square'}}, \
       {label:'Declining line'}, {label:'Zig Zag Line', lineWidth:5, showMarker:false}]});";
             
       
       o = "line1=[['2008-09-30', 4], ['2008-10-30', 6.5], ['2008-11-30', 5.7], ['2008-12-30', 9], ['2009-01-30', 8.2]]; \
       plot9 = $.jqplot('_target_', [line1], \
-      {title:'Default Date X Axis', \
-      axes:{xaxis:{renderer:$.jqplot.DateAxisRenderer, min:'2008-08-30', max:'2009-02-28'}}, \
+      {title:'Default Date Axis', \
+      axes:{xaxis:{renderer:$.jqplot.DateAxisRenderer}}, \
       series:[{lineWidth:4, markerOptions:{style:'square'}}]});";
       
       genplot(o);
       
+      o = "line1=[['2008-06-30', 4], ['2008-7-30', 6.5], ['2008-8-30', 5.7], ['2008-9-30', 9], ['2008-10-30', 8.2]]; \
+      plot10 = $.jqplot('_target_', [line1], \
+      {title:'Customized Date Axis', gridPadding:{right:45}, \
+      axes:{xaxis:{renderer:$.jqplot.DateAxisRenderer, tickOptions:{formatString:'%b %#d, %y'}, min:'May 30, 2008', tickInterval:'1 month'}}, \
+      series:[{lineWidth:4, markerOptions:{style:'square'}}]});";
+      
+      genplot(o);
+  
       prettyPrint();
       
       JSpec.options.profile = false;
         end
     end
     
-    describe 'Category Axes'
+    describe 'Category Axis Renderer Plugin'
         it plot6.title.text
             plot6.axes.xaxis.should.have_property '_elem'
             plot1.seriesCanvas.should.have_property '_elem'
         end
     end
 
-    describe 'Date Axes'
+    describe 'Date Axis Renderer Plugin'
         it plot9.title.text
             plot9.axes.xaxis.should.have_property '_elem'
-            plot1.seriesCanvas.should.have_property '_elem'
+            plot9.seriesCanvas.should.have_property '_elem'
             plot9.axes.xaxis.renderer.constructor.should.be $.jqplot.DateAxisRenderer
         end
+        it plot10.title.text
+            plot10.axes.xaxis.should.have_property '_elem'
+            plot10.seriesCanvas.should.have_property '_elem'
+            plot10.axes.xaxis.renderer.constructor.should.be $.jqplot.DateAxisRenderer
+            plot10.axes.xaxis.min.should.be Date.create('May 20, 2008').getTime()
+            plot10.axes.xaxis.tickInterval.should.be [1, 'month']
+            plot10.axes.xaxis.numberTicks.should.be 7
+        end
     end
 
 end