Commits

Chris Leonello committed 83d09c3

In process development of pyramid chart.

  • Participants
  • Parent commits 9faaeda

Comments (0)

Files changed (4)

File src/jquery.jqplot.css

     margin-right: 10px;
 }
 
-.jqplot-y2axis, .jqplot-y3axis, .jqplot-y4axis, .jqplot-y5axis, .jqplot-y6axis, .jqplot-y7axis, .jqplot-y8axis, .jqplot-y9axis {
+.jqplot-y2axis, .jqplot-y3axis, .jqplot-y4axis, .jqplot-y5axis, .jqplot-y6axis, .jqplot-y7axis, .jqplot-y8axis, .jqplot-y9axis, .jqplot-yMidAxis {
     margin-left: 10px;
     margin-right: 10px;
 }
 
 /*rules applied to all axis tick divs*/
-.jqplot-axis-tick, .jqplot-xaxis-tick, .jqplot-yaxis-tick, .jqplot-x2axis-tick, .jqplot-y2axis-tick, .jqplot-y3axis-tick, .jqplot-y4axis-tick, .jqplot-y5axis-tick, .jqplot-y6axis-tick, .jqplot-y7axis-tick, .jqplot-y8axis-tick, .jqplot-y9axis-tick {
+.jqplot-axis-tick, .jqplot-xaxis-tick, .jqplot-yaxis-tick, .jqplot-x2axis-tick, .jqplot-y2axis-tick, .jqplot-y3axis-tick, .jqplot-y4axis-tick, .jqplot-y5axis-tick, .jqplot-y6axis-tick, .jqplot-y7axis-tick, .jqplot-y8axis-tick, .jqplot-y9axis-tick, .jqplot-yMidAxis-tick {
     position: absolute;
 }
 
     text-align: left;
 }
 
-.jqplot-meterGauge-tick {
-    font-size: 0.75em;
-    color: #999999;
+.jqplot-yMidAxis-tick {
+    text-align: center;
 }
 
-.jqplot-meterGauge-label {
-    font-size: 1em;
-    color: #999999;
-}
 .jqplot-xaxis-label {
     margin-top: 10px;
     font-size: 11pt;
     position: absolute;
 }
 
+.jqplot-meterGauge-tick {
+    font-size: 0.75em;
+    color: #999999;
+}
+
+.jqplot-meterGauge-label {
+    font-size: 1em;
+    color: #999999;
+}
+
 table.jqplot-table-legend {
     margin-top: 12px;
     margin-bottom: 12px;
 }
       
 td.jqplot-cursor-legend-swatch {
-vertical-align:middle;
-text-align:center;
+    vertical-align: middle;
+    text-align: center;
 }
 
 div.jqplot-cursor-legend-swatch {
-width:1.2em;
-height:0.7em;
+    width: 1.2em;
+    height: 0.7em;
 }
 
 .jqplot-error {

File src/plugins/jqplot.cursor.js

         var plot = ev.data.plot;
         var go = plot.eventCanvas._elem.offset();
         var gridPos = {x:ev.pageX - go.left, y:ev.pageY - go.top};
-        var dataPos = {xaxis:null, yaxis:null, x2axis:null, y2axis:null, y3axis:null, y4axis:null, y5axis:null, y6axis:null, y7axis:null, y8axis:null, y9axis:null};
-        var an = ['xaxis', 'yaxis', 'x2axis', 'y2axis', 'y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis'];
+        //////
+        // TO DO: handle yMidAxis
+        //////
+        var dataPos = {xaxis:null, yaxis:null, x2axis:null, y2axis:null, y3axis:null, y4axis:null, y5axis:null, y6axis:null, y7axis:null, y8axis:null, y9axis:null, yMidAxis:null};
+        var an = ['xaxis', 'yaxis', 'x2axis', 'y2axis', 'y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis', 'yMidAxis'];
         var ax = plot.axes;
         var n, axis;
         for (n=11; n>0; n--) {

File src/plugins/jqplot.pyramidAxisRenderer.js

         // Position of axis.  Values are: top, bottom , left, center, right.
         // By default, x and x2 axes are bottom, y axis is center.
         this.position = null;
+        // prop: drawBaseline
+        // True to draw the axis baseline.
+        this.drawBaseline = true;
+        this.baselineWidth = null;
+        this.baselineColor = null;
         this._type = 'pyramid';
+        this._splitAxis = false;
+        this._splitLength = null;
 
         if (this.name.charAt(0) === 'x') {
             this.position = 'bottom';
         var db = this._dataBounds;
         db.min = null;
         db.max = null;
-        var tempxmin;
-        var tempxmax;
-        var tempy;
+        var temp;
         for (var i=0; i<this._series.length; i++) {
             var s = this._series[i];
             var d = s._plotData;
             
-            for (var j=0; j<d.length; j++) { 
+            for (var j=0, l=d.length; j<l; j++) { 
                 if (this.name.charAt(0) === 'x') {
-                    tempxmin = Math.min(d[j][1], d[j][2]);
-                    tempxmax = Math.max(d[j][1], d[j][2]);
-                    if ((tempxmin !== null && tempxmin < db.min) || db.min === null) {
-                        db.min = tempxmin;
+                    temp = d[j][1];
+                    if ((temp !== null && temp < db.min) || db.min === null) {
+                        db.min = temp;
                     }
-                    if ((tempxmax !== null && tempxmax > db.max) || db.max === null) {
-                        db.max = tempxmax;
+                    if ((temp !== null && temp > db.max) || db.max === null) {
+                        db.max = temp;
                     }
                 }              
                 else {
-                    tempy = d[j][0];
-                    if ((tempy !== null && tempy < db.min) || db.min === null) {
-                        db.min = tempy;
+                    temp = d[j][0];
+                    if ((temp !== null && temp < db.min) || db.min === null) {
+                        db.min = temp;
                     }
-                    if ((tempy !== null && tempy > db.max) || db.max === null) {
-                        db.max = tempy;
+                    if ((temp !== null && temp > db.max) || db.max === null) {
+                        db.max = temp;
                     }
                 }              
             }
             // populate the axis label and value properties.
             // createTicks is a method on the renderer, but
             // call it within the scope of the axis.
-            this.renderer.createTicks.call(this);
+            this.renderer.createTicks.call(this, plot);
             // 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.
         return this._elem;
     };   
 
+    // Note, primes can be found on http://primes.utm.edu/
+    var _primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997];
+
+    var _primesHash = {};
+
+    for (var i =0, l = _primes.length; i < l; i++) {
+        _primesHash[_primes[i]] = _primes[i];
+    }
+
     // called with scope of axis
-    $.jqplot.PyramidAxisRenderer.prototype.createTicks = function() {
+    $.jqplot.PyramidAxisRenderer.prototype.createTicks = function(plot) {
         // we're are operating on an axis here
         var ticks = this._ticks;
         var userTicks = this.ticks;
         // databounds were set on axis initialization.
         var db = this._dataBounds;
-        var dim, interval;
-        var min, max;
-        var pos1, pos2;
-        var tt, i;
+        var dim;
+        var interval;
+        var min;
+        var max;
+        var range;
+        var pos1;
+        var pos2;
+        var tt;
+        var i;
+        var s;
         // get a copy of user's settings for min/max.
         var userMin = this.min;
         var userMax = this.max;
+        var ut;
+        var t;
+        var threshold;
+        var tdim;
+        var scalefact;
+        var ret;
+        var tumin;
+        var tumax;
+        var maxVisibleTicks;
         
         // if we already have ticks, use them.
         // ticks must be in order of increasing value.
+
+        // if (this.name === 'yMidAxis') {
+        //     this.tickOptions._styles = {position: 'relative'};
+        // }
         
         if (userTicks.length) {
             // ticks could be 1D or 2D array of [val, val, ,,,] or [[val, label], [val, label], ...] or mixed
             for (i=0, l=userTicks.length; i<l; i++){
-                var ut = userTicks[i];
-                var t = new this.tickRenderer(this.tickOptions);
+                ut = userTicks[i];
+                t = new this.tickRenderer(this.tickOptions);
                 if ($.isArray(ut)) {
                     t.value = ut[0];
                     t.label = ut[1];
         else {
             if (this.name.charAt(0) === 'x') {
                 dim = this._plotDimensions.width;
+                // make sure x axis is symetric about 0.
+                var tempmax = Math.max(db.max, Math.abs(db.min));
+                var tempmin = Math.min(db.min, -tempmax);
+                min = ((this.min != null) ? this.min : tempmin);
+                max = ((this.max != null) ? this.max : tempmax);
+                range = max - min;
+
+                threshold = 30;
+                tdim = Math.max(dim, threshold+1);
+                scalefact =  (tdim-threshold)/300.0;
+                ret = $.jqplot.LinearTickGenerator(min, max, scalefact); 
+                // calculate a padded max and min, points should be less than these
+                // so that they aren't too close to the edges of the plot.
+                // User can adjust how much padding is allowed with pad, padMin and PadMax options. 
+                tumin = min + range*(this.padMin - 1);
+                tumax = max - range*(this.padMax - 1);
+
+                if (min <=tumin || max >= tumax) {
+                    tumin = min - range*(this.padMin - 1);
+                    tumax = max + range*(this.padMax - 1);
+                    ret = $.jqplot.LinearTickGenerator(tumin, tumax, scalefact);
+                }
+
+                this.min = ret[0];
+                this.max = ret[1];
+                this.numberTicks = ret[2];
+                this._autoFormatString = ret[3];
+                this.tickInterval = ret[4];
             }
             else {
                 dim = this._plotDimensions.height;
-            }
 
-            // ticks will be on whole integers like 1, 2, 3, ... or 1, 4, 7, ...
-            var min = this._dataBounds.min;
-            var max = this._dataBounds.max;
-            var s = this._series[0];
-            this.ticks = [];
+                // ticks will be on whole integers like 1, 2, 3, ... or 1, 4, 7, ...
+                min = db.min;
+                max = db.max;
+                s = this._series[0];
+                this.ticks = [];
 
-            var range = max - min;
+                range = max - min;
 
-            // if range is a prime, will get only 2 ticks, expand range in that case.
-            var primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997];
-            if ($.inArray(range, primes) > -1) {
-                range += 1;
-                max += 1;
-            }
+                // if range is a prime, will get only 2 ticks, expand range in that case.
+                if (_primesHash[range]) {
+                    range += 1;
+                    max += 1;
+                }
 
-            this.max = max;
-            this.min = min;
-            
-            var maxVisibleTicks = Math.round(2 + Math.pow((dim/35), 0.8));
+                this.max = max;
+                this.min = min;
+                
+                maxVisibleTicks = Math.round(2.0 + dim/55);
 
-            if (range + 1 <= maxVisibleTicks) {
-                this.numberTicks = range + 1;
-                this.tickInterval = 1.0;
-            }
+                if (range + 1 <= maxVisibleTicks) {
+                    this.numberTicks = range + 1;
+                    this.tickInterval = 1.0;
+                }
 
-            else {
-                // figure out a round number of ticks to skip in every interval
-                // range / ti + 1 = nt
-                // ti = range / (nt - 1)
-                for (var i=maxVisibleTicks; i>1; i--) {
-                    if (range/(i - 1) === Math.round(range/(i - 1))) {
-                        this.numberTicks = i;
-                        this.tickInterval = range/(i - 1);
-                        break;
+                else {
+                    // figure out a round number of ticks to skip in every interval
+                    // range / ti + 1 = nt
+                    // ti = range / (nt - 1)
+                    for (var i=maxVisibleTicks; i>1; i--) {
+                        if (range/(i - 1) === Math.round(range/(i - 1))) {
+                            this.numberTicks = i;
+                            this.tickInterval = range/(i - 1);
+                            break;
+                        }
+                        
                     }
-                    
                 }
             }
 
-            for (var i=0; i<this.numberTicks; i++) {
+            var labelval;
+            for (i=0; i<this.numberTicks; i++) {
                 this.tickOptions.axis = this.name;
-                this.tickOptions.label = String (this.min + this.tickInterval * i);
+                labelval = this.min + this.tickInterval * i;
+                if (this.name.charAt(0) === 'x') {
+                    labelval = Math.abs(labelval);
+                }
+                this.tickOptions.label = String (labelval);
                 this.tickOptions.value = this.min + this.tickInterval * i;
-                var t = new this.tickRenderer(this.tickOptions);
+                t = new this.tickRenderer(this.tickOptions);
                 this._ticks.push(t);
-                t = null;
+                // for x axis, if y axis is in middle, add a symetrical 0 tick
+                if (this.name.charAt(0) === 'x' && plot.axes.yMidAxis.show && this.tickOptions.value === 0) {
+                    this._splitAxis = true;
+                    this._splitLength = plot.axes.yMidAxis.getWidth();
+                    t.value = -0.1;
+                    t = new this.tickRenderer(this.tickOptions);
+                    this._ticks.push(t);
+                    t.value = 0.1;
+                }
+            }
+            t = null;
+        }
+
+        ticks = null;
+    };
+    
+    // called with scope of axis
+    $.jqplot.PyramidAxisRenderer.prototype.set = function() { 
+        var dim = 0;
+        var temp;
+        var w = 0;
+        var h = 0;
+        var i;
+        var t;
+        var tick;
+        var lshow = (this._label == null) ? false : this._label.show;
+        if (this.show) {
+            t = this._ticks;
+            l = t.length;
+            tick;
+            for (i=0; i<l; i++) {
+                tick = t[i];
+                if (!tick._breakTick && tick.show && tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) {
+                    if (this.name.charAt(0) === 'x') {
+                        temp = tick._elem.outerHeight(true);
+                    }
+                    else {
+                        temp = tick._elem.outerWidth(true);
+                    }
+                    if (temp > dim) {
+                        dim = temp;
+                    }
+                }
+            }
+
+            if (this.name === 'yMidAxis') {
+                for (i=0; i<l; i++) {
+                    tick = t[i];
+                    temp = (dim - tick._elem.outerWidth(true))/2.0;
+                    tick._elem.css('left', temp);
+                }
+            }
+            tick = null;
+            t = null;
+            
+            if (lshow) {
+                w = this._label._elem.outerWidth(true);
+                h = this._label._elem.outerHeight(true); 
+            }
+            if (this.name == 'xaxis') {
+                dim = dim + h;
+                this._elem.css({'height':dim+'px', left:'0px', bottom:'0px'});
+            }
+            else if (this.name == 'x2axis') {
+                dim = dim + h;
+                this._elem.css({'height':dim+'px', left:'0px', top:'0px'});
+            }
+            else if (this.name == 'yaxis') {
+                dim = dim + w;
+                this._elem.css({'width':dim+'px', left:'0px', top:'0px'});
+                if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) {
+                    this._label._elem.css('width', w+'px');
+                }
+            }
+            else {
+                dim = dim + w;
+                this._elem.css({'width':dim+'px', right:'0px', top:'0px'});
+                if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) {
+                    this._label._elem.css('width', w+'px');
+                }
+            }
+        }  
+    };
+    
+    $.jqplot.PyramidAxisRenderer.prototype.pack = function(pos, offsets) { 
+        // Add defaults for repacking from resetTickValues function.
+        pos = pos || {};
+        offsets = offsets || this._offsets;
+        
+        var ticks = this._ticks;
+        var max = this.max;
+        var min = this.min;
+        var offmax = offsets.max;
+        var offmin = offsets.min;
+        var lshow = (this._label == null) ? false : this._label.show;
+        
+        for (var p in pos) {
+            this._elem.css(p, pos[p]);
+        }
+        
+        this._offsets = offsets;
+        // pixellength will be + for x axes and - for y axes becasue pixels always measured from top left.
+        var pixellength = offmax - offmin;
+        var unitlength = max - min;
+        var sl = this._splitLength;
+        
+        // point to unit and unit to point conversions references to Plot DOM element top left corner.
+        if (this._splitAxis) {
+            pixellength -= this._splitLength;
+            
+            // don't know that this one is correct.
+            this.p2u = function(p){
+                return (p - offmin) * unitlength / pixellength + min;
+            };
+        
+            this.u2p = function(u){
+                if (u <= 0) {
+                    return (u - min) * pixellength / unitlength + offmin;
+                }
+                else {
+                    return (u - min) * pixellength / unitlength + offmin + sl;
+                }
+            };
+                
+            this.series_u2p = function(u){
+                if (u <= 0) {
+                    return (u - min) * pixellength / unitlength;
+                }
+                else {
+                    return (u - min) * pixellength / unitlength + sl;
+                }
+            };
+
+            // don't know that this one is correct.
+            this.series_p2u = function(p){
+                return p * unitlength / pixellength + min;
+            };
+        }
+        else {
+            this.p2u = function(p){
+                return (p - offmin) * unitlength / pixellength + min;
+            };
+        
+            this.u2p = function(u){
+                return (u - min) * pixellength / unitlength + offmin;
+            };
+                
+            if (this.name.charAt(0) === 'x'){
+                this.series_u2p = function(u){
+                    return (u - min) * pixellength / unitlength;
+                };
+                this.series_p2u = function(p){
+                    return p * unitlength / pixellength + min;
+                };
+            }
+        
+            else {
+                this.series_u2p = function(u){
+                    return (u - max) * pixellength / unitlength;
+                };
+                this.series_p2u = function(p){
+                    return p * unitlength / pixellength + max;
+                };
+            }
+        }
+        
+        if (this.show) {
+            if (this.name.charAt(0) === 'x') {
+                for (var i=0; i<ticks.length; i++) {
+                    var t = ticks[i];
+                    if (t.show && t.showLabel) {
+                        var shim;
+                        
+                        if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) {
+                            // will need to adjust auto positioning based on which axis this is.
+                            var temp = (this.name == 'xaxis') ? 1 : -1;
+                            switch (t.labelPosition) {
+                                case 'auto':
+                                    // position at end
+                                    if (temp * t.angle < 0) {
+                                        shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    }
+                                    // position at start
+                                    else {
+                                        shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
+                                    }
+                                    break;
+                                case 'end':
+                                    shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    break;
+                                case 'start':
+                                    shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
+                                    break;
+                                case 'middle':
+                                    shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    break;
+                                default:
+                                    shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    break;
+                            }
+                        }
+                        else {
+                            shim = -t.getWidth()/2;
+                        }
+                        var val = this.u2p(t.value) + shim + 'px';
+                        t._elem.css('left', val);
+                        t.pack();
+                    }
+                }
+                if (lshow) {
+                    var w = this._label._elem.outerWidth(true);
+                    this._label._elem.css('left', offmin + pixellength/2 - w/2 + 'px');
+                    if (this.name == 'xaxis') {
+                        this._label._elem.css('bottom', '0px');
+                    }
+                    else {
+                        this._label._elem.css('top', '0px');
+                    }
+                    this._label.pack();
+                }
+            }
+            else {
+                for (var i=0; i<ticks.length; i++) {
+                    var t = ticks[i];
+                    if (t.show && t.showLabel) {                        
+                        var shim;
+                        if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) {
+                            // will need to adjust auto positioning based on which axis this is.
+                            var temp = (this.name == 'yaxis') ? 1 : -1;
+                            switch (t.labelPosition) {
+                                case 'auto':
+                                    // position at end
+                                case 'end':
+                                    if (temp * t.angle < 0) {
+                                        shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2;
+                                    }
+                                    else {
+                                        shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2;
+                                    }
+                                    break;
+                                case 'start':
+                                    if (t.angle > 0) {
+                                        shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2;
+                                    }
+                                    else {
+                                        shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2;
+                                    }
+                                    break;
+                                case 'middle':
+                                    // if (t.angle > 0) {
+                                    //     shim = -t.getHeight()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    // }
+                                    // else {
+                                    //     shim = -t.getHeight()/2 - t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
+                                    // }
+                                    shim = -t.getHeight()/2;
+                                    break;
+                                default:
+                                    shim = -t.getHeight()/2;
+                                    break;
+                            }
+                        }
+                        else {
+                            shim = -t.getHeight()/2;
+                        }
+                        
+                        var val = this.u2p(t.value) + shim + 'px';
+                        t._elem.css('top', val);
+                        t.pack();
+                    }
+                }
+                if (lshow) {
+                    var h = this._label._elem.outerHeight(true);
+                    this._label._elem.css('top', offmax - pixellength/2 - h/2 + 'px');
+                    if (this.name == 'yaxis') {
+                        this._label._elem.css('left', '0px');
+                    }
+                    else {
+                        this._label._elem.css('right', '0px');
+                    }   
+                    this._label.pack();
+                }
             }
         }
 
         ticks = null;
-    };
+    }
 })(jQuery);

File src/plugins/jqplot.pyramidRenderer.js

         if (options.highlightMouseDown && options.highlightMouseOver == null) {
             options.highlightMouseOver = false;
         }
+
+        this._positiveSeries = true;
         
         $.extend(true, this, options);
+
+        // if (this.fill === false) {
+        //     this.shadow = false;
+        // }
         
         this.renderer.options = options;
         // index of the currenty highlighted point, if any
         this._barPoints = [];
         this.fillAxis = 'y';
         this._primaryAxis = '_yaxis';
+        this._xnudge = 0;
         
         // set the shape renderer options
-        var opts = {lineJoin:'miter', lineCap:'round', fill:this.fill, fillRect:this.fill, isarc:false, strokeStyle:this.color, fillStyle:this.color, closePath:this.fill};
+        var opts = {lineJoin:'miter', lineCap:'round', fill:this.fill, fillRect:this.fill, isarc:false, strokeStyle:this.color, fillStyle:this.color, closePath:this.fill, lineWidth: this.lineWidth};
         this.renderer.shapeRenderer.init(opts);
         // set the shadow renderer options
-        var sopts = {lineJoin:'miter', lineCap:'round', fill:this.fill, fillRect:this.fill, isarc:false, angle:this.shadowAngle, offset:this.shadowOffset, alpha:this.shadowAlpha, depth:this.shadowDepth, closePath:this.fill};
-        this.renderer.shadowRenderer.init(sopts);
-        
-        // set highlight colors if none provided
-        if (this.highlightColors.length === 0) {
-            for (var i=0, l=this.seriesColors.length; i<l; i++){
-                var rgba = $.jqplot.getColorComponents(this.seriesColors[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);
-                }
-                this.highlightColors.push('rgb('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+')');
+        var shadow_offset = options.shadowOffset;
+        // set the shadow renderer options
+        if (shadow_offset == null) {
+            // scale the shadowOffset to the width of the line.
+            if (this.lineWidth > 2.5) {
+                shadow_offset = 1.25 * (1 + (Math.atan((this.lineWidth/2.5))/0.785398163 - 1)*0.6);
+                // var shadow_offset = this.shadowOffset;
+            }
+            // for skinny lines, don't make such a big shadow.
+            else {
+                shadow_offset = 1.25 * Math.atan((this.lineWidth/2.5))/0.785398163;
             }
         }
-        
-        this.highlightColorGenerator = new $.jqplot.ColorGenerator(this.highlightColors);
+        var sopts = {lineJoin:'miter', lineCap:'round', fill:this.fill, fillRect:this.fill, isarc:false, angle:this.shadowAngle, offset:shadow_offset, alpha:this.shadowAlpha, depth:this.shadowDepth, closePath:this.fill, lineWidth: this.lineWidth};
+        this.renderer.shadowRenderer.init(sopts);
+
+        plot.postDrawHooks.addOnce(postPlotDraw);
+        plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove);
     };
     
     // setGridData
         var pdata = this._prevPlotData;
         this.gridData = [];
         this._prevGridData = [];
+        var l = data.length;
+        var adjust = false;
+        var i;
 
-        var hasNull = false;
-        for (var i=0, l=this.data.length; i < l; i++) {
+        // if any data values are < 0,  consider this a negative series
+        for (i = 0; i < l; i++) {
+            if (data[i][1] < 0) {
+                this._positiveSeries = false;
+            }
+        }
+
+        if (this._yaxis.name === 'yMidAxis' && this._positiveSeries) {
+            this._xnudge = this._xaxis.max/1000.0;
+            adjust = true;
+        }
+
+        for (i = 0; i < l; i++) {
             // if not a line series or if no nulls in data, push the converted point onto the array.
             if (data[i][0] != null && data[i][1] != null) {
                 this.gridData.push([xp(data[i][1]), yp(data[i][0])]);
             }
             // else if there is a null, preserve it.
             else if (data[i][0] == null) {
-                hasNull = true;
                 this.gridData.push([xp(data[i][1]), null]);
             }
             else if (data[i][1] == null) {
-                hasNull = true;
                 this.gridData.push(null, [yp(data[i][0])]);
             }
-            // if not a line series or if no nulls in data, push the converted point onto the array.
-            // if (pdata[i] != null && pdata[i][0] != null && pdata[i][1] != null) {
-            //     this._prevGridData.push([xp.call(this._xaxis, pdata[i][0]), yp.call(this._yaxis, pdata[i][1])]);
-            // }
-            // // else if there is a null, preserve it.
-            // else if (pdata[i] != null && pdata[i][0] == null) {
-            //     this._prevGridData.push([null, yp.call(this._yaxis, pdata[i][1])]);
-            // }  
-            // else if (pdata[i] != null && pdata[i][0] != null && pdata[i][1] == null) {
-            //     this._prevGridData.push([xp.call(this._xaxis, pdata[i][0]), null]);
-            // }
+            // finally, adjust x grid data if have to
+            if (data[i][1] === 0 && adjust) {
+                this.gridData[i][0] = xp(this._xnudge);
+            }
         }
     };
     
         var xp = this._xaxis.series_u2p;
         var yp = this._yaxis.series_u2p;
         var gd = [];
-        var pgd = [];
-        this.renderer._smoothedData = [];
-        this.renderer._smoothedPlotData = [];
-        this.renderer._hiBandGridData = [];
-        this.renderer._lowBandGridData = [];
-        this.renderer._hiBandSmoothedData = [];
-        this.renderer._lowBandSmoothedData = [];
-        var bands = this.renderer.bands;
-        var hasNull = false;
-        for (var i=0; i<data.length; i++) {
+        var l = data.length;
+        var adjust = false;
+        var i;
+
+        // if any data values are < 0,  consider this a negative series
+        for (i = 0; i < l; i++) {
+            if (data[i][1] < 0) {
+                this._positiveSeries = false;
+            }
+        }
+
+        if (this._yaxis.name === 'yMidAxis' && this._positiveSeries) {
+            this._xnudge = this._xaxis.max/1000.0;
+            adjust = true;
+        }
+
+        for (i = 0; i < l; i++) {
             // if not a line series or if no nulls in data, push the converted point onto the array.
             if (data[i][0] != null && data[i][1] != null) {
                 gd.push([xp(data[i][1]), yp(data[i][0])]);
             }
             // else if there is a null, preserve it.
             else if (data[i][0] == null) {
-                hasNull = true;
                 gd.push([xp(data[i][1]), null]);
             }
             else if (data[i][1] == null) {
-                hasNull = true;
                 gd.push([null, yp(data[i][0])]);
             }
+            // finally, adjust x grid data if have to
+            if (data[i][1] === 0 && adjust) {
+                gd[i][0] = xp(this._xnudge);
+            }
         }
+
         return gd;
     };
 
             this.barWidth = Math.round((paxis._offsets.max - paxis._offsets.min) / nvals - this.barPadding + 0.4);
         }
         else {
-            this.barWidth = Math.round((paxis._offsets.min - paxis._offsets.max) / nvals - this.barPadding + 0.4);
+            if (this.fill) {
+                var fact = (paxis._offsets.min - paxis._offsets.max) / nvals - this.barPadding;
+            }
+            else {
+                var fact = (paxis._offsets.min - paxis._offsets.max) / nvals;
+            }
+            this.barWidth = Math.round(fact + 0.4);
         }
     };
     
         var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow;
         var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine;
         var fill = (opts.fill != undefined) ? opts.fill : this.fill;
-        var xaxis = this.xaxis;
-        var yaxis = this.yaxis;
         var xp = this._xaxis.series_u2p;
         var yp = this._yaxis.series_u2p;
         var pointx, pointy;
         this._dataColors = [];
         this._barPoints = [];
         
-        if (this.barWidth == null) {
+        if (this.renderer.options.barWidth == null) {
             this.renderer.setBarWidth.call(this);
         }
         
             }
             var positiveColor = opts.fillStyle;
             var base;
-            var xstart; 
+            var xstart = this._xaxis.series_u2p(this._xnudge);; 
             var ystart;
+            var bw2 = this.barWidth/2.0;
             
-            for (var i=0; i<gridData.length; i++) {
+            for (var i=0, l=gridData.length; i<l; i++) {
                 if (this.data[i][0] == null) {
                     continue;
                 }
                 points = [];
                 base = gridData[i][1];
-                xstart;
                 // not stacked and first series in stack
 
-                xstart = this._xaxis.series_u2p(0);
-
                 if (this._plotData[i][1] < 0) {
                     if (this.varyBarColor && !this._stack) {
                         if (this.useNegativeColors) {
                     }                    
                 }
                 
+                if (this.fill) {
+                    if (this._plotData[i][1] >= 0) {
+                        // xstart = this._xaxis.series_u2p(this._xnudge);
+                        w = gridData[i][0] - xstart;
+                        h = this.barWidth;
+                        points = [xstart, base - bw2, w, h];
+                    }
+                    else {
+                        // xstart = this._xaxis.series_u2p(0);
+                        w = xstart - gridData[i][0];;
+                        h = this.barWidth;
+                        points = [gridData[i][0], base - bw2, w, h];
+                    }
 
-                if (this._plotData[i][1] >= 0) {
-                    // points.push([xstart, base + this.barWidth / 2]);
-                    // points.push([xstart, base - this.barWidth / 2]);
-                    // points.push([gridData[i][1], base - this.barWidth / 2]);
-                    // points.push([gridData[i][1], base + this.barWidth / 2]);
-                    w = gridData[i][0] - xstart;
-                    h = this.barWidth;
-                    points = [xstart, base - this.barWidth/2, w, h];
-                }
-                else {
-                    // points.push([gridData[i][1], base + this.barWidth / 2]);
-                    // points.push([gridData[i][1], base - this.barWidth / 2]);
-                    // points.push([xstart, base - this.barWidth / 2]);
-                    // points.push([xstart, base + this.barWidth / 2]);
-                    w = xstart - gridData[i][0];;
-                    h = this.barWidth;
-                    points = [gridData[i][0], base - this.barWidth/2, w, h];
+                    this._barPoints.push([[points[0], points[1] + h], [points[0], points[1]], [points[0] + w, points[1]], [points[0] + w, points[1] + h]]);
                 }
 
-                this._barPoints.push([[points[0], points[1] + h], [points[0], points[1]], [points[0] + w, points[1]], [points[0] + w, points[1] + h]]);
+                else {
+                    if (i === 0) {
+                        points = [[xstart, gridData[i][1]], [gridData[i][0], gridData[i][1]], [gridData[i][0], gridData[i][1] - bw2]];
+                    }
+                    else if (i === l-1) {
+                        points = [[gridData[i-1][0], gridData[i-1][1] - bw2], [gridData[i][0], gridData[i][1] + bw2], [gridData[i][0], gridData[i][1]], [xstart, gridData[i][1]]];
+                    }
+                    else {
+                        points = [[gridData[i-1][0], gridData[i-1][1] - bw2], [gridData[i][0], gridData[i][1] + bw2], [gridData[i][0], gridData[i][1] - bw2]];
+                    }
+                }
 
                 if (shadow) {
-                    var sopts = $.extend(true, {}, opts);
-                    delete sopts.fillStyle;
-                    this.renderer.shadowRenderer.draw(ctx, points, sopts);
+                    // var sopts = $.extend(true, {}, opts);
+                    // delete sopts.fillStyle;
+                    this.renderer.shadowRenderer.draw(ctx, points);
                 }
                 var clr = opts.fillStyle || this.color;
                 this._dataColors.push(clr);
         }
         
         else if (typeof(this.highlightColors) == 'string') {
-            var temp = this.highlightColors;
             this.highlightColors = [];
             for (var i=0; i<this._dataColors.length; i++) {
-                this.highlightColors.push(temp);
+                this.highlightColors.push(this.highlightColors);
             }
         }
         
     function preInit(target, data, options) {
         options = options || {};
         options.axesDefaults = options.axesDefaults || {};
-        options.axes = options.axes || {};
-        options.axes.yaxis = options.axes.yaxis || {};
+        options.grid = options.grid || {};
         options.legend = options.legend || {};
         options.seriesDefaults = options.seriesDefaults || {};
         // only set these if there is a pie series
         }
         
         if (setopts) {
-            options.axes.yaxis.renderer = $.jqplot.PyramidAxisRenderer;
+            options.axesDefaults.renderer = $.jqplot.PyramidAxisRenderer;
+            options.grid.renderer = $.jqplot.PyramidGridRenderer;
             options.legend.show = false;
             options.seriesDefaults.pointLabels = {show: false};
         }
     }
+    
+    // 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() {
+        // Memory Leaks patch    
+        if (this.plugins.pyramidRenderer && this.plugins.pyramidRenderer.highlightCanvas) {
+
+            this.plugins.pyramidRenderer.highlightCanvas.resetCanvas();
+            this.plugins.pyramidRenderer.highlightCanvas = null;
+        }
+         
+        this.plugins.pyramidRenderer = {highlightedSeriesIndex:null};
+        this.plugins.pyramidRenderer.highlightCanvas = new $.jqplot.GenericCanvas();
+        
+        this.eventCanvas._elem.before(this.plugins.pyramidRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-pyramidRenderer-highlight-canvas', this._plotDimensions, this));
+        this.plugins.pyramidRenderer.highlightCanvas.setContext();
+        this.eventCanvas._elem.bind('mouseleave', {plot:this}, function (ev) { unhighlight(ev.data.plot); });
+    }  
+    
+    function highlight (plot, sidx, pidx, points) {
+        var s = plot.series[sidx];
+        var canvas = plot.plugins.pyramidRenderer.highlightCanvas;
+        canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height);
+        s._highlightedPoint = pidx;
+        plot.plugins.pyramidRenderer.highlightedSeriesIndex = sidx;
+        var opts = {fillStyle: s.highlightColors[pidx], fillRect: false};
+        s.renderer.shapeRenderer.draw(canvas._ctx, points, opts);
+        canvas = null;
+    }
+    
+    function unhighlight (plot) {
+        var canvas = plot.plugins.pyramidRenderer.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.pyramidRenderer.highlightedSeriesIndex = null;
+        plot.target.trigger('jqplotDataUnhighlight');
+        canvas =  null;
+    }
+    
+    
+    function handleMove(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
+            var evt1 = jQuery.Event('jqplotDataMouseOver');
+            evt1.pageX = ev.pageX;
+            evt1.pageY = ev.pageY;
+            plot.target.trigger(evt1, ins);
+            if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.pyramidRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) {
+                var evt = jQuery.Event('jqplotDataHighlight');
+                evt.pageX = ev.pageX;
+                evt.pageY = ev.pageY;
+                plot.target.trigger(evt, ins);
+                highlight (plot, neighbor.seriesIndex, neighbor.pointIndex, neighbor.points);
+            }
+        }
+        else if (neighbor == null) {
+            unhighlight (plot);
+        }
+    }
 
     // Have to add hook here, becuase it needs called before series is inited.
     $.jqplot.preInitHooks.push(preInit);