diff --git a/README.md b/README.md index 623ad80..35994b5 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,13 @@ Fork, hack, send a pull request :) ## Changelog +### 0.2.6 - 17th March 2012 + +- The X labels can be disabled (with `drawXlabels` option, default is true) +- The Y labels can be disabled (with `drawYlabels` option, default is true) +- The automatic data sort can be disabled (with `sortData` option, default is true) +- The options.data can be sorted via custom function (with `sortDataFunction` option, default is null) + ### 0.2.5 - 15th March 2012 - Raw millisecond timestamp support (with `dateFormat` option) diff --git a/examples/custom-sort.html b/examples/custom-sort.html new file mode 100644 index 0000000..54980cd --- /dev/null +++ b/examples/custom-sort.html @@ -0,0 +1,39 @@ + +
+ + + + + + + + + ++ var data = [ + {"elapsed": "I", "value": 34}, + {"elapsed": "II", "value": 24}, + {"elapsed": "III", "value": 3}, + {"elapsed": "IV", "value": 12}, + {"elapsed": "V", "value": 13}, + {"elapsed": "VI", "value": 22}, + {"elapsed": "VII", "value": 5}, + {"elapsed": "VIII", "value": 26}, + {"elapsed": "IX", "value": 12}, + {"elapsed": "X", "value": 19}, + ]; + Morris.Line({ + element: 'graph', + data: data, + xkey: 'elapsed', + ykeys: ['value'], + labels: ['value'], + parseTime: false, + sortDataFunction: function(a,b){ + return a["value"] < b["value"]; + } + }); ++ diff --git a/examples/no-xylabels.html b/examples/no-xylabels.html new file mode 100644 index 0000000..3b68357 --- /dev/null +++ b/examples/no-xylabels.html @@ -0,0 +1,38 @@ + + + + + + + + + + + +
+ var timestamp_data = [ + {"period": 1349046000000, "licensed": 3407, "sorned": 660}, + {"period": 1313103600000, "licensed": 3351, "sorned": 629}, + {"period": 1299110400000, "licensed": 3269, "sorned": 618}, + {"period": 1281222000000, "licensed": 3246, "sorned": 661}, + {"period": 1273446000000, "licensed": 3257, "sorned": 667}, + {"period": 1268524800000, "licensed": 3248, "sorned": 627}, + {"period": 1263081600000, "licensed": 3171, "sorned": 660}, + {"period": 1260403200000, "licensed": 3171, "sorned": 676}, + {"period": 1254870000000, "licensed": 3201, "sorned": 656}, + {"period": 1253833200000, "licensed": 3215, "sorned": 622} + ]; + Morris.Line({ + element: 'graph', + data: timestamp_data, + xkey: 'period', + ykeys: ['licensed', 'sorned'], + labels: ['Licensed', 'SORN'], + drawYlabel: false, + drawXlabel: false, + dateFormat: function (x) { return new Date(x).toDateString(); } + }); ++ diff --git a/morris.coffee b/morris.coffee index bc2408a..42e6f93 100644 --- a/morris.coffee +++ b/morris.coffee @@ -61,12 +61,20 @@ class Morris.Line parseTime: true units: '' dateFormat: (x) -> new Date(x).toString() + sortData: true + sortDataFunction: null + drawYlabel: true + drawXlabel: true # Do any necessary pre-processing for a new dataset # precalc: -> # sort data - @options.data.sort (a, b) => (a[@options.xkey] < b[@options.xkey]) - (b[@options.xkey] < a[@options.xkey]) + if @options.sortData + if typeof @options.sortDataFunction isnt 'function' + @options.data.sort (a,b) => (a[@options.xkey] < b[@options.xkey]) - (b[@options.xkey] < a[@options.xkey]) + else + @options.data.sort @options.sortDataFunction # extract labels @columnLabels = $.map @options.data, (d) => d[@options.xkey] @seriesLabels = @options.labels @@ -144,40 +152,43 @@ class Morris.Line for lineY in [firstY..lastY] by yInterval v = Math.floor(lineY) y = transY(v) - @r.text(left - @options.marginLeft/2, y, v + @options.units) - .attr('font-size', @options.gridTextSize) - .attr('fill', @options.gridTextColor) - .attr('text-anchor', 'end') + if @options.drawYlabels + @r.text(left - @options.marginLeft/2, y, v + @options.units) + .attr('font-size', @options.gridTextSize) + .attr('fill', @options.gridTextColor) + .attr('text-anchor', 'end') @r.path("M" + left + "," + y + 'H' + (left + width)) .attr('stroke', @options.gridLineColor) .attr('stroke-width', @options.gridStrokeWidth) ## draw x axis labels - prevLabelMargin = null - xLabelMargin = 50 # make this an option? - if @options.parseTime - x1 = new Date(@xmin).getFullYear() - x2 = new Date(@xmax).getFullYear() - else - x1 = @xmin - x2 = @xmax - for i in [x1..x2] + if @options.drawXlabel + prevLabelMargin = null + xLabelMargin = 50 # make this an option? if @options.parseTime - xpos = new Date(i, 0, 1).getTime() - if xpos < @xmin - continue + x1 = new Date(@xmin).getFullYear() + x2 = new Date(@xmax).getFullYear() else - xpos = i - labelText = if @options.parseTime then i else @columnLabels[@columnLabels.length-i-1] - label = @r.text(transX(xpos), @options.marginTop + height + @options.marginBottom / 2, labelText) - .attr('font-size', @options.gridTextSize) - .attr('fill', @options.gridTextColor) - labelBox = label.getBBox() + x1 = @xmin + x2 = @xmax + for i in [x1..x2] + if @options.parseTime + xpos = new Date(i, 0, 1).getTime() + if xpos < @xmin + continue + else + xpos = i + + labelText = if @options.parseTime then i else @columnLabels[@columnLabels.length-i-1] + label = @r.text(transX(xpos), @options.marginTop + height + @options.marginBottom / 2, labelText) + .attr('font-size', @options.gridTextSize) + .attr('fill', @options.gridTextColor) + labelBox = label.getBBox() # ensure a minimum of `xLabelMargin` pixels between labels - if prevLabelMargin is null or prevLabelMargin <= labelBox.x - prevLabelMargin = labelBox.x + labelBox.width + xLabelMargin - else - label.remove() + if prevLabelMargin is null or prevLabelMargin <= labelBox.x + prevLabelMargin = labelBox.x + labelBox.width + xLabelMargin + else + label.remove() # draw the actual series columns = (transX(x) for x in @xvals) diff --git a/morris.js b/morris.js index 72d0f4b..1fb1821 100644 --- a/morris.js +++ b/morris.js @@ -51,15 +51,25 @@ units: '', dateFormat: function(x) { return new Date(x).toString(); - } + }, + sortData: true, + sortDataFunction: null, + drawYlabel: true, + drawXlabel: true }; Line.prototype.precalc = function() { var ykey, ymax, ymin, _i, _j, _len, _ref, _ref2, _results, _this = this; - this.options.data.sort(function(a, b) { - return (a[_this.options.xkey] < b[_this.options.xkey]) - (b[_this.options.xkey] < a[_this.options.xkey]); - }); + if (this.options.sortData) { + if (typeof this.options.sortDataFunction !== 'function') { + this.options.data.sort(function(a, b) { + return (a[_this.options.xkey] < b[_this.options.xkey]) - (b[_this.options.xkey] < a[_this.options.xkey]); + }); + } else { + this.options.data.sort(this.options.sortDataFunction); + } + } this.columnLabels = $.map(this.options.data, function(d) { return d[_this.options.xkey]; }); @@ -141,32 +151,36 @@ for (lineY = firstY; firstY <= lastY ? lineY <= lastY : lineY >= lastY; lineY += yInterval) { v = Math.floor(lineY); y = transY(v); - this.r.text(left - this.options.marginLeft / 2, y, v + this.options.units).attr('font-size', this.options.gridTextSize).attr('fill', this.options.gridTextColor).attr('text-anchor', 'end'); + if (this.options.drawYlabels) { + this.r.text(left - this.options.marginLeft / 2, y, v + this.options.units).attr('font-size', this.options.gridTextSize).attr('fill', this.options.gridTextColor).attr('text-anchor', 'end'); + } this.r.path("M" + left + "," + y + 'H' + (left + width)).attr('stroke', this.options.gridLineColor).attr('stroke-width', this.options.gridStrokeWidth); } - prevLabelMargin = null; - xLabelMargin = 50; - if (this.options.parseTime) { - x1 = new Date(this.xmin).getFullYear(); - x2 = new Date(this.xmax).getFullYear(); - } else { - x1 = this.xmin; - x2 = this.xmax; - } - for (i = x1; x1 <= x2 ? i <= x2 : i >= x2; x1 <= x2 ? i++ : i--) { + if (this.options.drawXlabel) { + prevLabelMargin = null; + xLabelMargin = 50; if (this.options.parseTime) { - xpos = new Date(i, 0, 1).getTime(); - if (xpos < this.xmin) continue; + x1 = new Date(this.xmin).getFullYear(); + x2 = new Date(this.xmax).getFullYear(); } else { - xpos = i; + x1 = this.xmin; + x2 = this.xmax; } - labelText = this.options.parseTime ? i : this.columnLabels[this.columnLabels.length - i - 1]; - label = this.r.text(transX(xpos), this.options.marginTop + height + this.options.marginBottom / 2, labelText).attr('font-size', this.options.gridTextSize).attr('fill', this.options.gridTextColor); - labelBox = label.getBBox(); - if (prevLabelMargin === null || prevLabelMargin <= labelBox.x) { - prevLabelMargin = labelBox.x + labelBox.width + xLabelMargin; - } else { - label.remove(); + for (i = x1; x1 <= x2 ? i <= x2 : i >= x2; x1 <= x2 ? i++ : i--) { + if (this.options.parseTime) { + xpos = new Date(i, 0, 1).getTime(); + if (xpos < this.xmin) continue; + } else { + xpos = i; + } + labelText = this.options.parseTime ? i : this.columnLabels[this.columnLabels.length - i - 1]; + label = this.r.text(transX(xpos), this.options.marginTop + height + this.options.marginBottom / 2, labelText).attr('font-size', this.options.gridTextSize).attr('fill', this.options.gridTextColor); + labelBox = label.getBBox(); + if (prevLabelMargin === null || prevLabelMargin <= labelBox.x) { + prevLabelMargin = labelBox.x + labelBox.width + xLabelMargin; + } else { + label.remove(); + } } } columns = (function() { diff --git a/morris.min.js b/morris.min.js index cf4a66f..4d82082 100644 --- a/morris.min.js +++ b/morris.min.js @@ -1 +1 @@ -((function(){var a,b;a=jQuery,b={},b.Line=function(){function c(c){if(!(this instanceof b.Line))return new b.Line(c);typeof c.element=="string"?this.el=a(document.getElementById(c.element)):this.el=a(c.element),this.options=a.extend({},this.defaults,c);if(this.options.data===void 0||this.options.data.length===0)return;this.el.addClass("graph-initialised"),this.precalc(),this.redraw()}return c.prototype.defaults={lineWidth:3,pointSize:4,lineColors:["#0b62a4","#7A92A3","#4da74d","#afd8f8","#edc240","#cb4b4b","#9440ed"],ymax:"auto",ymin:"auto 0",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,hideHover:!1,parseTime:!0,units:"",dateFormat:function(a){return(new Date(a)).toString()}},c.prototype.precalc=function(){var b,c,d,e,f,g,h,i,j,k=this;this.options.data.sort(function(a,b){return(a[k.options.xkey]=0;i<=0?a++:a--)j.push(a);return j}.apply(this),this.columnLabels=a.map(this.columnLabels,function(a){return typeof a=="number"?k.options.dateFormat(a):a}),this.xmin=Math.min.apply(null,this.xvals),this.xmax=Math.max.apply(null,this.xvals),this.xmin===this.xmax&&(this.xmin-=1,this.xmax+=1),typeof this.options.ymax=="string"&&this.options.ymax.slice(0,4)==="auto"&&(c=Math.max.apply(null,Array.prototype.concat.apply([],this.series)),this.options.ymax.length>5?this.options.ymax=Math.max(parseInt(this.options.ymax.slice(5),10),c):this.options.ymax=c);if(typeof this.options.ymin=="string"&&this.options.ymin.slice(0,4)==="auto")return d=Math.min.apply(null,Array.prototype.concat.apply([],this.series)),this.options.ymin.length>5?this.options.ymin=Math.min(parseInt(this.options.ymin.slice(5),10),d):this.options.ymin=d},c.prototype.redraw=function(){var 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,_,ba,bb,bc,bd,be=this;this.el.empty(),this.r=new Raphael(this.el[0]),w=Math.max(this.measureText(this.options.ymin+this.options.units,this.options.gridTextSize).width,this.measureText(this.options.ymax+this.options.units,this.options.gridTextSize).width),u=w+this.options.marginLeft,L=this.el.width()-u-this.options.marginRight,i=this.el.height()-this.options.marginTop-this.options.marginBottom,f=L/(this.xmax-this.xmin),g=i/(this.options.ymax-this.options.ymin),G=function(a){return be.xvals.length===1?u+L/2:u+(a-be.xmin)*f},H=function(a){return be.options.marginTop+i-(a-be.options.ymin)*g},T=(this.options.ymax-this.options.ymin)/(this.options.numLines-1),h=Math.ceil(this.options.ymin/T)*T,t=Math.floor(this.options.ymax/T)*T;for(v=h;h<=t?v<=t:v>=t;v+=T)K=Math.floor(v),S=H(K),this.r.text(u-this.options.marginLeft/2,S,K+this.options.units).attr("font-size",this.options.gridTextSize).attr("fill",this.options.gridTextColor).attr("text-anchor","end"),this.r.path("M"+u+","+S+"H"+(u+L)).attr("stroke",this.options.gridLineColor).attr("stroke-width",this.options.gridStrokeWidth);B=null,Q=50,this.options.parseTime?(N=(new Date(this.xmin)).getFullYear(),O=(new Date(this.xmax)).getFullYear()):(N=this.xmin,O=this.xmax);for(p=N;N<=O?p<=O:p>=O;N<=O?p++:p--){if(this.options.parseTime){R=(new Date(p,0,1)).getTime();if(R