diff --git a/example.html b/example.html index 5d211b6..916bb16 100644 --- a/example.html +++ b/example.html @@ -83,7 +83,7 @@ xkey: 'period', ykeys: ['licensed', 'sorned'], labels: ['Licensed', 'SORN'], - lineDrawer: 'straight' + smooth: false }); }); diff --git a/morris.coffee b/morris.coffee index 79cbd73..4a3148f 100644 --- a/morris.coffee +++ b/morris.coffee @@ -50,7 +50,7 @@ class Morris.Line hoverOpacity: 0.95 hoverLabelColor: '#444' hoverFontSize: 12 - lineDrawer: 'spline' + smooth: true # Do any necessary pre-processing for a new dataset # @@ -142,10 +142,7 @@ class Morris.Line for i in [seriesCoords.length-1..0] coords = seriesCoords[i] if coords.length > 1 - if @options.lineDrawer == 'straight' - path = @createStraightPath coords, @options.marginTop, left, @options.marginTop + height, left + width - else - path = @createPath coords, @options.marginTop, left, @options.marginTop + height, left + width + path = @createPath coords, @options.marginTop, left, @options.marginTop + height, left + width @r.path(path) .attr('stroke', @options.lineColors[i]) .attr('stroke-width', @options.lineWidth) @@ -242,33 +239,24 @@ class Morris.Line # createPath: (coords, top, left, bottom, right) -> path = "" - grads = @gradients coords - for i in [0..coords.length-1] - c = coords[i] - if i is 0 - path += "M#{c.x},#{c.y}" - else - g = grads[i] - lc = coords[i - 1] - lg = grads[i - 1] - ix = (c.x - lc.x) / 4 - x1 = lc.x + ix - y1 = Math.min(bottom, lc.y + ix * lg) - x2 = c.x - ix - y2 = Math.min(bottom, c.y - ix * g) - path += "C#{x1},#{y1},#{x2},#{y2},#{c.x},#{c.y}" - return path - - # creates a straight path for a data series - # - createStraightPath: (coords, top, left, bottom, right) -> - path = "" - for i in [0..coords.length-1] - c = coords[i] - if i is 0 - path += "M#{c.x},#{c.y}" - else - path += "L#{c.x},#{c.y}" + if @options.smooth + grads = @gradients coords + for i in [0..coords.length-1] + c = coords[i] + if i is 0 + path += "M#{c.x},#{c.y}" + else + g = grads[i] + lc = coords[i - 1] + lg = grads[i - 1] + ix = (c.x - lc.x) / 4 + x1 = lc.x + ix + y1 = Math.min(bottom, lc.y + ix * lg) + x2 = c.x - ix + y2 = Math.min(bottom, c.y - ix * g) + path += "C#{x1},#{y1},#{x2},#{y2},#{c.x},#{c.y}" + else + path = "M" + $.map(coords, (c) -> "#{c.x},#{c.y}").join("L") return path # calculate a gradient at each point for a series of points diff --git a/morris.js b/morris.js index 5c47f00..230e0f0 100644 --- a/morris.js +++ b/morris.js @@ -8,7 +8,7 @@ function Line(options) { if (!(this instanceof Morris.Line)) return new Morris.Line(options); this.el = $(document.getElementById(options.element)); - this.options = $.extend($.extend({}, this.defaults), options); + this.options = $.extend({}, this.defaults, options); if (this.options.data === void 0 || this.options.data.length === 0) return; this.el.addClass('graph-initialised'); this.precalc(); @@ -37,7 +37,8 @@ hoverBorderWidth: 2, hoverOpacity: 0.95, hoverLabelColor: '#444', - hoverFontSize: 12 + hoverFontSize: 12, + smooth: true }; Line.prototype.precalc = function() { @@ -250,22 +251,28 @@ Line.prototype.createPath = function(coords, top, left, bottom, right) { var c, g, grads, i, ix, lc, lg, path, x1, x2, y1, y2, _ref; path = ""; - grads = this.gradients(coords); - for (i = 0, _ref = coords.length - 1; 0 <= _ref ? i <= _ref : i >= _ref; 0 <= _ref ? i++ : i--) { - c = coords[i]; - if (i === 0) { - path += "M" + c.x + "," + c.y; - } else { - g = grads[i]; - lc = coords[i - 1]; - lg = grads[i - 1]; - ix = (c.x - lc.x) / 4; - x1 = lc.x + ix; - y1 = Math.min(bottom, lc.y + ix * lg); - x2 = c.x - ix; - y2 = Math.min(bottom, c.y - ix * g); - path += "C" + x1 + "," + y1 + "," + x2 + "," + y2 + "," + c.x + "," + c.y; + if (this.options.smooth) { + grads = this.gradients(coords); + for (i = 0, _ref = coords.length - 1; 0 <= _ref ? i <= _ref : i >= _ref; 0 <= _ref ? i++ : i--) { + c = coords[i]; + if (i === 0) { + path += "M" + c.x + "," + c.y; + } else { + g = grads[i]; + lc = coords[i - 1]; + lg = grads[i - 1]; + ix = (c.x - lc.x) / 4; + x1 = lc.x + ix; + y1 = Math.min(bottom, lc.y + ix * lg); + x2 = c.x - ix; + y2 = Math.min(bottom, c.y - ix * g); + path += "C" + x1 + "," + y1 + "," + x2 + "," + y2 + "," + c.x + "," + c.y; + } } + } else { + path = "M" + $.map(coords, function(c) { + return "" + c.x + "," + c.y; + }).join("L"); } return path; }; diff --git a/morris.min.js b/morris.min.js index 3e49943..0c51447 100644 --- a/morris.min.js +++ b/morris.min.js @@ -1 +1 @@ -((function(){var a;a={},a.Line=function(){function b(b){if(!(this instanceof a.Line))return new a.Line(b);this.el=$(document.getElementById(b.element)),this.options=$.extend($.extend({},this.defaults),b);if(this.options.data===void 0||this.options.data.length===0)return;this.el.addClass("graph-initialised"),this.precalc(),this.redraw()}return b.prototype.defaults={lineWidth:3,pointSize:4,lineColors:["#0b62a4","#7A92A3","#4da74d","#afd8f8","#edc240","#cb4b4b","#9440ed"],ymax:"auto",marginTop:25,marginRight:25,marginBottom:30,marginLeft:25,numLines:5,gridLineColor:"#aaa",gridTextColor:"#888",gridTextSize:12,gridStrokeWidth:.5,hoverPaddingX:10,hoverPaddingY:5,hoverMargin:10,hoverFillColor:"#fff",hoverBorderColor:"#ccc",hoverBorderWidth:2,hoverOpacity:.95,hoverLabelColor:"#444",hoverFontSize:12},b.prototype.precalc=function(){var a,b,c,d,e,f=this;this.columnLabels=$.map(this.options.data,function(a){return a[f.options.xkey]}),this.seriesLabels=this.options.labels,this.series=[],e=this.options.ykeys;for(c=0,d=e.length;c5?this.options.ymax=Math.max(parseInt(this.options.ymax.slice(5),10),b):this.options.ymax=b},b.prototype.redraw=function(){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z=this;this.el.empty(),this.r=new Raphael(this.el[0]),q=this.measureText(this.options.ymax,this.options.gridTextSize).width+this.options.marginLeft,G=this.el.width()-q-this.options.marginRight,g=this.el.height()-this.options.marginTop-this.options.marginBottom,e=G/(this.xmax-this.xmin),f=g/this.options.ymax,B=function(a){return Z.xvals.length===1?q+G/2:q+(a-Z.xmin)*e},C=function(a){return Z.options.marginTop+g-a*f},r=g/(this.options.numLines-1);for(n=0,R=this.options.numLines-1;0<=R?n<=R:n>=R;0<=R?n++:n--)K=this.options.marginTop+n*r,F=Math.round((this.options.numLines-1-n)*this.options.ymax/(this.options.numLines-1)),this.r.text(q-this.options.marginLeft/2,K,F).attr("font-size",this.options.gridTextSize).attr("fill",this.options.gridTextColor).attr("text-anchor","end"),this.r.path("M"+q+","+K+"H"+(q+G)).attr("stroke",this.options.gridLineColor).attr("stroke-width",this.options.gridStrokeWidth);w=null,J=50;for(n=S=Math.ceil(this.xmin),T=Math.floor(this.xmax);S<=T?n<=T:n>=T;S<=T?n++:n--)o=this.r.text(B(n),this.options.marginTop+g+this.options.marginBottom/2,n).attr("font-size",this.options.gridTextSize).attr("fill",this.options.gridTextColor),p=o.getBBox(),w===null||w<=p.x?w=p.x+p.width+J:o.remove();c=function(){var a,b,c,d;c=this.xvals,d=[];for(a=0,b=c.length;a=0;V<=0?n++:n--)d=y[n],d.length>1&&(s=this.createPath(d,this.options.marginTop,q,this.options.marginTop+g,q+G),this.r.path(s).attr("stroke",this.options.lineColors[n]).attr("stroke-width",this.options.lineWidth));z=function(){var a,b;b=[];for(n=0,a=y.length-1;0<=a?n<=a:n>=a;0<=a?n++:n--)b.push([]);return b}();for(n=W=y.length-1;W<=0?n<=0:n>=0;W<=0?n++:n--){X=y[n];for(O=0,Q=X.length;O=Y;0<=Y?n++:n--)L=this.r.text(0,this.options.hoverFontSize*1.5*(n+1.5)-k/2,"").attr("fill",this.options.lineColors[n]).attr("font-size",this.options.hoverFontSize),M.push(L),m.push(L);return E=function(a){var b,d,e,f,h;m.show(),I.attr("text",Z.columnLabels[a]);for(b=0,h=Z.series.length-1;0<=h?b<=h:b>=h;0<=h?b++:b--)M[b].attr("text",""+Z.seriesLabels[b]+": "+Z.commas(Z.series[b][a]));return d=Math.max.apply(null,$.map(M,function(a){return a.getBBox().width})),d=Math.max(d,I.getBBox().width),j.attr("width",d+Z.options.hoverPaddingX*2),j.attr("x",-Z.options.hoverPaddingX-d/2),f=Math.min.apply(null,$.map(Z.series,function(b){return C(b[a])})),f>k+Z.options.hoverPaddingY*2+Z.options.hoverMargin+Z.options.marginTop?f=f-k/2-Z.options.hoverPaddingY-Z.options.hoverMargin:f=f+k/2+Z.options.hoverPaddingY+Z.options.hoverMargin,f=Math.max(Z.options.marginTop+k/2+Z.options.hoverPaddingY,f),f=Math.min(Z.options.marginTop+g-k/2-Z.options.hoverPaddingY,f),e=Math.min(q+G-d/2-Z.options.hoverPaddingX,c[a]),e=Math.max(q+d/2+Z.options.hoverPaddingX,e),m.attr("transform","t"+e+","+f)},h=function(){return m.hide()},l=$.map(c.slice(1),function(a,b){return(a+c[b])/2}),v=null,t=Raphael.animation({r:this.options.pointSize+3},25,"linear"),u=Raphael.animation({r:this.options.pointSize},25,"linear"),i=function(a){var b,c,d;if(v!==null&&v!==a)for(b=0,d=z.length-1;0<=d?b<=d:b>=d;0<=d?b++:b--)z[b][v].animate(u);if(a!==null&&v!==a){for(b=0,c=z.length-1;0<=c?b<=c:b>=c;0<=c?b++:b--)z[b][a].animate(t);E(a)}v=a;if(a===null)return h()},D=function(a){var b,c;a-=Z.el.offset().left;for(b=c=l.length;c<=1?b<=1:b>=1;c<=1?b++:b--)if(l[b-1]>a)break;return i(b)},this.el.mousemove(function(a){return D(a.pageX)}),A=function(a){var b;return b=a.originalEvent.touches[0]||a.originalEvent.changedTouches[0],D(b.pageX),b},this.el.bind("touchstart",A),this.el.bind("touchmove",A),this.el.bind("touchend",A),i(0)},b.prototype.createPath=function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q,r;m="",h=this.gradients(a);for(i=0,r=a.length-1;0<=r?i<=r:i>=r;0<=r?i++:i--)f=a[i],i===0?m+="M"+f.x+","+f.y:(g=h[i],k=a[i-1],l=h[i-1],j=(f.x-k.x)/4,n=k.x+j,p=Math.min(d,k.y+j*l),o=f.x-j,q=Math.min(d,f.y-j*g),m+="C"+n+","+p+","+o+","+q+","+f.x+","+f.y);return m},b.prototype.gradients=function(a){return $.map(a,function(b,c){return c===0?(a[1].y-b.y)/(a[1].x-b.x):c===a.length-1?(b.y-a[c-1].y)/(b.x-a[c-1].x):(a[c+1].y-a[c-1].y)/(a[c+1].x-a[c-1].x)})},b.prototype.measureText=function(a,b){var c,d;return b==null&&(b=12),d=this.r.text(100,100,a).attr("font-size",b),c=d.getBBox(),d.remove(),c},b.prototype.parseYear=function(a){var b,c,d,e,f,g,h,i,j,k;return g=a.toString(),c=g.match(/^(\d+) Q(\d)$/),e=g.match(/^(\d+)-(\d+)$/),f=g.match(/^(\d+)-(\d+)-(\d+)$/),c?parseInt(c[1],10)+(parseInt(c[2],10)*3-1)/12:e?parseInt(e[1],10)+(parseInt(e[2],10)-1)/12:f?(k=parseInt(f[1],10),d=parseInt(f[2],10),b=parseInt(f[3],10),h=(new Date(k,d-1,b)).getTime(),i=(new Date(k,0,1)).getTime(),j=(new Date(k+1,0,1)).getTime(),k+(h-i)/(j-i)):parseInt(a,10)},b.prototype.commas=function(a){return Math.max(0,a).toFixed(0).replace(/(?=(?:\d{3})+$)(?!^)/g,",")},b}(),window.Morris=a})).call(this); \ No newline at end of file +((function(){var a;a={},a.Line=function(){function b(b){if(!(this instanceof a.Line))return new a.Line(b);this.el=$(document.getElementById(b.element)),this.options=$.extend({},this.defaults,b);if(this.options.data===void 0||this.options.data.length===0)return;this.el.addClass("graph-initialised"),this.precalc(),this.redraw()}return b.prototype.defaults={lineWidth:3,pointSize:4,lineColors:["#0b62a4","#7A92A3","#4da74d","#afd8f8","#edc240","#cb4b4b","#9440ed"],ymax:"auto",marginTop:25,marginRight:25,marginBottom:30,marginLeft:25,numLines:5,gridLineColor:"#aaa",gridTextColor:"#888",gridTextSize:12,gridStrokeWidth:.5,hoverPaddingX:10,hoverPaddingY:5,hoverMargin:10,hoverFillColor:"#fff",hoverBorderColor:"#ccc",hoverBorderWidth:2,hoverOpacity:.95,hoverLabelColor:"#444",hoverFontSize:12,smooth:!0},b.prototype.precalc=function(){var a,b,c,d,e,f=this;this.columnLabels=$.map(this.options.data,function(a){return a[f.options.xkey]}),this.seriesLabels=this.options.labels,this.series=[],e=this.options.ykeys;for(c=0,d=e.length;c5?this.options.ymax=Math.max(parseInt(this.options.ymax.slice(5),10),b):this.options.ymax=b},b.prototype.redraw=function(){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z=this;this.el.empty(),this.r=new Raphael(this.el[0]),q=this.measureText(this.options.ymax,this.options.gridTextSize).width+this.options.marginLeft,G=this.el.width()-q-this.options.marginRight,g=this.el.height()-this.options.marginTop-this.options.marginBottom,e=G/(this.xmax-this.xmin),f=g/this.options.ymax,B=function(a){return Z.xvals.length===1?q+G/2:q+(a-Z.xmin)*e},C=function(a){return Z.options.marginTop+g-a*f},r=g/(this.options.numLines-1);for(n=0,R=this.options.numLines-1;0<=R?n<=R:n>=R;0<=R?n++:n--)K=this.options.marginTop+n*r,F=Math.round((this.options.numLines-1-n)*this.options.ymax/(this.options.numLines-1)),this.r.text(q-this.options.marginLeft/2,K,F).attr("font-size",this.options.gridTextSize).attr("fill",this.options.gridTextColor).attr("text-anchor","end"),this.r.path("M"+q+","+K+"H"+(q+G)).attr("stroke",this.options.gridLineColor).attr("stroke-width",this.options.gridStrokeWidth);w=null,J=50;for(n=S=Math.ceil(this.xmin),T=Math.floor(this.xmax);S<=T?n<=T:n>=T;S<=T?n++:n--)o=this.r.text(B(n),this.options.marginTop+g+this.options.marginBottom/2,n).attr("font-size",this.options.gridTextSize).attr("fill",this.options.gridTextColor),p=o.getBBox(),w===null||w<=p.x?w=p.x+p.width+J:o.remove();c=function(){var a,b,c,d;c=this.xvals,d=[];for(a=0,b=c.length;a=0;V<=0?n++:n--)d=y[n],d.length>1&&(s=this.createPath(d,this.options.marginTop,q,this.options.marginTop+g,q+G),this.r.path(s).attr("stroke",this.options.lineColors[n]).attr("stroke-width",this.options.lineWidth));z=function(){var a,b;b=[];for(n=0,a=y.length-1;0<=a?n<=a:n>=a;0<=a?n++:n--)b.push([]);return b}();for(n=W=y.length-1;W<=0?n<=0:n>=0;W<=0?n++:n--){X=y[n];for(O=0,Q=X.length;O=Y;0<=Y?n++:n--)L=this.r.text(0,this.options.hoverFontSize*1.5*(n+1.5)-k/2,"").attr("fill",this.options.lineColors[n]).attr("font-size",this.options.hoverFontSize),M.push(L),m.push(L);return E=function(a){var b,d,e,f,h;m.show(),I.attr("text",Z.columnLabels[a]);for(b=0,h=Z.series.length-1;0<=h?b<=h:b>=h;0<=h?b++:b--)M[b].attr("text",""+Z.seriesLabels[b]+": "+Z.commas(Z.series[b][a]));return d=Math.max.apply(null,$.map(M,function(a){return a.getBBox().width})),d=Math.max(d,I.getBBox().width),j.attr("width",d+Z.options.hoverPaddingX*2),j.attr("x",-Z.options.hoverPaddingX-d/2),f=Math.min.apply(null,$.map(Z.series,function(b){return C(b[a])})),f>k+Z.options.hoverPaddingY*2+Z.options.hoverMargin+Z.options.marginTop?f=f-k/2-Z.options.hoverPaddingY-Z.options.hoverMargin:f=f+k/2+Z.options.hoverPaddingY+Z.options.hoverMargin,f=Math.max(Z.options.marginTop+k/2+Z.options.hoverPaddingY,f),f=Math.min(Z.options.marginTop+g-k/2-Z.options.hoverPaddingY,f),e=Math.min(q+G-d/2-Z.options.hoverPaddingX,c[a]),e=Math.max(q+d/2+Z.options.hoverPaddingX,e),m.attr("transform","t"+e+","+f)},h=function(){return m.hide()},l=$.map(c.slice(1),function(a,b){return(a+c[b])/2}),v=null,t=Raphael.animation({r:this.options.pointSize+3},25,"linear"),u=Raphael.animation({r:this.options.pointSize},25,"linear"),i=function(a){var b,c,d;if(v!==null&&v!==a)for(b=0,d=z.length-1;0<=d?b<=d:b>=d;0<=d?b++:b--)z[b][v].animate(u);if(a!==null&&v!==a){for(b=0,c=z.length-1;0<=c?b<=c:b>=c;0<=c?b++:b--)z[b][a].animate(t);E(a)}v=a;if(a===null)return h()},D=function(a){var b,c;a-=Z.el.offset().left;for(b=c=l.length;c<=1?b<=1:b>=1;c<=1?b++:b--)if(l[b-1]>a)break;return i(b)},this.el.mousemove(function(a){return D(a.pageX)}),A=function(a){var b;return b=a.originalEvent.touches[0]||a.originalEvent.changedTouches[0],D(b.pageX),b},this.el.bind("touchstart",A),this.el.bind("touchmove",A),this.el.bind("touchend",A),i(0)},b.prototype.createPath=function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q,r;m="";if(this.options.smooth){h=this.gradients(a);for(i=0,r=a.length-1;0<=r?i<=r:i>=r;0<=r?i++:i--)f=a[i],i===0?m+="M"+f.x+","+f.y:(g=h[i],k=a[i-1],l=h[i-1],j=(f.x-k.x)/4,n=k.x+j,p=Math.min(d,k.y+j*l),o=f.x-j,q=Math.min(d,f.y-j*g),m+="C"+n+","+p+","+o+","+q+","+f.x+","+f.y)}else m="M"+$.map(a,function(a){return""+a.x+","+a.y}).join("L");return m},b.prototype.gradients=function(a){return $.map(a,function(b,c){return c===0?(a[1].y-b.y)/(a[1].x-b.x):c===a.length-1?(b.y-a[c-1].y)/(b.x-a[c-1].x):(a[c+1].y-a[c-1].y)/(a[c+1].x-a[c-1].x)})},b.prototype.measureText=function(a,b){var c,d;return b==null&&(b=12),d=this.r.text(100,100,a).attr("font-size",b),c=d.getBBox(),d.remove(),c},b.prototype.parseYear=function(a){var b,c,d,e,f,g,h,i,j,k;return g=a.toString(),c=g.match(/^(\d+) Q(\d)$/),e=g.match(/^(\d+)-(\d+)$/),f=g.match(/^(\d+)-(\d+)-(\d+)$/),c?parseInt(c[1],10)+(parseInt(c[2],10)*3-1)/12:e?parseInt(e[1],10)+(parseInt(e[2],10)-1)/12:f?(k=parseInt(f[1],10),d=parseInt(f[2],10),b=parseInt(f[3],10),h=(new Date(k,d-1,b)).getTime(),i=(new Date(k,0,1)).getTime(),j=(new Date(k+1,0,1)).getTime(),k+(h-i)/(j-i)):parseInt(a,10)},b.prototype.commas=function(a){return Math.max(0,a).toFixed(0).replace(/(?=(?:\d{3})+$)(?!^)/g,",")},b}(),window.Morris=a})).call(this); \ No newline at end of file