Commits

Chris Leonello committed 44b6fb7

Heavy tear up and reorg.
Break out linear and category axis renderers into their own plugins.
Break out ticks into own plugin.
Give axis reference back to series attached to it. This should make it much easier to draw the axes.
This revision is probably broken.

  • Participants
  • Parent commits 9ba587a

Comments (0)

Files changed (8)

jqplot.AxisRenderers.js

+(function($) {
+    var debug = 1;
+        
+	// Convienence function that won't hang IE.
+	function log() {
+	    if (window.console && debug) {
+	       if (arguments.length == 1) console.log (arguments[0]);
+	       else console.log(arguments);
+	    }
+	};
+	
+    $.jqplot.linearAxisRenderer = function() {
+    };
+    
+    $.jqplot.linearAxisRenderer.prototype.init = function(options){
+        $.extend(true, this, options);
+    };
+    
+    // function: draw
+    // Creates the axis container DOM element and tick DOM elements.
+    // Populates some properties of the elements and figures out
+    // height and width of element.
+    // called with scope of axis
+    $.jqplot.linearAxisRenderer.prototype.draw = function(target, plotHeight, plotWidth) {
+        var axis = this;
+        if (axis.show) {
+            // populate the axis label and value properties.
+            axis.renderer.setAxis.call(axis, plotHeight, plotWidth);
+            // fill a div with axes labels in the right direction.
+            // Need to pregenerate each axis to get it's bounds and
+            // position it and the labels correctly on the plot.
+            var h, w;
+            
+            axis._elem = $('<div class="jqplot-axis"></div>').appendTo(target).get(0);
+            //for (var s in axis.style) $(axis._elem).css(s, axis.style[s]);
+    
+            if (axis.ticks.showLabels) {
+                var t = axis._ticks;
+                for (var i=0; i<t.length; i++) {
+                    var tick = t[i];
+                    if (!tick.isMinorTick || axis.showMinorTicks) {
+                        var elem = tick.draw();
+                        //var elem = $(frag).appendTo(axis._elem).get(0);
+                        $(elem).appendTo(axis._elem);
+                        for (var s in tick._styles) {
+                            $(elem).css(s, tick._styles[s]);   
+                        }
+                        $(elem).html(tick.label);
+                    
+                        if (axis.ticks.fontFamily) elem.style.fontFamily = axis.ticks.fontFamily;
+                        if (axis.ticks.fontSize) elem.style.fontSize = axis.ticks.fontSize;
+                    
+                        h = $(elem).outerHeight(true);
+                        w = $(elem).outerWidth(true);
+                    
+                        if (axis._height < h) {
+                            axis._height = h;
+                        }
+                        if (axis._width < w) {
+                            axis._width = w;
+                        }
+                    }
+                }
+            }
+        }
+    };
+    
+    // function: setAxis
+    // called with scope of an axis
+    // Populate the axis properties, giving a label and value
+    // (corresponding to the user data coordinates, not plot coords.)
+    // for each tick on the axis.
+    $.jqplot.linearAxisRenderer.prototype.setAxis = function(plotHeight, plotWidth) {
+        // we're are operating on an axis here
+        var axis = this;
+        var ticks = axis._ticks;
+        var name = axis.name;
+        var db = axis._dataBounds;
+        var dim, interval;
+        var min, max;
+        var pos1, pos2;
+        var tt, i;
+        
+        axis._canvasHeight = plotHeight;
+        axis._canvasWidth = plotWidth;
+        
+        // if we already have ticks, use them.
+        // ticks must be in order of increasing value.
+        if (ticks.length) {
+            for (i=0; i<ticks.length; i++){
+                var t = ticks[i];
+                if (!t.label) t.label = t.value.toString();
+                // set iitial css positioning styles for the ticks.
+                var pox = i*15+'px';
+                switch (name) {
+                    case 'xaxis':
+                        t._styles = {position:'absolute', top:'0px', left:pox, paddingTop:'10px'};
+                        break;
+                    case 'x2axis':
+                        t._styles = {position:'absolute', bottom:'0px', left:pox, paddingBottom:'10px'};
+                        break;
+                    case 'yaxis':
+                        t._styles = {position:'absolute', right:'0px', top:pox, paddingRight:'10px'};
+                        break;
+                    case 'y2axis':
+                        t._styles = {position:'absolute', left:'0px', top:pox, paddingLeft:'10px'};
+                        break;
+                }
+            }
+            axis.numberTicks = ticks.length;
+            axis.min = ticks[0].value;
+            axis.max = ticks[axis.numberTicks-1].value;
+            axis.tickInterval = (axis.max - axis.min) / (axis.numberTicks - 1);
+        }
+        
+        // we don't have any ticks yet, let's make some!
+        else {
+            if (name == 'xaxis' || name == 'x2axis') {
+                dim = this._canvasWidth;
+            }
+            else {
+                dim = this._canvasHeight;
+            }
+        
+            min = ((this.min != null) ? this.min : db.min);
+            max = ((this.max != null) ? this.max : db.max);
+            // perform some checks
+            if (min < 1e-9) min = 1e-9;
+            if (max < 1e-8) max = 1e-8;
+            if (this.pad >1.99) this.pad = 1.99;
+            var range = max - min;
+            var rmin, rmax;
+        
+            rmin = min - range/2*(this.pad - 1);
+            rmax = max + range/2*(this.pad - 1);
+            this.min = rmin;
+            this.max = rmax;
+            range = this.max - this.min;
+    
+            if (this.numberTicks == null){
+                if (dim > 100) {
+                    this.numberTicks = parseInt(3+(dim-100)/75);
+                }
+                else this.numberTicks = 2;
+            }
+    
+            this.tickInterval = range / (this.numberTicks-1);
+            for (var i=0; i<this.numberTicks; i++){
+                tt = this.min + i * range / (this.numberTicks-1);
+                var t = new $.jqplot.AxisTick();
+                var label = axis.tickFormatter(axis.ticks.formatString, tt);
+                t.setTick(tt, label, this.name);
+                axis._ticks.push(t);
+            }
+        }
+        
+        if (name == 'yaxis' || name == 'y2axis') this.ticks.styles.reverse();
+    };
+    
+    // functions: pack
+    // Define unit <-> coordinate conversions and properly position tick dom elements.
+    // Now we know offsets around the grid, we can define conversioning functions.
+    $.jqplot.linearAxisRenderer.prototype.pack = function(offsets, gridwidth, gridheight) {
+        var ticks = this._ticks;
+        var max = this.max;
+        var min = this.min;
+        
+        if (this.name == 'xaxis' || this.name == 'x2axis') {
+            this._offsets = {min:offsets.left, max:offsets.right};
+            
+            this.p2u = function(p) {
+                return (p - this._offsets.min)*(this.max - this.min)/(this._canvasWidth - this._offsets.max - this._offsets.min) + this.min;
+            }
+            
+            this.u2p = function(u) {
+                return (u - this.min) * (this._canvasWidth - this._offsets.max - this._offsets.min) / (this.max - this.min) + this._offsets.min;
+            }
+            
+            this.series_u2p = function(u) {
+                return (u - this.min) * gridwidth / (this.max - this.min);
+            }
+            
+            this.series_p2u = function(p) {
+                return p * (this.max - this.min) / gridwidth + this.min;
+            }
+            
+            if (this.show) {
+                // set the position
+                if (this.name == 'xaxis') {
+                    $(this._elem).css({position:'absolute', left:'0px', top:(this._canvasHeight-offsets.bottom)+'px'});
+                }
+                else {
+                    $(this._elem).css({position:'absolute', left:'0px', bottom:(this._canvasHeight-offsets.top)+'px'});
+                }
+                for (i=0; i<ticks.length; i++) {
+                    var t = ticks[i];
+                    var shim = $(t._elem).outerWidth(true)/2;
+                    var val = this.u2p(t.value) - shim + 'px';
+                    $(t._elem).css('left', val);
+                    // remember, could have done it this way
+                    //tickdivs[i].style.left = val;
+                }
+            }
+        }  
+        else {
+            this._offsets = {min:offsets.bottom, max:offsets.top};
+            
+            this.p2u = function(p) {
+                return (p - this._canvasHeight + this._offsets.min)*(max - min)/(this._canvasHeight - this._offsets.min - this._offsets.max) + min;
+            }
+            
+            this.u2p = function(u) {
+                return -(u - min) * (this._canvasHeight - this._offsets.min - this._offsets.max) / (max - min) + this._canvasHeight - this._offsets.min;
+            }
+            
+            this.series_u2p = function(u) {
+                return (max - u) * gridheight /(max - min);
+            }
+            
+            this.series_p2u = function(p) {
+                return -p * (max - min) / gridheight + max;
+            }
+            
+            if (this.show) {
+                // set the position
+                if (this.name == 'yaxis') {
+                    $(this._elem).css({position:'absolute', right:(this._canvasWidth-offsets.left)+'px', top:'0px'});
+                }
+                else {
+                    $(this._elem).css({position:'absolute', left:(this._canvasWidth - offsets.right)+'px', top:'0px'});
+                }
+                for (i=0; i<tickdivs.length; i++) {
+                    var t = ticks[i];
+                    var shim = $(t._elem).outerHeight(true)/2;
+                    var val = axis.u2p(t.value) - shim + 'px';
+                    $(t._elem).css('top', val);
+                }
+            }
+        }    
+    };
+    
+    // Class: categoryAxisRenderer
+    // Subclass of linearAxisRenderer.  Renderes axis as equally spaced category labels.
+    $.jqplot.categoryAxisRenderer = function() {
+        $.jqplot.linearAxisRenderer.call(this);
+    };
+ 
+    // function: setAxis
+    // called with scope of an axis
+    // Populate the axis properties, giving a label and value
+    // (corresponding to the user data coordinates, not plot coords.)
+    // for each tick on the axis.
+    $.jqplot.categroyAxisRenderer.prototype.setAxis = function(plotHeight, plotWidth) {
+        // we're are operating on an axis here
+        var axis = this;
+        var ticks = this._ticks;
+        var name = this.name;
+        var db = this._dataBounds;
+        var dim, interval;
+        var min, max;
+        var pos1, pos2;
+        var tt, i;
+        
+        this._canvasHeight = plotHeight;
+        this._canvasWidth = plotWidth;
+        
+        // if tick
+        
+        // Ticks should have been already specified by the user or set by the line renderer.  Since we
+        // don't have access to the series, there's not much to do if we don't already have ticks.
+        if (ticks.length) {
+            this.numberTicks =ticks.length;
+            for (i=0; i<ticks.length; i++){
+                var t = ticks[i];
+                if (!t.label) t.label = this.formatter(this.formatString, t.value.toString());
+                // databounds should have been set to 0, 2*series length in line renderer
+                this.min = db.min;
+                this.max = db.max;
+                // set iitial css positioning styles for the ticks.
+                var pox = i*15+'px';
+                switch (name) {
+                    case 'xaxis':
+                        t._styles = {position:'absolute', top:'0px', left:pox, paddingTop:'10px'};
+                        break;
+                    case 'x2axis':
+                        t._styles = {position:'absolute', bottom:'0px', left:pox, paddingBottom:'10px'};
+                        break;
+                    case 'yaxis':
+                        t._styles = {position:'absolute', right:'0px', top:pox, paddingRight:'10px'};
+                        break;
+                    case 'y2axis':
+                        t._styles = {position:'absolute', left:'0px', top:pox, paddingLeft:'10px'};
+                        break;
+                }
+            }
+        }
+        
+        // we don't have any ticks yet, try to guess based on the bounds.
+        else {
+            // TBD what to do here.
+        }
+        
+        if (name == 'yaxis' || name == 'y2axis') this.ticks.styles.reverse();
+    };
+    
+    
+})(jQuery);

jqplot.AxisTicks.js

+(function($) {    
+    $.jqplot.AxisTick = function() {
+        this.mark = 'outside';
+        this.isMinorTick = false;
+        this.size = 4;
+        this.show = true;
+        this.showLabel = true;
+        this.label = '';
+        this.value;
+        this._styles = {};
+        this.formatString;
+        this.fontFamily='';
+        this.fontSize = '0.75em';
+        this.textColor = '';
+        this._elem;
+    };
+    
+    $.jqplot.AxisTick.prototype.setTick = function(value, label, axisName, isMinor) {
+        this.label = label;
+        this.value = value;
+        var pox = '15px';
+        switch (axisName) {
+            case 'xaxis':
+                this._styles = {position:'absolute', top:'0px', left:pox, paddingTop:'10px'};
+                break;
+            case 'x2axis':
+                this._styles = {position:'absolute', bottom:'0px', left:pox, paddingBottom:'10px'};
+                break;
+            case 'yaxis':
+                this._styles = {position:'absolute', right:'0px', top:pox, paddingRight:'10px'};
+                break;
+            case 'y2axis':
+                this._styles = {position:'absolute', left:'0px', top:pox, paddingLeft:'10px'};
+                break;
+        }
+        if (isMinor) this.isMinorTick = true;
+        return this;
+    };
+    
+    $.jqplot.AxisTick.prototype.draw = function() {
+        this._elem = $('<div class="jqplot-axis-tick"></div>').get(0);
+        return this._elem;
+    }
+})(jQuery);

jqplot.canvasGridRenderer.js

         ctx.save();
         ctx.fillStyle = grid.background;
         ctx.fillRect(grid._left, grid._top, grid._width, grid._height);
+        
+        function drawGridLine(x1, y1, x2, y2) {
+            
+        }
+        
         if (grid.drawGridlines) {
             ctx.save();
             ctx.lineJoin = 'miter';
             ctx.strokeStyle = '#cccccc';
             for (var name in axes) {
                 var axis = axes[name];
+                var ticks = axis._ticks;
                 if (axis.show) {
-                    var ticks = axis.ticks;
                     switch (name) {
                         case 'xaxis':
-                            for (var i=0; i<ticks.values.length; i++) {
-                                var pos = Math.round(axis.u2p(ticks.values[i])) + 0.5;
-                                ctx.beginPath();
-                                ctx.moveTo(pos, grid._top);
-                                ctx.lineTo(pos, grid._bottom);
-                                ctx.stroke();
+                            for (var i=0; i<ticks.length; i++) {
+                                var pos = Math.round(axis.u2p(axis._ticks[i].value)) + 0.5;
+                                drawLine(pos, grid._top, pos, grid._bottom);
                             }
                             break;
                         case 'yaxis':
-                            for (var i=0; i<ticks.values.length; i++) {
-                                var pos = Math.round(axis.u2p(ticks.values[i])) + 0.5;
-                                ctx.beginPath();
-                                ctx.moveTo(grid._right, pos);
-                                ctx.lineTo(grid._left, pos);
-                                ctx.stroke();
+                            for (var i=0; i<ticks.length; i++) {
+                                var pos = Math.round(axis.u2p(axis._ticks[i].value)) + 0.5;
+                                drawLine(grid._right, pos, grid._left, pos);
                             }
                             break;
                         case 'x2axis':
-                            for (var i=0; i<ticks.values.length; i++) {
-                                var pos = Math.round(axis.u2p(ticks.values[i])) + 0.5;
-                                ctx.beginPath();
-                                ctx.moveTo(pos, grid._bottom);
-                                ctx.lineTo(pos, grid._top);
-                                ctx.stroke();
+                            for (var i=0; i<ticks.length; i++) {
+                                var pos = Math.round(axis.u2p(axis._ticks[i].value)) + 0.5;
+                                drawLine(pos, grid._bottom, pos, grid._top);
                             }
                             break;
                         case 'y2axis':
-                            for (var i=0; i<ticks.values.length; i++) {
-                                var pos = Math.round(axis.u2p(ticks.values[i])) + 0.5;
-                                ctx.beginPath();
-                                ctx.moveTo(grid._left, pos);
-                                ctx.lineTo(grid._right, pos);
-                                ctx.stroke();
+                            for (var i=0; i<ticks.length; i++) {
+                                var pos = Math.round(axis.u2p(axis._ticks[i].value)) + 0.5;
+                                drawLine(grid._left, pos, grid._right, pos);
                             }
                             break;
                     }
             ctx.restore();
         }
         
-        function drawMark(bx, by, ex, ey) {
+        function drawLine(bx, by, ex, ey) {
             ctx.beginPath();
             ctx.moveTo(bx, by);
             ctx.lineTo(ex, ey);
             var axis = axes[name];
             if (axis.show && axis.ticks.mark) {
                 var ticks = axis.ticks;
-                var s = ticks.size;
+                var s = ticks.markSize;
                 var m = ticks.mark;
+                var t = axis._ticks;
                 switch (name) {
                     case 'xaxis':
-                        for (var i=0; i<ticks.values.length; i++) {
-                            var pos = Math.round(axis.u2p(ticks.values[i])) + 0.5;
+                        for (var i=0; i<t.length; i++) {
+                            var pos = Math.round(axis.u2p(t[i].value)) + 0.5;
                             var b, e;
                             switch (m) {
                                 case 'inside':
                                     e = grid._bottom+s;
                                     break;
                             }
-                            drawMark(pos, b, pos, e);
+                            drawLine(pos, b, pos, e);
                         }
                         break;
                     case 'yaxis':
-                        for (var i=0; i<ticks.values.length; i++) {
-                            var pos = Math.round(axis.u2p(ticks.values[i])) + 0.5;
+                        for (var i=0; i<t.length; i++) {
+                            var pos = Math.round(axis.u2p(t[i].value)) + 0.5;
                             var b, e;
                             switch (m) {
                                 case 'outside':
                                     e = grid._left;
                                     break;
                             }
-                            drawMark(b, pos, e, pos);
+                            drawLine(b, pos, e, pos);
                         }
                         break;
                     case 'x2axis':
-                        for (var i=0; i<ticks.values.length; i++) {
-                            var pos = Math.round(axis.u2p(ticks.values[i])) + 0.5;
+                        for (var i=0; i<t.length; i++) {
+                            var pos = Math.round(axis.u2p(t[i].value)) + 0.5;
                             var b, e;
                             switch (m) {
                                 case 'outside':
                                     e = grid._top;
                                     break;
                             }
-                            drawMark(pos, b, pos, e);
+                            drawLine(pos, b, pos, e);
                         }
                         break;
                     case 'y2axis':
-                        for (var i=0; i<ticks.values.length; i++) {
-                            var pos = Math.round(axis.u2p(ticks.values[i])) + 0.5;
+                        for (var i=0; i<t.length; i++) {
+                            var pos = Math.round(axis.u2p(t[i].value)) + 0.5;
                             var b, e;
                             switch (m) {
                                 case 'inside':
                                     e = grid._right+s;
                                     break;
                             }
-                            drawMark(b, pos, e, pos);
+                            drawLine(b, pos, e, pos);
                         }
                         break;
                 }
 	<!--[if IE]><script language="javascript" type="text/javascript" src="excanvas.js"></script><![endif]-->
     <script language="javascript" type="text/javascript" src="../jquery-1.3.2.js"></script>
     <script language="javascript" type="text/javascript" src="jquery.jqplot.js"></script>
-    <script language="javascript" type="text/javascript" src="jqplot.lineAxisRenderer.js"></script>
+    <script language="javascript" type="text/javascript" src="jqplot.AxisTicks.js"></script>
+    <script language="javascript" type="text/javascript" src="jqplot.linearAxisRenderer.js"></script>
+    <script language="javascript" type="text/javascript" src="jqplot.logAxisRenderer.js"></script>
     <script language="javascript" type="text/javascript" src="jqplot.canvasGridRenderer.js"></script>
     <script language="javascript" type="text/javascript" src="jqplot.lineRenderer.js"></script>
     <script language="javascript" type="text/javascript" src="jqplot.markRenderer.js"></script>
     <script language="javascript" type="text/javascript" src="jqplot.trendLines.js"></script>
     <script language="javascript">
+				
 		$(document).ready(function(){
 			// chart = $.jqplot('chartdiv', [[[1,1],[2,4], [3,5], [4,5], [6,12]], [[32,18],[46,32],[79,47]]], {series:[{}, {xaxis:'x2axis', yaxis:'y2axis'}], axes:{y2axis:{scale:1.5}}});
 			// chart = $.jqplot('chartdiv', [[[1,1],[2,4], [3,5], [4,5], [6,12]], [[32,18],[46,14],[79,55]]]);
         chart1 = $.jqplot('chartdiv1', [[[1,1],[2,4], [4,5], [5,3], null, [7,8]], [[6,10],[10,7.7],[14,10],[18,15]]]);
         chart2 = $.jqplot('chartdiv2', [[[1,1],[2,4], [4,5], [5,3], null, [7,8]], [[2,35],[5,30],[9,18],[11,21]]], {title:'Sample #2', series:[{}, {yaxis:'y2axis'}]});
 */
+
+				
 				var data = [];
 				var temp = [];
-				var d = 87;
+				var d = 2;
 				var g = 1.136465;
-				g = 1.2;
-				for (var i=1; i<20; i++) {
+				g = 1.6;
+				for (var i=1; i<10; i++) {
 					temp.push([i, d]);
 					d *= g;
 				}
 				// 	console.log(data[0][i]);
 				// }
         chart = $.jqplot('chartdiv', 
-            [[[1,1],[2,2],[3,5],[4,3.6],[5,6.2],[6,12.1]], [[21,1],[32,313],[43,752],[54,3081],[65,6472],[76,8947]]],
+            //[[[1,1],[2,2],[3,5],[4,3.6],[5,6.2],[6,12.1]], [[21,1],[32,313],[43,752],[54,3081],[65,6472],[76,8947]]],
             //[[[1,1.1],[2,1.4],[3,1.6],[4,2],[5,2.45],[6,2.8],[7,3.4],[8,4.4],[9,4.8],[10,5.4]]], 
-						// data,
+						data,
+						//[[[4, 34], [5, 39], [6, 47], [7, 41], [8, 52], [9, 63], [10, 76]]],
             {
                 title:{text:'dP increase'}, 
                 legend:{textColor:'', fontSize:'0.8em', location:'nw', rowSpacing:'0.2em'}, 
                 grid:{borderShadow:true, drawGridlines:true, background: '#ffffe0'}, 
                 axesDefaults:{ticks:{mark:'cross', showLabels:true, size:6, fontSize:'0.75em'}}, 
-                series:[{show:true, trendLines:{type: 'exp', show:true, label:'Line 1 Trend'}, label:'Line 1'}, {show: false, label:'Line 2', xaxis:'xaxis', yaxis:'yaxis'}],
-								// series:[{label:'calculated 2', xaxis:'xaxis', yaxis:'yaxis'}],
+                // series:[{show:true, trendLines:{type: 'exp', show:true, label:'Line 1 Trend'}, label:'Line 1'}, {show: false, label:'Line 2', xaxis:'xaxis', yaxis:'yaxis'}],
+								series:[{renderer:{}, label:'calculated 2', xaxis:'xaxis', yaxis:'yaxis'}],
                 // axes:{yaxis:{pad:1, type:'log', ticks:{values:[100, 158, 251, 398, 630, 1000]}}},
-								axes:{yaxis:{pad:1.1, type:'log', logStyle:'power'}},
+								axes:{yaxis:{rendererOptions:{tickDistribution:'power'}, renderer: new $.jqplot.logAxisRenderer()}},
         				seriesDefaults:{rendererOptions:{marker:{style:'filledCircle'}}}
             });
 

jqplot.lineAxisRenderer.js

     $.jqplot.lineAxisRenderer = function() {
     };
     
+    $.jqplot.lineAxisRenderer.prototype.init = function(options){
+        $.extend(true, this, options);
+    };
     
+    // function: draw
+    // Creates the axis container DOM element and tick DOM elements.
+    // Populates some properties of the elements and figures out
+    // height and width of element.
     // called with scope of axis
     $.jqplot.lineAxisRenderer.prototype.draw = function(target, plotHeight, plotWidth) {
         var axis = this;
+        if (this._mode == 'category') {
+            this.type = 'linear';
+        }
         if (axis.show) {
             // populate the axis label and value properties.
             axis.renderer.setAxis.call(axis, plotHeight, plotWidth);
         }
     };
     
+    // function: setAxis
     // called with scope of an axis
     // Populate the axis properties, giving a label and value
     // (corresponding to the user data coordinates, not plot coords.)
         this.renderer.fill.call(this);
     };
     
+    // function: fill
+    // Set initial styles on tick dom elements.
+    // figure out numberTicks, min, max, tickInterval and tick values.
     $.jqplot.lineAxisRenderer.prototype.fill = function() {
         var name = this.name;
         var db = this._dataBounds;
         var min, max;
         var pos1, pos2;
         var tt;
-        if (this.ticks.values.length) {
+        
+        if (this._mode == 'category') {
+            var v = this.ticks.values;
+            var l = this.ticks.labels;
+            this.numberTicks = v.length;
+            this.tickInterval = this.max/2;
+            this.min = db.min;
+            this.max = db.max;
+            this.max = 2*this.numberTicks;
+            for (var i=0; i<this.numberTicks; i++) {
+                var pox = i*15+'px';
+                switch (name) {
+                    case 'xaxis':
+                        this.ticks.styles.push({position:'absolute', top:'0px', left:pox, paddingTop:'10px'});
+                        break;
+                    case 'x2axis':
+                        this.ticks.styles.push({position:'absolute', bottom:'0px', left:pox, paddingBottom:'10px'});
+                        break;
+                    case 'yaxis':
+                        this.ticks.styles.push({position:'absolute', right:'0px', top:pox, paddingRight:'10px'});
+                        break;
+                    case 'y2axis':
+                        this.ticks.styles.push({position:'absolute', left:'0px', top:pox, paddingLeft:'10px'});
+                        break;
+                }
+            }
+        }
+        
+        else if (this.ticks.values.length) {
             var v = this.ticks.values;
             this.numberTicks = v.length;
             this.min = v[0];
         if (name == 'yaxis' || name == 'y2axis') this.ticks.styles.reverse();
     };
     
-    // Now we know offsets around the grid, we can define conversioning functions
-    // and properly lay out the axes.
+    // functions: pack
+    // Define unit <-> coordinate conversions and properly position tick dom elements.
+    // Now we know offsets around the grid, we can define conversioning functions.
     $.jqplot.lineAxisRenderer.prototype.pack = function(offsets, gridwidth, gridheight) {
         var lb = parseInt(this.logBase);
         var ticks = this.ticks;

jqplot.lineRenderer.js

         // or an options object with a renderer property and additional options to pass
         // to the renderer.  See the renderer for additional options.
         this.marker = new $.jqplot.markRenderer();
+        // // prop: mode
+        // // 'scatter' or 'category'
+        // // 'scatter' gives an X-Y scatter line plot, 'category' gives equally spaced data line plot.
+        // this.mode = 'scatter'
+        // prop: showLine
+        // wether to actually draw the line or not.  Series will still be renderered, even if no line is drawn.
+        this.showLine = true;
     };
     
     $.jqplot.lineRenderer.prototype.init = function(options) {
 
     $.jqplot.lineRenderer.prototype.draw = function(series, grid, ctx) {
         var i;
-        ctx.save();
-        ctx.beginPath();
         var xaxis = series.xaxis;
         var yaxis = series.yaxis;
         var d = series.data;
         var xp = series._xaxis.series_u2p;
         var yp = series._yaxis.series_u2p;
-        // use a clipping path to cut lines outside of grid.
-        // ctx.moveTo(grid._left, grid._top);
-        // ctx.lineTo(grid._right, grid._top);
-        // ctx.lineTo(grid._right, grid._bottom);
-        // ctx.lineTo(grid._left, grid._bottom);
-        // ctx.closePath();
-        // ctx.clip();
-        // ctx.beginPath();
-        ctx.lineJoin = 'round';
-        ctx.lineCap = 'round';
-        ctx.lineWidth = this.lineWidth;
-        ctx.strokeStyle = this.color;
         var pointx, pointy;
-        // recalculate the grid data
-        series.gridData = [];
-        series.gridData.push([xp.call(series._xaxis, series.data[0][0]), yp.call(series._yaxis, series.data[0][1])]);
-        ctx.moveTo(series.gridData[0][0], series.gridData[0][1]);
-        for (var i=1; i<series.data.length; i++) {
-            series.gridData.push([xp.call(series._xaxis, series.data[i][0]), yp.call(series._yaxis, series.data[i][1])]);
-            ctx.lineTo(series.gridData[i][0], series.gridData[i][1]);
-        }
-        ctx.stroke();
+        ctx.save();
+        if (this.showLine) {
+            ctx.beginPath();
+            ctx.lineJoin = 'round';
+            ctx.lineCap = 'round';
+            ctx.lineWidth = this.lineWidth;
+            ctx.strokeStyle = this.color;
+            // recalculate the grid data
+            series.gridData = [];
+            series.gridData.push([xp.call(series._xaxis, series.data[0][0]), yp.call(series._yaxis, series.data[0][1])]);
+            ctx.moveTo(series.gridData[0][0], series.gridData[0][1]);
+            for (var i=1; i<series.data.length; i++) {
+                series.gridData.push([xp.call(series._xaxis, series.data[i][0]), yp.call(series._yaxis, series.data[i][1])]);
+                ctx.lineTo(series.gridData[i][0], series.gridData[i][1]);
+            }
+            ctx.stroke();
         
-        // now draw the shadows
-        if (this.shadow) {
-            ctx.save();
-            for (var j=0; j<this.shadowDepth; j++) {
-                ctx.translate(Math.cos(this.shadowAngle*Math.PI/180)*this.shadowOffset, Math.sin(this.shadowAngle*Math.PI/180)*this.shadowOffset);
-                ctx.beginPath();
-                ctx.strokeStyle = 'rgba(0,0,0,'+this.shadowAlpha+')';
-                ctx.moveTo(series.gridData[0][0], series.gridData[0][1]);
-                for (var i=1; i<series.data.length; i++) {
-                    // pointx = xp.call(series._xaxis, series.data[i][0]);
-                    // pointy = yp.call(series._yaxis, series.data[i][1]);
-                    ctx.lineTo(series.gridData[i][0], series.gridData[i][1]);
+            // now draw the shadows
+            if (this.shadow) {
+                ctx.save();
+                for (var j=0; j<this.shadowDepth; j++) {
+                    ctx.translate(Math.cos(this.shadowAngle*Math.PI/180)*this.shadowOffset, Math.sin(this.shadowAngle*Math.PI/180)*this.shadowOffset);
+                    ctx.beginPath();
+                    ctx.strokeStyle = 'rgba(0,0,0,'+this.shadowAlpha+')';
+                    ctx.moveTo(series.gridData[0][0], series.gridData[0][1]);
+                    for (var i=1; i<series.data.length; i++) {
+                        // pointx = xp.call(series._xaxis, series.data[i][0]);
+                        // pointy = yp.call(series._yaxis, series.data[i][1]);
+                        ctx.lineTo(series.gridData[i][0], series.gridData[i][1]);
+                    }
+                    ctx.stroke();
                 }
-                ctx.stroke();
+                ctx.restore();
             }
-            ctx.restore();
         }
         
         // now draw the markers
         // points and set the axes bounds.  
         var d = this.data;
         var i;
-        var dbx = this._xaxis._dataBounds;
-        var dby = this._yaxis._dataBounds;
+        var xaxis = this._xaxis;
+        var yaxis = this._yaxis;
+        var dbx = xaxis._dataBounds;
+        var dby = yaxis._dataBounds;
 
         // weed out any null points and set the axes bounds
         for (i=0; i<d.length; i++) {
             if (d[i] == null || d[i][0] == null || d[i][1] == null) {
                 // if line breaking on null values is set, keep the null in the data
-                if (this.breakOnNull) d[i] = null;
+                // if (this.renderer.breakOnNull && this.renderer.mode == 'scatter') d[i] = null;
                 // else delete the null to skip the point.
-                else d.splice(i,1);
+                // else d.splice(i,1);
+                // For the time being, just delete null values
+                d.splice(i,1);
+                continue;
             }
-        }
-        for (i=0; i<d.length; i++) {
-            if (d[i] == null || d[i][0] == null || d[i][1] == null) continue;
+            // Set the initial axes databounds.  May be overriden later by axis padding or by
+            // specific types of axes which call for different bounds.
             else {                
                 if (d[i][0] < dbx.min || dbx.min == null) dbx.min = d[i][0];
                 if (d[i][0] > dbx.max || dbx.max == null) dbx.max = d[i][0];
                 if (d[i][1] > dby.max || dby.max == null) dby.max = d[i][1];
             }
         }
+        
+        // Maybe don't do any of this.  Handle category intelligence within Axis.
+        // 
+        // // if the xaxis is a category axis, modify the databounds and ticks.
+        // if (xaxis.renderer.prototype == $.jqplot.categoryAxisRenderer) {
+        //     // A category line (or just line) plot.
+        //     // Populate the axis values if none were given.
+        //     // Each axis values will start at 1 and increment by 2 so
+        //     // that we can have nice "bins" for data and ticks.
+        //     // set the databaounds to 1 less and greater than number of bins.
+        //     dbx.min = 0;
+        //     dbx.max = d.length*2;
+        //     var ticks = xaxis._ticks;
+        //     if (!ticks.length) {
+        //         ticks = [];
+        //         for (i=0; i<d.length; i++) {
+        //             var t = new $.jqplot.AxisTick();
+        //             // set the tick value to it's position on the axis
+        //             // and set its label to the x value of the line.
+        //             ticks.push(t.init(2*i+1, d[i][0].toString(), xaxis.name));
+        //             // now reassign the x value to the right bin.
+        //             d[i][0] = 2*i+1;
+        //         }
+        //     }
+        // }
+        // 
+        // // Don't know if this makes sense, but allow it anyway.
+        // // if the yaxis is a category axis, modify the databounds and ticks.
+        // if (yaxis.renderer.prototype == $.jqplot.categoryAxisRenderer) {
+        //     // A category line (or just line) plot.
+        //     // Populate the axis values if none were given.
+        //     // Each axis values will start at 1 and increment by 2 so
+        //     // that we can have nice "bins" for data and ticks.
+        //     // set the databaounds to 1 less and greater than number of bins.
+        //     dby.min = 0;
+        //     dby.max = d.length*2;
+        //     var ticks = yaxis._ticks;
+        //     if (!ticks.length) {
+        //         ticks = [];
+        //         for (i=0; i<d.length; i++) {
+        //             var t = new $.jqplot.AxisTick();
+        //             // set the tick value to it's position on the axis
+        //             // and set its label to the y value of the line.
+        //             ticks.push(t.init(2*i+1, d[i][1].toString(), xaxis.name));
+        //             // now reassign the y value to the right bin.
+        //             d[i][1] = 2*i+1;
+        //         }
+        //     }
+        // }
     };
 })(jQuery);

jqplot.logAxisRenderer.js

 (function($) {
+    var debug = 1;
+        
+	// Convienence function that won't hang IE.
+	function log() {
+	    if (window.console && debug) {
+	       if (arguments.length == 1) console.log (arguments[0]);
+	       else console.log(arguments);
+	    }
+	};
+	
     $.jqplot.logAxisRenderer = function() {
+        this.base = 10;
+        this.tickDistribution = 'even';
     };
     
-    $.jqplot.logAxisRenderer.prototype.fill = $.jqplot.linearAxisRenderer.fill;
+    $.jqplot.logAxisRenderer.prototype.init = function(options) {
+        $.extend(true, this, options);
+    };
     
+    // function: draw
+    // Creates the axis container DOM element and tick DOM elements.
+    // Populates some properties of the elements and figures out
+    // height and width of element.
+    // called with scope of axis
+    $.jqplot.logAxisRenderer.prototype.draw = function(target, plotHeight, plotWidth) {
+        // we are operating on a axis objec here.
+        var axis = this;
+        if (axis.show) {
+            // populate the axis label and value properties.
+            axis.renderer.setAxis.call(axis, plotHeight, plotWidth);
+            // fill a div with axes labels in the right direction.
+            // Need to pregenerate each axis to get it's bounds and
+            // position it and the labels correctly on the plot.
+            var h, w;
+            
+            axis._elem = $('<div class="jqplot-axis"></div>').appendTo(target).get(0);
+            //for (var s in axis.style) $(axis._elem).css(s, axis.style[s]);
+    
+            if (axis.ticks.showLabels) {
+                var t = axis._ticks;
+                for (var i=0; i<t.length; i++) {
+                    var tick = t[i];
+                    if (!tick.isMinorTick || axis.showMinorTicks) {
+                        var elem = tick.draw();
+                        //var elem = $(frag).appendTo(axis._elem).get(0);
+                        $(elem).appendTo(axis._elem);
+                        for (var s in tick._styles) {
+                            $(elem).css(s, tick._styles[s]);   
+                        }
+                        $(elem).html(tick.label);
+                    
+                        if (axis.ticks.fontFamily) elem.style.fontFamily = axis.ticks.fontFamily;
+                        if (axis.ticks.fontSize) elem.style.fontSize = axis.ticks.fontSize;
+                    
+                        h = $(elem).outerHeight(true);
+                        w = $(elem).outerWidth(true);
+                    
+                        if (axis._height < h) {
+                            axis._height = h;
+                        }
+                        if (axis._width < w) {
+                            axis._width = w;
+                        }
+                    }
+                }
+            }
+        }
+    };
+    
+    // function: setAxis
+    // called with scope of an axis
+    // Populate the axis properties, giving a label and value
+    // (corresponding to the user data coordinates, not plot coords.)
+    // for each tick on the axis.
+    // Set initial styles on tick dom elements.
+    // figure out numberTicks, min, max, tickInterval and tick values.
+    $.jqplot.logAxisRenderer.prototype.setAxis = function(plotHeight, plotWidth) {
+        // we're are operating on an axis here
+        var axis = this;
+        var ticks = axis._ticks;
+        var name = axis.name;
+        var db = axis._dataBounds;
+        var dim, interval;
+        var min, max;
+        var pos1, pos2;
+        var tt, i;
+        
+        axis._canvasHeight = plotHeight;
+        axis._canvasWidth = plotWidth;
+        
+        // if we already have ticks, use them.
+        // ticks must be in order of increasing value.
+        if (ticks.length) {
+            for (i=0; i<ticks.length; i++){
+                var t = ticks[i];
+                if (!t.label) t.label = t.value.toString();
+                // set iitial css positioning styles for the ticks.
+                var pox = i*15+'px';
+                switch (name) {
+                    case 'xaxis':
+                        t._styles = {position:'absolute', top:'0px', left:pox, paddingTop:'10px'};
+                        break;
+                    case 'x2axis':
+                        t._styles = {position:'absolute', bottom:'0px', left:pox, paddingBottom:'10px'};
+                        break;
+                    case 'yaxis':
+                        t._styles = {position:'absolute', right:'0px', top:pox, paddingRight:'10px'};
+                        break;
+                    case 'y2axis':
+                        t._styles = {position:'absolute', left:'0px', top:pox, paddingLeft:'10px'};
+                        break;
+                }
+            }
+            axis.numberTicks = ticks.length;
+            axis.min = ticks[0].value;
+            axis.max = ticks[axis.numberTicks-1].value;
+            axis.tickInterval = (axis.max - axis.min) / (axis.numberTicks - 1);
+        }
+        
+        // we don't have any ticks yet, let's make some!
+        else {
+            if (name == 'xaxis' || name == 'x2axis') {
+                dim = axis._canvasWidth;
+            }
+            else {
+                dim = axis._canvasHeight;
+            }
+        
+            min = ((axis.min != null) ? axis.min : db.min);
+            max = ((axis.max != null) ? axis.max : db.max);
+            // perform some checks
+            if (min < 1e-9) min = 1e-9;
+            if (max < 1e-8) max = 1e-8;
+            if (axis.pad >1.99) axis.pad = 1.99;
+            var range = max - min;
+            var rmin, rmax;
 
-    $.jqplot.logAxisRenderer.prototype.pack = function(offsets) {
-        var ticks = this.ticks;
-        var tickdivs = $(this.elem).children('div');
-        if (this.name == 'xaxis' || this.name == 'x2axis') {
-            this.offsets = {min:offsets.left, max:offsets.right};
-            
-            this.p2u = function(p) {
-                return (p - this.offsets.min)*(this.max - this.min)/(this.gridWidth - this.offsets.max - this.offsets.min) + this.min;
+            if (axis.renderer.tickDistribution == 'even') {                    
+                // for log axes, open up range to get a nice power of axis.renderer.base.
+                rmin = min - min*((axis.pad-1)/2);
+                rmax = max + max*((axis.pad-1)/2);
+                axis.min = rmin;
+                axis.max = rmax;
+                range = axis.max - axis.min;            
+        
+                if (axis.numberTicks == null){
+                    if (dim > 100) {
+                        axis.numberTicks = parseInt(3+(dim-100)/75);
+                    }
+                    else {
+                        axis.numberTicks = 2;
+                    }
+                }
+    
+                var u = Math.pow(axis.renderer.base, (1/(axis.numberTicks-1)*Math.log(axis.max/axis.min)/Math.log(axis.renderer.base)));
+                for (var i=0; i<axis.numberTicks; i++){
+                    tt = axis.min * Math.pow(u, i);
+                    var t = new $.jqplot.AxisTick();
+                    var label = axis.tickFormatter(axis.ticks.formatString, tt);
+                    t.setTick(tt, label, name);
+                    axis._ticks.push(t);
+                }
+                
             }
             
-            this.u2p = function(u) {
-                return (u - this.min) * (this.gridWidth - this.offsets.max - this.offsets.min) / (this.max - this.min) + this.offsets.min;
+            else if (axis.renderer.tickDistribution == 'power'){
+                
+                // for log axes, open up range to get a nice power of axis.renderer.base.
+                rmin = Math.pow(axis.renderer.base, Math.ceil(Math.log(min*(2-axis.pad))/Math.log(axis.renderer.base))-1);
+                rmax = Math.pow(axis.renderer.base, Math.floor(Math.log(max*axis.pad)/Math.log(axis.renderer.base))+1);
+                axis.min = rmin;
+                axis.max = rmax;
+                range = axis.max - axis.min;            
+        
+                var fittedTicks = 0;
+                var minorTicks = 0;
+                if (axis.numberTicks == null){
+                    if (dim > 100) {
+                        axis.numberTicks = Math.round(Math.log(axis.max/axis.min)/Math.log(axis.renderer.base) + 1);
+                        if (axis.numberTicks < 2) axis.numberTicks = 2;
+                        fittedTicks = parseInt(3+(dim-100)/75);
+                    }
+                    else {
+                        axis.numberTicks = 2;
+                        fittedTicks = 2;
+                    }
+                    // if we don't have enough ticks, add some intermediate ticks
+                    // how many to have between major ticks.
+                    if (axis.numberTicks < fittedTicks-1) {
+                        minorTicks = Math.floor(fittedTicks/axis.numberTicks);
+                    }
+                }
+
+                for (var i=0; i<axis.numberTicks; i++){
+                    tt = Math.pow(axis.renderer.base, i - axis.numberTicks + 1) * axis.max;
+                    var t = new $.jqplot.AxisTick();
+                    var label = axis.tickFormatter(axis.ticks.formatString, tt);
+                    t.setTick(tt, label, name);
+                    axis._ticks.push(t);
+            
+                    if (minorTicks && i<axis.numberTicks-1) {
+                        var tt1 = Math.pow(axis.renderer.base, i - axis.numberTicks + 2) * axis.max;
+                        var spread = tt1 - tt;
+                        var interval = tt1 / (minorTicks+1);
+                        for (var j=minorTicks-1; j>=0; j--) {
+                            var t = new $.jqplot.AxisTick();
+                            var val = tt1-interval*(j+1);
+                            var label = axis.tickFormatter(axis.ticks.formatString, val, true);
+                            t.setTick(val, label, name);
+                            axis._ticks.push(t);
+                        }
+                    }       
+                }                    
+            }       
+        }
+        
+        if (name == 'yaxis' || name == 'y2axis') this.ticks.styles.reverse();
+    };
+    
+    // functions: pack
+    // Define unit <-> coordinate conversions and properly position tick dom elements.
+    // Now we know offsets around the grid, we can define conversioning functions.
+    // called with scope of axis.
+    $.jqplot.logAxisRenderer.prototype.pack = function(offsets, gridwidth, gridheight) {
+        var axis = this;
+        var lb = parseInt(axis.renderer.base);
+        var ticks = axis._ticks;
+        var trans = function (v) { return Math.log(v)/Math.log(lb); };
+        max = Math.log(axis.max)/Math.log(lb);
+        min = Math.log(axis.min)/Math.log(lb);
+        
+        if (axis.name == 'xaxis' || axis.name == 'x2axis') {
+            axis._offsets = {min:offsets.left, max:offsets.right};
+            
+            axis.p2u = function(p) {
+                return (trans(p) - axis._offsets.min)*(axis.max - axis.min)/(axis._canvasWidth - axis._offsets.max - axis._offsets.min) + axis.min;
             }
             
-            if (this.show) {
-                for (i=0; i<tickdivs.length; i++) {
-                    var shim = $(tickdivs[i]).outerWidth()/2;
-                    var t = this.u2p(ticks.values[i]);
-                    var val = this.u2p(ticks.values[i]) - shim + 'px';
-                    $(tickdivs[i]).css('left', val);
+            axis.u2p = function(u) {
+                return (trans(u) - axis.min) * (axis._canvasWidth - axis._offsets.max - axis._offsets.min) / (axis.max - axis.min) + axis._offsets.min;
+            }
+            
+            axis.series_u2p = function(u) {
+                return (trans(u) - axis.min) * gridwidth / (axis.max - axis.min);
+            }
+            
+            axis.series_p2u = function(p) {
+                return trans(p) * (axis.max - axis.min) / gridwidth + axis.min;
+            }
+            
+            if (axis.show) {
+                // set the position
+                if (axis.name == 'xaxis') {
+                    $(axis._elem).css({position:'absolute', left:'0px', top:(axis._canvasHeight-offsets.bottom)+'px'});
+                }
+                else {
+                    $(axis._elem).css({position:'absolute', left:'0px', bottom:(axis._canvasHeight-offsets.top)+'px'});
+                }
+                for (i=0; i<ticks.length; i++) {
+                    var t = ticks[i];
+                    var shim = $(t._elem).outerWidth(true)/2;
+                    var val = axis.u2p(t.value) - shim + 'px';
+                    $(t._elem).css('left', val);
                     // remember, could have done it this way
                     //tickdivs[i].style.left = val;
                 }
             }
         }  
         else {
-            this.offsets = {min:offsets.bottom, max:offsets.top};
+            axis._offsets = {min:offsets.bottom, max:offsets.top};
             
-            this.p2u = function(p) {
-                return (p - this.gridHeight + this.offsets.min)*(this.max - this.min)/(this.gridHeight - this.offsets.min - this.offsets.max) + this.min;
+            axis.p2u = function(p) {
+                return (trans(p) - axis._canvasHeight + axis._offsets.min)*(max - min)/(axis._canvasHeight - axis._offsets.min - axis._offsets.max) + min;
             }
             
-            this.u2p = function(u) {
-                return -(u - this.min) * (this.gridHeight - this.offsets.min - this.offsets.max) / (this.max - this.min) + this.gridHeight - this.offsets.min;
+            axis.u2p = function(u) {
+                return -(trans(u) - min) * (axis._canvasHeight - axis._offsets.min - axis._offsets.max) / (max - min) + axis._canvasHeight - axis._offsets.min;
             }
-            if (this.show) {
-                for (i=0; i<tickdivs.length; i++) {
-                    var shim = $(tickdivs[i]).outerHeight()/2;
-                    var val = this.u2p(ticks.values[i]) - shim + 'px';
-                    $(tickdivs[i]).css('top', val);
+            
+            axis.series_u2p = function(u) {
+                return (max - trans(u)) * gridheight /(max - min);
+            }
+            
+            axis.series_p2u = function(p) {
+                return -trans(p) * (max - min) / gridheight + max;
+            }
+            
+            if (axis.show) {
+                // set the position
+                if (axis.name == 'yaxis') {
+                    $(axis._elem).css({position:'absolute', right:(axis._canvasWidth-offsets.left)+'px', top:'0px'});
+                }
+                else {
+                    $(axis._elem).css({position:'absolute', left:(axis._canvasWidth - offsets.right)+'px', top:'0px'});
+                }
+                for (i=0; i<ticks.length; i++) {
+                    var t = ticks[i];
+                    var shim = $(t._elem).outerHeight(true)/2;
+                    var val = axis.u2p(t.value) - shim + 'px';
+                    $(t._elem).css('top', val);
                 }
             }
         }    
         Parameters:
             name - Axis name (identifier).  One of 'xaxis', 'yaxis', 'x2axis' or 'y2axis'.
     */
-    function Axis(name) {
+    function Axis(name, series) {
         // Group: Properties
         
         // prop: name
         // Axis name (identifier).  One of 'xaxis', 'yaxis', 'x2axis' or 'y2axis'.
         this.name = name;
+        this.series = [];
         // prop: show
         // Wether to display the axis on the graph.
         this.show = false;
         // Padding to extend the range above and below the data bounds.
         // Factor to multiply by the data range when setting the axis bounds
         this.pad = 1.2;
-        // prop: type
-        // Type of axis, linear or log
-        this.type = 'linear';
-        // prop: logStyle
-        // For log axis, weather to compute ticks by equal power (power) or by equal display spacing (even).
-        // One of 'power' or 'even'
-        this.logStyle = 'even';
-        this.logBase = 10;
+        // // prop: type
+        // // Type of axis, linear or log
+        // this.type = 'linear';
+        // // prop: logStyle
+        // // For log axis, weather to compute ticks by equal power (power) or by equal display spacing (even).
+        // // One of 'power' or 'even'
+        // this.logStyle = 'even';
+        // this.logBase = 10;
+        // // prop: _mode
+        // // 'scatter' or 'category'
+        // // Category axis data value considered a category label, not a position.
+        // this._mode = 'scatter';
         // prop: numberTicks
         // Desired number of ticks.  Computed automatically by default
         this.numberTicks;
         this.tickInterval;
         // prop: renderer
         // Instance of a rendering engine that draws the axis on the plot.
-        this.renderer = new $.jqplot.lineAxisRenderer();
+        this.renderer = new $.jqplot.linearAxisRenderer();
+        this.rendererOptions = {};
         /*  
             prop: label
             Axis label object.  Container for axis label properties. Not implimeted yet.
             fontSize -css font-size spec.
             textColor - css color spec.
         */
-        this.ticks = {mark:'outside', size:4, showLabels:true, labels:[], values:[], styles:[], formatString:'%.1f', fontFamily:'', fontSize:'0.75em', textColor:''};
+        this.ticks = {mark:'outside', markSize:4, size:4, showLabels:true, labels:[], values:[], styles:[], formatString:'%.1f', fontFamily:'', fontSize:'0.75em', textColor:''};
+        this.showMinorTicks = true;
         // prop: tickFormatter
         // Function applied to format tick label text.
         this.tickFormatter = sprintf;
         // height of the grid canvas, total DOM element height.
         this._canvasHeight;
         this._ticks=[];
-    };
-    
-    function AxisTick() {
-        this.mark = 'outside';
-        this.isMinorTick = false;
-        this.size = 4;
-        this.show = true;
-        this.showLabel = true;
-        this.label = '';
-        this.value;
-        this.styles = {};
-        this.formatString;
-        this.fontFamily='';
-        this.fontSize = '0.75em';
-        this.textColor = '';
-        this._elem;
+        
+        // append a reference to the series using this axis.
+        this.series.push(series);
     };
     
     /* 
         this.xaxis = 'xaxis';
         // prop: _xaxis
         // reference to the underlying x axis object associated with this series.
-        this._xaxis = new Axis(this.xaxis);
+        this._xaxis = new Axis(this.xaxis, this);
         // prop: yaxis
         // name of y axis to associate with this series.
         this.yaxis = 'yaxis';
         // prop: _yaxis
         // reference to the underlying y axis object associated with this series.
-        this._yaxis = new Axis(this.yaxis);
+        this._yaxis = new Axis(this.yaxis, this);
         // prop: renderer
         // Instance of a renderer which will draw the series.
         this.renderer = new $.jqplot.lineRenderer();
         this.equalYTicks = true;
         // borrowed colors from Flot.
         this.seriesColors = ["#edc240", "#afd8f8", "#cb4b4b", "#4da74d", "#9440ed"];
-        this.seriesColorsIndex = 0;
+        this._seriesColorsIndex = 0;
         // Default font characteristics which can be overriden by individual 
         // plot elements.  All are css specs.
         this.textColor = '#666666';
             // get a handle to the plot object from the target to help with events.
             $(target).data('jqplot', this);
             this.data = data;
-            if (data.length < 1 || data[0].length < 2) throw "Invalid Plot data";
+            // if (data.length < 1 || data[0].length < 2) throw "Invalid Plot data";
             this.parseOptions(options);
             
             // set the dataBounds (min and max) for each axis
         }
         
         this.getNextSeriesColor = function() {
-            var c = this.seriesColors[this.seriesColorsIndex];
-            this.seriesColorsIndex++;
+            var c = this.seriesColors[this._seriesColorsIndex];
+            this._seriesColorsIndex++;
             return c;
         }
     
                     default:
                         break;
                 }
+                axis.renderer.init(axis.rendererOptions);
             }
+            if (this.data.length == 0) {
+                this.data = [];
+                for (var i=0; i<this.options.series.length; i++) {
+                    this.data.push(this.options.series.data);
+                }    
+            }
+                
+                
+            function normalizeData(data) {
+                // return data as an array of point arrays,
+                // in form [[x1,y1...], [x2,y2...], ...]
+                var temp = [];
+                var i;
+                if (!(data[0] instanceof Array)) {
+                    // we have a series of scalars.  One line with just y values.
+                    // turn the scalar list of data into a data array of form:
+                    // [[1, data[0]], [2, data[1]], ...]
+                    for (i=0; i<data.length; i++) {
+                        temp.push([i+1, data[i]]);
+                    }
+                }
             
+                else {
+                    // we have a properly formatted data series, copy it.
+                    $.extend(true, temp, data);
+                }
+                return temp;
+            };
+
+
+                
             for (var i=0; i<this.data.length; i++) {
                 var temp = $.extend(true, new Series(), this.options.seriesDefaults, this.options.series[i]);
-                temp.data = this.data[i];
+                temp.data = normalizeData(this.data[i]);
                 switch (temp.xaxis) {
                     case 'xaxis':
                         temp._xaxis = this.axes.xaxis;
                     temp._xaxis.show = true;
                     temp._yaxis.show = true;
                 }
-                
+
                 // parse the renderer options and apply default colors if not provided
                 if (!temp.rendererOptions.color && temp.show != false) {
                     temp.rendererOptions.color = this.getNextSeriesColor();
                 // temp.rendererOptions.show = temp.show;
                 temp.renderer.init(temp.rendererOptions);
                 // $.extend(true, temp.renderer, {color:this.seriesColors[i]}, this.rendererOptions);
-                this.series.push(temp);
+                this.series.push(temp);  
             }
             
             // copy the grid and title options into this object.
     // data - an array of data series.
     // options - user defined options object.
     $.jqplot = function(target, data, options) {
-        options = options || {};
+        var _data, _options;
+        
+        // check to see if only 2 arguments were specified, what is what.
+        if (options == null) {
+            if (data instanceof Array) {
+                _data = data;
+                _options = null;   
+            }
+            
+            else if (data.constructor == Object) {
+                _data = null;
+                _options = data;
+            }
+        }
+        else {
+            _data = data;
+            _options = options;
+        }
         var plot = new jqPlot();
-        plot.init(target, data, options);
+        plot.init(target, _data, _options);
         plot.draw();
         return plot;
     };