[services] Fix 'repo_gc_stats' to return a list of unreachable objects' repr
/* Javascript plotting library for jQuery, v. 0.6. * * Released under the MIT license by IOLA, December 2007. * */// first an inline dependency, jquery.colorhelpers.js, we inline it here// for convenience/* Plugin for jQuery for working with colors. * * Version 1.0. * * Inspiration from jQuery color animation plugin by John Resig. * * Released under the MIT license by Ole Laursen, October 2009. * * Examples: * * $.color.parse("#fff").scale('rgb', 0.25).add('a', -0.5).toString() * var c = $.color.extract($("#mydiv"), 'background-color'); * console.log(c.r, c.g, c.b, c.a); * $.color.make(100, 50, 25, 0.4).toString() // returns "rgba(100,50,25,0.4)" * * Note that .scale() and .add() work in-place instead of returning * new objects. */(function(){jQuery.color={};jQuery.color.make=function(E,D,B,C){varF={};F.r=E||0;F.g=D||0;F.b=B||0;F.a=C!=null?C:1;F.add=function(I,H){for(varG=0;G<I.length;++G){F[I.charAt(G)]+=H}returnF.normalize()};F.scale=function(I,H){for(varG=0;G<I.length;++G){F[I.charAt(G)]*=H}returnF.normalize()};F.toString=function(){if(F.a>=1){return"rgb("+[F.r,F.g,F.b].join(",")+")"}else{return"rgba("+[F.r,F.g,F.b,F.a].join(",")+")"}};F.normalize=function(){functionG(I,J,H){returnJ<I?I:(J>H?H:J)}F.r=G(0,parseInt(F.r),255);F.g=G(0,parseInt(F.g),255);F.b=G(0,parseInt(F.b),255);F.a=G(0,F.a,1);returnF};F.clone=function(){returnjQuery.color.make(F.r,F.b,F.g,F.a)};returnF.normalize()};jQuery.color.extract=function(C,B){varD;do{D=C.css(B).toLowerCase();if(D!=""&&D!="transparent"){break}C=C.parent()}while(!jQuery.nodeName(C.get(0),"body"));if(D=="rgba(0, 0, 0, 0)"){D="transparent"}returnjQuery.color.parse(D)};jQuery.color.parse=function(E){varD,B=jQuery.color.make;if(D=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(E)){returnB(parseInt(D[1],10),parseInt(D[2],10),parseInt(D[3],10))}if(D=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(E)){returnB(parseInt(D[1],10),parseInt(D[2],10),parseInt(D[3],10),parseFloat(D[4]))}if(D=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(E)){returnB(parseFloat(D[1])*2.55,parseFloat(D[2])*2.55,parseFloat(D[3])*2.55)}if(D=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(E)){returnB(parseFloat(D[1])*2.55,parseFloat(D[2])*2.55,parseFloat(D[3])*2.55,parseFloat(D[4]))}if(D=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(E)){returnB(parseInt(D[1],16),parseInt(D[2],16),parseInt(D[3],16))}if(D=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(E)){returnB(parseInt(D[1]+D[1],16),parseInt(D[2]+D[2],16),parseInt(D[3]+D[3],16))}varC=jQuery.trim(E).toLowerCase();if(C=="transparent"){returnB(255,255,255,0)}else{D=A[C];returnB(D[0],D[1],D[2])}};varA={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]}})();// the actual Flot code(function($){functionPlot(placeholder,data_,options_,plugins){// data is on the form:// [ series1, series2 ... ]// where series is either just the data as [ [x1, y1], [x2, y2], ... ]// or { data: [ [x1, y1], [x2, y2], ... ], label: "some label", ... }varseries=[],options={// the color theme used for graphscolors:["#edc240","#afd8f8","#cb4b4b","#4da74d","#9440ed"],legend:{show:true,noColumns:1,// number of colums in legend tablelabelFormatter:null,// fn: string -> stringlabelBoxBorderColor:"#ccc",// border color for the little label boxescontainer:null,// container (as jQuery object) to put legend in, null means default on top of graphposition:"ne",// position of default legend container within plotmargin:5,// distance from grid edge to default legend container within plotbackgroundColor:null,// null means auto-detectbackgroundOpacity:0.85// set to 0 to avoid background},xaxis:{mode:null,// null or "time"transform:null,// null or f: number -> number to transform axisinverseTransform:null,// if transform is set, this should be the inverse functionmin:null,// min. value to show, null means set automaticallymax:null,// max. value to show, null means set automaticallyautoscaleMargin:null,// margin in % to add if auto-setting min/maxticks:null,// either [1, 3] or [[1, "a"], 3] or (fn: axis info -> ticks) or app. number of ticks for auto-tickstickFormatter:null,// fn: number -> stringlabelWidth:null,// size of tick labels in pixelslabelHeight:null,// mode specific optionstickDecimals:null,// no. of decimals, null means autotickSize:null,// number or [number, "unit"]minTickSize:null,// number or [number, "unit"]monthNames:null,// list of names of monthstimeformat:null,// format string to usetwelveHourClock:false// 12 or 24 time in time mode},yaxis:{autoscaleMargin:0.02},x2axis:{autoscaleMargin:null},y2axis:{autoscaleMargin:0.02},series:{points:{show:false,radius:3,lineWidth:2,// in pixelsfill:true,fillColor:"#ffffff"},lines:{// we don't put in show: false so we can see// whether lines were actively disabled lineWidth:2,// in pixelsfill:false,fillColor:null,steps:false},bars:{show:false,lineWidth:2,// in pixelsbarWidth:1,// in units of the x axisfill:true,fillColor:null,align:"left",// or "center" horizontal:false// when horizontal, left is now top},shadowSize:3},grid:{show:true,aboveData:false,color:"#545454",// primary color used for outline and labelsbackgroundColor:null,// null for transparent, else colortickColor:"rgba(0,0,0,0.15)",// color used for the tickslabelMargin:5,// in pixelsborderWidth:2,// in pixelsborderColor:null,// set if different from the grid colormarkings:null,// array of ranges or fn: axes -> array of rangesmarkingsColor:"#f4f4f4",markingsLineWidth:2,// interactive stuffclickable:false,hoverable:false,autoHighlight:true,// highlight in case mouse is nearmouseActiveRadius:10// how far the mouse can be away to activate an item},hooks:{}},canvas=null,// the canvas for the plot itselfoverlay=null,// canvas for interactive stuff on top of ploteventHolder=null,// jQuery object that events should be bound toctx=null,octx=null,axes={xaxis:{},yaxis:{},x2axis:{},y2axis:{}},plotOffset={left:0,right:0,top:0,bottom:0},canvasWidth=0,canvasHeight=0,plotWidth=0,plotHeight=0,hooks={processOptions:[],processRawData:[],processDatapoints:[],draw:[],bindEvents:[],drawOverlay:[]},plot=this;// public functionsplot.setData=setData;plot.setupGrid=setupGrid;plot.draw=draw;plot.getPlaceholder=function(){returnplaceholder;};plot.getCanvas=function(){returncanvas;};plot.getPlotOffset=function(){returnplotOffset;};plot.width=function(){returnplotWidth;};plot.height=function(){returnplotHeight;};plot.offset=function(){varo=eventHolder.offset();o.left+=plotOffset.left;o.top+=plotOffset.top;returno;};plot.getData=function(){returnseries;};plot.getAxes=function(){returnaxes;};plot.getOptions=function(){returnoptions;};plot.highlight=highlight;plot.unhighlight=unhighlight;plot.triggerRedrawOverlay=triggerRedrawOverlay;plot.pointOffset=function(point){return{left:parseInt(axisSpecToRealAxis(point,"xaxis").p2c(+point.x)+plotOffset.left),top:parseInt(axisSpecToRealAxis(point,"yaxis").p2c(+point.y)+plotOffset.top)};};// public attributesplot.hooks=hooks;// initializeinitPlugins(plot);parseOptions(options_);constructCanvas();setData(data_);setupGrid();draw();bindEvents();functionexecuteHooks(hook,args){args=[plot].concat(args);for(vari=0;i<hook.length;++i)hook[i].apply(this,args);}functioninitPlugins(){for(vari=0;i<plugins.length;++i){varp=plugins[i];p.init(plot);if(p.options)$.extend(true,options,p.options);}}functionparseOptions(opts){$.extend(true,options,opts);if(options.grid.borderColor==null)options.grid.borderColor=options.grid.color;// backwards compatibility, to be removed in futureif(options.xaxis.noTicks&&options.xaxis.ticks==null)options.xaxis.ticks=options.xaxis.noTicks;if(options.yaxis.noTicks&&options.yaxis.ticks==null)options.yaxis.ticks=options.yaxis.noTicks;if(options.grid.coloredAreas)options.grid.markings=options.grid.coloredAreas;if(options.grid.coloredAreasColor)options.grid.markingsColor=options.grid.coloredAreasColor;if(options.lines)$.extend(true,options.series.lines,options.lines);if(options.points)$.extend(true,options.series.points,options.points);if(options.bars)$.extend(true,options.series.bars,options.bars);if(options.shadowSize)options.series.shadowSize=options.shadowSize;for(varninhooks)if(options.hooks[n]&&options.hooks[n].length)hooks[n]=hooks[n].concat(options.hooks[n]);executeHooks(hooks.processOptions,[options]);}functionsetData(d){series=parseData(d);fillInSeriesOptions();processData();}functionparseData(d){varres=[];for(vari=0;i<d.length;++i){vars=$.extend(true,{},options.series);if(d[i].data){s.data=d[i].data;// move the data instead of deep-copydeleted[i].data;$.extend(true,s,d[i]);d[i].data=s.data;}elses.data=d[i];res.push(s);}returnres;}functionaxisSpecToRealAxis(obj,attr){vara=obj[attr];if(!a||a==1)returnaxes[attr];if(typeofa=="number")returnaxes[attr.charAt(0)+a+attr.slice(1)];returna;// assume it's OK}functionfillInSeriesOptions(){vari;// collect what we already got of colorsvarneededColors=series.length,usedColors=[],assignedColors=[];for(i=0;i<series.length;++i){varsc=series[i].color;if(sc!=null){--neededColors;if(typeofsc=="number")assignedColors.push(sc);elseusedColors.push($.color.parse(series[i].color));}}// we might need to generate more colors if higher indices// are assignedfor(i=0;i<assignedColors.length;++i){neededColors=Math.max(neededColors,assignedColors[i]+1);}// produce colors as neededvarcolors=[],variation=0;i=0;while(colors.length<neededColors){varc;if(options.colors.length==i)// check degenerate casec=$.color.make(100,100,100);elsec=$.color.parse(options.colors[i]);// vary color if neededvarsign=variation%2==1?-1:1;c.scale('rgb',1+sign*Math.ceil(variation/2)*0.2)// FIXME: if we're getting to close to something else,// we should probably skip this onecolors.push(c);++i;if(i>=options.colors.length){i=0;++variation;}}// fill in the optionsvarcolori=0,s;for(i=0;i<series.length;++i){s=series[i];// assign colorsif(s.color==null){s.color=colors[colori].toString();++colori;}elseif(typeofs.color=="number")s.color=colors[s.color].toString();// turn on lines automatically in case nothing is setif(s.lines.show==null){varv,show=true;for(vins)if(s[v].show){show=false;break;}if(show)s.lines.show=true;}// setup axess.xaxis=axisSpecToRealAxis(s,"xaxis");s.yaxis=axisSpecToRealAxis(s,"yaxis");}}functionprocessData(){vartopSentry=Number.POSITIVE_INFINITY,bottomSentry=Number.NEGATIVE_INFINITY,i,j,k,m,length,s,points,ps,x,y,axis,val,f,p;for(axisinaxes){axes[axis].datamin=topSentry;axes[axis].datamax=bottomSentry;axes[axis].used=false;}functionupdateAxis(axis,min,max){if(min<axis.datamin)axis.datamin=min;if(max>axis.datamax)axis.datamax=max;}for(i=0;i<series.length;++i){s=series[i];s.datapoints={points:[]};executeHooks(hooks.processRawData,[s,s.data,s.datapoints]);}// first pass: clean and copy datafor(i=0;i<series.length;++i){s=series[i];vardata=s.data,format=s.datapoints.format;if(!format){format=[];// find out how to copyformat.push({x:true,number:true,required:true});format.push({y:true,number:true,required:true});if(s.bars.show)format.push({y:true,number:true,required:false,defaultValue:0});s.datapoints.format=format;}if(s.datapoints.pointsize!=null)continue;// already filled inif(s.datapoints.pointsize==null)s.datapoints.pointsize=format.length;ps=s.datapoints.pointsize;points=s.datapoints.points;insertSteps=s.lines.show&&s.lines.steps;s.xaxis.used=s.yaxis.used=true;for(j=k=0;j<data.length;++j,k+=ps){p=data[j];varnullify=p==null;if(!nullify){for(m=0;m<ps;++m){val=p[m];f=format[m];if(f){if(f.number&&val!=null){val=+val;// convert to numberif(isNaN(val))val=null;}if(val==null){if(f.required)nullify=true;if(f.defaultValue!=null)val=f.defaultValue;}}points[k+m]=val;}}if(nullify){for(m=0;m<ps;++m){val=points[k+m];if(val!=null){f=format[m];// extract min/max infoif(f.x)updateAxis(s.xaxis,val,val);if(f.y)updateAxis(s.yaxis,val,val);}points[k+m]=null;}}else{// a little bit of line specific stuff that// perhaps shouldn't be here, but lacking// better means...if(insertSteps&&k>0&&points[k-ps]!=null&&points[k-ps]!=points[k]&&points[k-ps+1]!=points[k+1]){// copy the point to make room for a middle pointfor(m=0;m<ps;++m)points[k+ps+m]=points[k+m];// middle point has same ypoints[k+1]=points[k-ps+1];// we've added a point, better reflect thatk+=ps;}}}}// give the hooks a chance to runfor(i=0;i<series.length;++i){s=series[i];executeHooks(hooks.processDatapoints,[s,s.datapoints]);}// second pass: find datamax/datamin for auto-scalingfor(i=0;i<series.length;++i){s=series[i];points=s.datapoints.points,ps=s.datapoints.pointsize;varxmin=topSentry,ymin=topSentry,xmax=bottomSentry,ymax=bottomSentry;for(j=0;j<points.length;j+=ps){if(points[j]==null)continue;for(m=0;m<ps;++m){val=points[j+m];f=format[m];if(!f)continue;if(f.x){if(val<xmin)xmin=val;if(val>xmax)xmax=val;}if(f.y){if(val<ymin)ymin=val;if(val>ymax)ymax=val;}}}if(s.bars.show){// make sure we got room for the bar on the dancing floorvardelta=s.bars.align=="left"?0:-s.bars.barWidth/2;if(s.bars.horizontal){ymin+=delta;ymax+=delta+s.bars.barWidth;}else{xmin+=delta;xmax+=delta+s.bars.barWidth;}}updateAxis(s.xaxis,xmin,xmax);updateAxis(s.yaxis,ymin,ymax);}for(axisinaxes){if(axes[axis].datamin==topSentry)axes[axis].datamin=null;if(axes[axis].datamax==bottomSentry)axes[axis].datamax=null;}}functionconstructCanvas(){functionmakeCanvas(width,height){varc=document.createElement('canvas');c.width=width;c.height=height;if($.browser.msie)// excanvas hackc=window.G_vmlCanvasManager.initElement(c);returnc;}canvasWidth=placeholder.width();canvasHeight=placeholder.height();placeholder.html("");// clear placeholderif(placeholder.css("position")=='static')placeholder.css("position","relative");// for positioning labels and overlayif(canvasWidth<=0||canvasHeight<=0)throw"Invalid dimensions for plot, width = "+canvasWidth+", height = "+canvasHeight;if($.browser.msie)// excanvas hackwindow.G_vmlCanvasManager.init_(document);// make sure everything is setup// the canvascanvas=$(makeCanvas(canvasWidth,canvasHeight)).appendTo(placeholder).get(0);ctx=canvas.getContext("2d");// overlay canvas for interactive featuresoverlay=$(makeCanvas(canvasWidth,canvasHeight)).css({position:'absolute',left:0,top:0}).appendTo(placeholder).get(0);octx=overlay.getContext("2d");octx.stroke();}functionbindEvents(){// we include the canvas in the event holder too, because IE 7// sometimes has trouble with the stacking ordereventHolder=$([overlay,canvas]);// bind eventsif(options.grid.hoverable)eventHolder.mousemove(onMouseMove);if(options.grid.clickable)eventHolder.click(onClick);executeHooks(hooks.bindEvents,[eventHolder]);}functionsetupGrid(){functionsetTransformationHelpers(axis,o){functionidentity(x){returnx;}vars,m,t=o.transform||identity,it=o.inverseTransform;// add transformation helpersif(axis==axes.xaxis||axis==axes.x2axis){// precompute how much the axis is scaling a point// in canvas spaces=axis.scale=plotWidth/(t(axis.max)-t(axis.min));m=t(axis.min);// data point to canvas coordinateif(t==identity)// slight optimizationaxis.p2c=function(p){return(p-m)*s;};elseaxis.p2c=function(p){return(t(p)-m)*s;};// canvas coordinate to data pointif(!it)axis.c2p=function(c){returnm+c/s;};elseaxis.c2p=function(c){returnit(m+c/s);};}else{s=axis.scale=plotHeight/(t(axis.max)-t(axis.min));m=t(axis.max);if(t==identity)axis.p2c=function(p){return(m-p)*s;};elseaxis.p2c=function(p){return(m-t(p))*s;};if(!it)axis.c2p=function(c){returnm-c/s;};elseaxis.c2p=function(c){returnit(m-c/s);};}}functionmeasureLabels(axis,axisOptions){vari,labels=[],l;axis.labelWidth=axisOptions.labelWidth;axis.labelHeight=axisOptions.labelHeight;if(axis==axes.xaxis||axis==axes.x2axis){// to avoid measuring the widths of the labels, we// construct fixed-size boxes and put the labels inside// them, we don't need the exact figures and the// fixed-size box content is easy to centerif(axis.labelWidth==null)axis.labelWidth=canvasWidth/(axis.ticks.length>0?axis.ticks.length:1);// measure x label heightsif(axis.labelHeight==null){labels=[];for(i=0;i<axis.ticks.length;++i){l=axis.ticks[i].label;if(l)labels.push('<div class="tickLabel" style="float:left;width:'+axis.labelWidth+'px">'+l+'</div>');}if(labels.length>0){vardummyDiv=$('<div style="position:absolute;top:-10000px;width:10000px;font-size:smaller">'+labels.join("")+'<div style="clear:left"></div></div>').appendTo(placeholder);axis.labelHeight=dummyDiv.height();dummyDiv.remove();}}}elseif(axis.labelWidth==null||axis.labelHeight==null){// calculate y label dimensionsfor(i=0;i<axis.ticks.length;++i){l=axis.ticks[i].label;if(l)labels.push('<div class="tickLabel">'+l+'</div>');}if(labels.length>0){vardummyDiv=$('<div style="position:absolute;top:-10000px;font-size:smaller">'+labels.join("")+'</div>').appendTo(placeholder);if(axis.labelWidth==null)axis.labelWidth=dummyDiv.width();if(axis.labelHeight==null)axis.labelHeight=dummyDiv.find("div").height();dummyDiv.remove();}}if(axis.labelWidth==null)axis.labelWidth=0;if(axis.labelHeight==null)axis.labelHeight=0;}functionsetGridSpacing(){// get the most space needed around the grid for things// that may stick outvarmaxOutset=options.grid.borderWidth;for(i=0;i<series.length;++i)maxOutset=Math.max(maxOutset,2*(series[i].points.radius+series[i].points.lineWidth/2));plotOffset.left=plotOffset.right=plotOffset.top=plotOffset.bottom=maxOutset;varmargin=options.grid.labelMargin+options.grid.borderWidth;if(axes.xaxis.labelHeight>0)plotOffset.bottom=Math.max(maxOutset,axes.xaxis.labelHeight+margin);if(axes.yaxis.labelWidth>0)plotOffset.left=Math.max(maxOutset,axes.yaxis.labelWidth+margin);if(axes.x2axis.labelHeight>0)plotOffset.top=Math.max(maxOutset,axes.x2axis.labelHeight+margin);if(axes.y2axis.labelWidth>0)plotOffset.right=Math.max(maxOutset,axes.y2axis.labelWidth+margin);plotWidth=canvasWidth-plotOffset.left-plotOffset.right;plotHeight=canvasHeight-plotOffset.bottom-plotOffset.top;}varaxis;for(axisinaxes)setRange(axes[axis],options[axis]);if(options.grid.show){for(axisinaxes){prepareTickGeneration(axes[axis],options[axis]);setTicks(axes[axis],options[axis]);measureLabels(axes[axis],options[axis]);}setGridSpacing();}else{plotOffset.left=plotOffset.right=plotOffset.top=plotOffset.bottom=0;plotWidth=canvasWidth;plotHeight=canvasHeight;}for(axisinaxes)setTransformationHelpers(axes[axis],options[axis]);if(options.grid.show)insertLabels();insertLegend();}functionsetRange(axis,axisOptions){varmin=+(axisOptions.min!=null?axisOptions.min:axis.datamin),max=+(axisOptions.max!=null?axisOptions.max:axis.datamax),delta=max-min;if(delta==0.0){// degenerate casevarwiden=max==0?1:0.01;if(axisOptions.min==null)min-=widen;// alway widen max if we couldn't widen min to ensure we// don't fall into min == max which doesn't workif(axisOptions.max==null||axisOptions.min!=null)max+=widen;}else{// consider autoscalingvarmargin=axisOptions.autoscaleMargin;if(margin!=null){if(axisOptions.min==null){min-=delta*margin;// make sure we don't go below zero if all values// are positiveif(min<0&&axis.datamin!=null&&axis.datamin>=0)min=0;}if(axisOptions.max==null){max+=delta*margin;if(max>0&&axis.datamax!=null&&axis.datamax<=0)max=0;}}}axis.min=min;axis.max=max;}functionprepareTickGeneration(axis,axisOptions){// estimate number of ticksvarnoTicks;if(typeofaxisOptions.ticks=="number"&&axisOptions.ticks>0)noTicks=axisOptions.ticks;elseif(axis==axes.xaxis||axis==axes.x2axis)// heuristic based on the model a*sqrt(x) fitted to// some reasonable data pointsnoTicks=0.3*Math.sqrt(canvasWidth);elsenoTicks=0.3*Math.sqrt(canvasHeight);vardelta=(axis.max-axis.min)/noTicks,size,generator,unit,formatter,i,magn,norm;if(axisOptions.mode=="time"){// pretty handling of time// map of app. size of time units in millisecondsvartimeUnitSize={"second":1000,"minute":60*1000,"hour":60*60*1000,"day":24*60*60*1000,"month":30*24*60*60*1000,"year":365.2425*24*60*60*1000};// the allowed tick sizes, after 1 year we use// an integer algorithmvarspec=[[1,"second"],[2,"second"],[5,"second"],[10,"second"],[30,"second"],[1,"minute"],[2,"minute"],[5,"minute"],[10,"minute"],[30,"minute"],[1,"hour"],[2,"hour"],[4,"hour"],[8,"hour"],[12,"hour"],[1,"day"],[2,"day"],[3,"day"],[0.25,"month"],[0.5,"month"],[1,"month"],[2,"month"],[3,"month"],[6,"month"],[1,"year"]];varminSize=0;if(axisOptions.minTickSize!=null){if(typeofaxisOptions.tickSize=="number")minSize=axisOptions.tickSize;elseminSize=axisOptions.minTickSize[0]*timeUnitSize[axisOptions.minTickSize[1]];}for(i=0;i<spec.length-1;++i)if(delta<(spec[i][0]*timeUnitSize[spec[i][1]]+spec[i+1][0]*timeUnitSize[spec[i+1][1]])/2&&spec[i][0]*timeUnitSize[spec[i][1]]>=minSize)break;size=spec[i][0];unit=spec[i][1];// special-case the possibility of several yearsif(unit=="year"){magn=Math.pow(10,Math.floor(Math.log(delta/timeUnitSize.year)/Math.LN10));norm=(delta/timeUnitSize.year)/magn;if(norm<1.5)size=1;elseif(norm<3)size=2;elseif(norm<7.5)size=5;elsesize=10;size*=magn;}if(axisOptions.tickSize){size=axisOptions.tickSize[0];unit=axisOptions.tickSize[1];}generator=function(axis){varticks=[],tickSize=axis.tickSize[0],unit=axis.tickSize[1],d=newDate(axis.min);varstep=tickSize*timeUnitSize[unit];if(unit=="second")d.setUTCSeconds(floorInBase(d.getUTCSeconds(),tickSize));if(unit=="minute")d.setUTCMinutes(floorInBase(d.getUTCMinutes(),tickSize));if(unit=="hour")d.setUTCHours(floorInBase(d.getUTCHours(),tickSize));if(unit=="month")d.setUTCMonth(floorInBase(d.getUTCMonth(),tickSize));if(unit=="year")d.setUTCFullYear(floorInBase(d.getUTCFullYear(),tickSize));// reset smaller componentsd.setUTCMilliseconds(0);if(step>=timeUnitSize.minute)d.setUTCSeconds(0);if(step>=timeUnitSize.hour)d.setUTCMinutes(0);if(step>=timeUnitSize.day)d.setUTCHours(0);if(step>=timeUnitSize.day*4)d.setUTCDate(1);if(step>=timeUnitSize.year)d.setUTCMonth(0);varcarry=0,v=Number.NaN,prev;do{prev=v;v=d.getTime();ticks.push({v:v,label:axis.tickFormatter(v,axis)});if(unit=="month"){if(tickSize<1){// a bit complicated - we'll divide the month// up but we need to take care of fractions// so we don't end up in the middle of a dayd.setUTCDate(1);varstart=d.getTime();d.setUTCMonth(d.getUTCMonth()+1);varend=d.getTime();d.setTime(v+carry*timeUnitSize.hour+(end-start)*tickSize);carry=d.getUTCHours();d.setUTCHours(0);}elsed.setUTCMonth(d.getUTCMonth()+tickSize);}elseif(unit=="year"){d.setUTCFullYear(d.getUTCFullYear()+tickSize);}elsed.setTime(v+step);}while(v<axis.max&&v!=prev);returnticks;};formatter=function(v,axis){vard=newDate(v);// first check global formatif(axisOptions.timeformat!=null)return$.plot.formatDate(d,axisOptions.timeformat,axisOptions.monthNames);vart=axis.tickSize[0]*timeUnitSize[axis.tickSize[1]];varspan=axis.max-axis.min;varsuffix=(axisOptions.twelveHourClock)?" %p":"";if(t<timeUnitSize.minute)fmt="%h:%M:%S"+suffix;elseif(t<timeUnitSize.day){if(span<2*timeUnitSize.day)fmt="%h:%M"+suffix;elsefmt="%b %d %h:%M"+suffix;}elseif(t<timeUnitSize.month)fmt="%b %d";elseif(t<timeUnitSize.year){if(span<timeUnitSize.year)fmt="%b";elsefmt="%b %y";}elsefmt="%y";return$.plot.formatDate(d,fmt,axisOptions.monthNames);};}else{// pretty rounding of base-10 numbersvarmaxDec=axisOptions.tickDecimals;vardec=-Math.floor(Math.log(delta)/Math.LN10);if(maxDec!=null&&dec>maxDec)dec=maxDec;magn=Math.pow(10,-dec);norm=delta/magn;// norm is between 1.0 and 10.0if(norm<1.5)size=1;elseif(norm<3){size=2;// special case for 2.5, requires an extra decimalif(norm>2.25&&(maxDec==null||dec+1<=maxDec)){size=2.5;++dec;}}elseif(norm<7.5)size=5;elsesize=10;size*=magn;if(axisOptions.minTickSize!=null&&size<axisOptions.minTickSize)size=axisOptions.minTickSize;if(axisOptions.tickSize!=null)size=axisOptions.tickSize;axis.tickDecimals=Math.max(0,(maxDec!=null)?maxDec:dec);generator=function(axis){varticks=[];// spew out all possible ticksvarstart=floorInBase(axis.min,axis.tickSize),i=0,v=Number.NaN,prev;do{prev=v;v=start+i*axis.tickSize;ticks.push({v:v,label:axis.tickFormatter(v,axis)});++i;}while(v<axis.max&&v!=prev);returnticks;};formatter=function(v,axis){returnv.toFixed(axis.tickDecimals);};}axis.tickSize=unit?[size,unit]:size;axis.tickGenerator=generator;if($.isFunction(axisOptions.tickFormatter))axis.tickFormatter=function(v,axis){return""+axisOptions.tickFormatter(v,axis);};elseaxis.tickFormatter=formatter;}functionsetTicks(axis,axisOptions){axis.ticks=[];if(!axis.used)return;if(axisOptions.ticks==null)axis.ticks=axis.tickGenerator(axis);elseif(typeofaxisOptions.ticks=="number"){if(axisOptions.ticks>0)axis.ticks=axis.tickGenerator(axis);}elseif(axisOptions.ticks){varticks=axisOptions.ticks;if($.isFunction(ticks))// generate the ticksticks=ticks({min:axis.min,max:axis.max});// clean up the user-supplied ticks, copy them overvari,v;for(i=0;i<ticks.length;++i){varlabel=null;vart=ticks[i];if(typeoft=="object"){v=t[0];if(t.length>1)label=t[1];}elsev=t;if(label==null)label=axis.tickFormatter(v,axis);axis.ticks[i]={v:v,label:label};}}if(axisOptions.autoscaleMargin!=null&&axis.ticks.length>0){// snap to ticksif(axisOptions.min==null)axis.min=Math.min(axis.min,axis.ticks[0].v);if(axisOptions.max==null&&axis.ticks.length>1)axis.max=Math.max(axis.max,axis.ticks[axis.ticks.length-1].v);}}functiondraw(){ctx.clearRect(0,0,canvasWidth,canvasHeight);vargrid=options.grid;if(grid.show&&!grid.aboveData)drawGrid();for(vari=0;i<series.length;++i)drawSeries(series[i]);executeHooks(hooks.draw,[ctx]);if(grid.show&&grid.aboveData)drawGrid();}functionextractRange(ranges,coord){varfirstAxis=coord+"axis",secondaryAxis=coord+"2axis",axis,from,to,reverse;if(ranges[firstAxis]){axis=axes[firstAxis];from=ranges[firstAxis].from;to=ranges[firstAxis].to;}elseif(ranges[secondaryAxis]){axis=axes[secondaryAxis];from=ranges[secondaryAxis].from;to=ranges[secondaryAxis].to;}else{// backwards-compat stuff - to be removed in futureaxis=axes[firstAxis];from=ranges[coord+"1"];to=ranges[coord+"2"];}// auto-reverse as an added bonusif(from!=null&&to!=null&&from>to)return{from:to,to:from,axis:axis};return{from:from,to:to,axis:axis};}functiondrawGrid(){vari;ctx.save();ctx.translate(plotOffset.left,plotOffset.top);// draw background, if anyif(options.grid.backgroundColor){ctx.fillStyle=getColorOrGradient(options.grid.backgroundColor,plotHeight,0,"rgba(255, 255, 255, 0)");ctx.fillRect(0,0,plotWidth,plotHeight);}// draw markingsvarmarkings=options.grid.markings;if(markings){if($.isFunction(markings))// xmin etc. are backwards-compatible, to be removed in futuremarkings=markings({xmin:axes.xaxis.min,xmax:axes.xaxis.max,ymin:axes.yaxis.min,ymax:axes.yaxis.max,xaxis:axes.xaxis,yaxis:axes.yaxis,x2axis:axes.x2axis,y2axis:axes.y2axis});for(i=0;i<markings.length;++i){varm=markings[i],xrange=extractRange(m,"x"),yrange=extractRange(m,"y");// fill in missingif(xrange.from==null)xrange.from=xrange.axis.min;if(xrange.to==null)xrange.to=xrange.axis.max;if(yrange.from==null)yrange.from=yrange.axis.min;if(yrange.to==null)yrange.to=yrange.axis.max;// clipif(xrange.to<xrange.axis.min||xrange.from>xrange.axis.max||yrange.to<yrange.axis.min||yrange.from>yrange.axis.max)continue;xrange.from=Math.max(xrange.from,xrange.axis.min);xrange.to=Math.min(xrange.to,xrange.axis.max);yrange.from=Math.max(yrange.from,yrange.axis.min);yrange.to=Math.min(yrange.to,yrange.axis.max);if(xrange.from==xrange.to&&yrange.from==yrange.to)continue;// then drawxrange.from=xrange.axis.p2c(xrange.from);xrange.to=xrange.axis.p2c(xrange.to);yrange.from=yrange.axis.p2c(yrange.from);yrange.to=yrange.axis.p2c(yrange.to);if(xrange.from==xrange.to||yrange.from==yrange.to){// draw linectx.beginPath();ctx.strokeStyle=m.color||options.grid.markingsColor;ctx.lineWidth=m.lineWidth||options.grid.markingsLineWidth;//ctx.moveTo(Math.floor(xrange.from), yrange.from);//ctx.lineTo(Math.floor(xrange.to), yrange.to);ctx.moveTo(xrange.from,yrange.from);ctx.lineTo(xrange.to,yrange.to);ctx.stroke();}else{// fill areactx.fillStyle=m.color||options.grid.markingsColor;ctx.fillRect(xrange.from,yrange.to,xrange.to-xrange.from,yrange.from-yrange.to);}}}// draw the inner gridctx.lineWidth=1;ctx.strokeStyle=options.grid.tickColor;ctx.beginPath();varv,axis=axes.xaxis;for(i=0;i<axis.ticks.length;++i){v=axis.ticks[i].v;if(v<=axis.min||v>=axes.xaxis.max)continue;// skip those lying on the axesctx.moveTo(Math.floor(axis.p2c(v))+ctx.lineWidth/2,0);ctx.lineTo(Math.floor(axis.p2c(v))+ctx.lineWidth/2,plotHeight);}axis=axes.yaxis;for(i=0;i<axis.ticks.length;++i){v=axis.ticks[i].v;if(v<=axis.min||v>=axis.max)continue;ctx.moveTo(0,Math.floor(axis.p2c(v))+ctx.lineWidth/2);ctx.lineTo(plotWidth,Math.floor(axis.p2c(v))+ctx.lineWidth/2);}axis=axes.x2axis;for(i=0;i<axis.ticks.length;++i){v=axis.ticks[i].v;if(v<=axis.min||v>=axis.max)continue;ctx.moveTo(Math.floor(axis.p2c(v))+ctx.lineWidth/2,-5);ctx.lineTo(Math.floor(axis.p2c(v))+ctx.lineWidth/2,5);}axis=axes.y2axis;for(i=0;i<axis.ticks.length;++i){v=axis.ticks[i].v;if(v<=axis.min||v>=axis.max)continue;ctx.moveTo(plotWidth-5,Math.floor(axis.p2c(v))+ctx.lineWidth/2);ctx.lineTo(plotWidth+5,Math.floor(axis.p2c(v))+ctx.lineWidth/2);}ctx.stroke();if(options.grid.borderWidth){// draw bordervarbw=options.grid.borderWidth;ctx.lineWidth=bw;ctx.strokeStyle=options.grid.borderColor;ctx.strokeRect(-bw/2,-bw/2,plotWidth+bw,plotHeight+bw);}ctx.restore();}functioninsertLabels(){placeholder.find(".tickLabels").remove();varhtml=['<div class="tickLabels" style="font-size:smaller;color:'+options.grid.color+'">'];functionaddLabels(axis,labelGenerator){for(vari=0;i<axis.ticks.length;++i){vartick=axis.ticks[i];if(!tick.label||tick.v<axis.min||tick.v>axis.max)continue;html.push(labelGenerator(tick,axis));}}varmargin=options.grid.labelMargin+options.grid.borderWidth;addLabels(axes.xaxis,function(tick,axis){return'<div style="position:absolute;top:'+(plotOffset.top+plotHeight+margin)+'px;left:'+Math.round(plotOffset.left+axis.p2c(tick.v)-axis.labelWidth/2)+'px;width:'+axis.labelWidth+'px;text-align:center" class="tickLabel">'+tick.label+"</div>";});addLabels(axes.yaxis,function(tick,axis){return'<div style="position:absolute;top:'+Math.round(plotOffset.top+axis.p2c(tick.v)-axis.labelHeight/2)+'px;right:'+(plotOffset.right+plotWidth+margin)+'px;width:'+axis.labelWidth+'px;text-align:right" class="tickLabel">'+tick.label+"</div>";});addLabels(axes.x2axis,function(tick,axis){return'<div style="position:absolute;bottom:'+(plotOffset.bottom+plotHeight+margin)+'px;left:'+Math.round(plotOffset.left+axis.p2c(tick.v)-axis.labelWidth/2)+'px;width:'+axis.labelWidth+'px;text-align:center" class="tickLabel">'+tick.label+"</div>";});addLabels(axes.y2axis,function(tick,axis){return'<div style="position:absolute;top:'+Math.round(plotOffset.top+axis.p2c(tick.v)-axis.labelHeight/2)+'px;left:'+(plotOffset.left+plotWidth+margin)+'px;width:'+axis.labelWidth+'px;text-align:left" class="tickLabel">'+tick.label+"</div>";});html.push('</div>');placeholder.append(html.join(""));}functiondrawSeries(series){if(series.lines.show)drawSeriesLines(series);if(series.bars.show)drawSeriesBars(series);if(series.points.show)drawSeriesPoints(series);}functiondrawSeriesLines(series){functionplotLine(datapoints,xoffset,yoffset,axisx,axisy){varpoints=datapoints.points,ps=datapoints.pointsize,prevx=null,prevy=null;ctx.beginPath();for(vari=ps;i<points.length;i+=ps){varx1=points[i-ps],y1=points[i-ps+1],x2=points[i],y2=points[i+1];if(x1==null||x2==null)continue;// clip with yminif(y1<=y2&&y1<axisy.min){if(y2<axisy.min)continue;// line segment is outside// compute new intersection pointx1=(axisy.min-y1)/(y2-y1)*(x2-x1)+x1;y1=axisy.min;}elseif(y2<=y1&&y2<axisy.min){if(y1<axisy.min)continue;x2=(axisy.min-y1)/(y2-y1)*(x2-x1)+x1;y2=axisy.min;}// clip with ymaxif(y1>=y2&&y1>axisy.max){if(y2>axisy.max)continue;x1=(axisy.max-y1)/(y2-y1)*(x2-x1)+x1;y1=axisy.max;}elseif(y2>=y1&&y2>axisy.max){if(y1>axisy.max)continue;x2=(axisy.max-y1)/(y2-y1)*(x2-x1)+x1;y2=axisy.max;}// clip with xminif(x1<=x2&&x1<axisx.min){if(x2<axisx.min)continue;y1=(axisx.min-x1)/(x2-x1)*(y2-y1)+y1;x1=axisx.min;}elseif(x2<=x1&&x2<axisx.min){if(x1<axisx.min)continue;y2=(axisx.min-x1)/(x2-x1)*(y2-y1)+y1;x2=axisx.min;}// clip with xmaxif(x1>=x2&&x1>axisx.max){if(x2>axisx.max)continue;y1=(axisx.max-x1)/(x2-x1)*(y2-y1)+y1;x1=axisx.max;}elseif(x2>=x1&&x2>axisx.max){if(x1>axisx.max)continue;y2=(axisx.max-x1)/(x2-x1)*(y2-y1)+y1;x2=axisx.max;}if(x1!=prevx||y1!=prevy)ctx.moveTo(axisx.p2c(x1)+xoffset,axisy.p2c(y1)+yoffset);prevx=x2;prevy=y2;ctx.lineTo(axisx.p2c(x2)+xoffset,axisy.p2c(y2)+yoffset);}ctx.stroke();}functionplotLineArea(datapoints,axisx,axisy){varpoints=datapoints.points,ps=datapoints.pointsize,bottom=Math.min(Math.max(0,axisy.min),axisy.max),top,lastX=0,areaOpen=false;for(vari=ps;i<points.length;i+=ps){varx1=points[i-ps],y1=points[i-ps+1],x2=points[i],y2=points[i+1];if(areaOpen&&x1!=null&&x2==null){// close areactx.lineTo(axisx.p2c(lastX),axisy.p2c(bottom));ctx.fill();areaOpen=false;continue;}if(x1==null||x2==null)continue;// clip x values// clip with xminif(x1<=x2&&x1<axisx.min){if(x2<axisx.min)continue;y1=(axisx.min-x1)/(x2-x1)*(y2-y1)+y1;x1=axisx.min;}elseif(x2<=x1&&x2<axisx.min){if(x1<axisx.min)continue;y2=(axisx.min-x1)/(x2-x1)*(y2-y1)+y1;x2=axisx.min;}// clip with xmaxif(x1>=x2&&x1>axisx.max){if(x2>axisx.max)continue;y1=(axisx.max-x1)/(x2-x1)*(y2-y1)+y1;x1=axisx.max;}elseif(x2>=x1&&x2>axisx.max){if(x1>axisx.max)continue;y2=(axisx.max-x1)/(x2-x1)*(y2-y1)+y1;x2=axisx.max;}if(!areaOpen){// open areactx.beginPath();ctx.moveTo(axisx.p2c(x1),axisy.p2c(bottom));areaOpen=true;}// now first check the case where both is outsideif(y1>=axisy.max&&y2>=axisy.max){ctx.lineTo(axisx.p2c(x1),axisy.p2c(axisy.max));ctx.lineTo(axisx.p2c(x2),axisy.p2c(axisy.max));lastX=x2;continue;}elseif(y1<=axisy.min&&y2<=axisy.min){ctx.lineTo(axisx.p2c(x1),axisy.p2c(axisy.min));ctx.lineTo(axisx.p2c(x2),axisy.p2c(axisy.min));lastX=x2;continue;}// else it's a bit more complicated, there might// be two rectangles and two triangles we need to fill// in; to find these keep track of the current x valuesvarx1old=x1,x2old=x2;// and clip the y values, without shortcutting// clip with yminif(y1<=y2&&y1<axisy.min&&y2>=axisy.min){x1=(axisy.min-y1)/(y2-y1)*(x2-x1)+x1;y1=axisy.min;}elseif(y2<=y1&&y2<axisy.min&&y1>=axisy.min){x2=(axisy.min-y1)/(y2-y1)*(x2-x1)+x1;y2=axisy.min;}// clip with ymaxif(y1>=y2&&y1>axisy.max&&y2<=axisy.max){x1=(axisy.max-y1)/(y2-y1)*(x2-x1)+x1;y1=axisy.max;}elseif(y2>=y1&&y2>axisy.max&&y1<=axisy.max){x2=(axisy.max-y1)/(y2-y1)*(x2-x1)+x1;y2=axisy.max;}// if the x value was changed we got a rectangle// to fillif(x1!=x1old){if(y1<=axisy.min)top=axisy.min;elsetop=axisy.max;ctx.lineTo(axisx.p2c(x1old),axisy.p2c(top));ctx.lineTo(axisx.p2c(x1),axisy.p2c(top));}// fill the trianglesctx.lineTo(axisx.p2c(x1),axisy.p2c(y1));ctx.lineTo(axisx.p2c(x2),axisy.p2c(y2));// fill the other rectangle if it's thereif(x2!=x2old){if(y2<=axisy.min)top=axisy.min;elsetop=axisy.max;ctx.lineTo(axisx.p2c(x2),axisy.p2c(top));ctx.lineTo(axisx.p2c(x2old),axisy.p2c(top));}lastX=Math.max(x2,x2old);}if(areaOpen){ctx.lineTo(axisx.p2c(lastX),axisy.p2c(bottom));ctx.fill();}}ctx.save();ctx.translate(plotOffset.left,plotOffset.top);ctx.lineJoin="round";varlw=series.lines.lineWidth,sw=series.shadowSize;// FIXME: consider another form of shadow when filling is turned onif(lw>0&&sw>0){// draw shadow as a thick and thin line with transparencyctx.lineWidth=sw;ctx.strokeStyle="rgba(0,0,0,0.1)";// position shadow at angle from the mid of linevarangle=Math.PI/18;plotLine(series.datapoints,Math.sin(angle)*(lw/2+sw/2),Math.cos(angle)*(lw/2+sw/2),series.xaxis,series.yaxis);ctx.lineWidth=sw/2;plotLine(series.datapoints,Math.sin(angle)*(lw/2+sw/4),Math.cos(angle)*(lw/2+sw/4),series.xaxis,series.yaxis);}ctx.lineWidth=lw;ctx.strokeStyle=series.color;varfillStyle=getFillStyle(series.lines,series.color,0,plotHeight);if(fillStyle){ctx.fillStyle=fillStyle;plotLineArea(series.datapoints,series.xaxis,series.yaxis);}if(lw>0)plotLine(series.datapoints,0,0,series.xaxis,series.yaxis);ctx.restore();}functiondrawSeriesPoints(series){functionplotPoints(datapoints,radius,fillStyle,offset,circumference,axisx,axisy){varpoints=datapoints.points,ps=datapoints.pointsize;for(vari=0;i<points.length;i+=ps){varx=points[i],y=points[i+1];if(x==null||x<axisx.min||x>axisx.max||y<axisy.min||y>axisy.max)continue;ctx.beginPath();ctx.arc(axisx.p2c(x),axisy.p2c(y)+offset,radius,0,circumference,false);if(fillStyle){ctx.fillStyle=fillStyle;ctx.fill();}ctx.stroke();}}ctx.save();ctx.translate(plotOffset.left,plotOffset.top);varlw=series.lines.lineWidth,sw=series.shadowSize,radius=series.points.radius;if(lw>0&&sw>0){// draw shadow in two stepsvarw=sw/2;ctx.lineWidth=w;ctx.strokeStyle="rgba(0,0,0,0.1)";plotPoints(series.datapoints,radius,null,w+w/2,Math.PI,series.xaxis,series.yaxis);ctx.strokeStyle="rgba(0,0,0,0.2)";plotPoints(series.datapoints,radius,null,w/2,Math.PI,series.xaxis,series.yaxis);}ctx.lineWidth=lw;ctx.strokeStyle=series.color;plotPoints(series.datapoints,radius,getFillStyle(series.points,series.color),0,2*Math.PI,series.xaxis,series.yaxis);ctx.restore();}functiondrawBar(x,y,b,barLeft,barRight,offset,fillStyleCallback,axisx,axisy,c,horizontal){varleft,right,bottom,top,drawLeft,drawRight,drawTop,drawBottom,tmp;if(horizontal){drawBottom=drawRight=drawTop=true;drawLeft=false;left=b;right=x;top=y+barLeft;bottom=y+barRight;// account for negative barsif(right<left){tmp=right;right=left;left=tmp;drawLeft=true;drawRight=false;}}else{drawLeft=drawRight=drawTop=true;drawBottom=false;left=x+barLeft;right=x+barRight;bottom=b;top=y;// account for negative barsif(top<bottom){tmp=top;top=bottom;bottom=tmp;drawBottom=true;drawTop=false;}}// clipif(right<axisx.min||left>axisx.max||top<axisy.min||bottom>axisy.max)return;if(left<axisx.min){left=axisx.min;drawLeft=false;}if(right>axisx.max){right=axisx.max;drawRight=false;}if(bottom<axisy.min){bottom=axisy.min;drawBottom=false;}if(top>axisy.max){top=axisy.max;drawTop=false;}left=axisx.p2c(left);bottom=axisy.p2c(bottom);right=axisx.p2c(right);top=axisy.p2c(top);// fill the barif(fillStyleCallback){c.beginPath();c.moveTo(left,bottom);c.lineTo(left,top);c.lineTo(right,top);c.lineTo(right,bottom);c.fillStyle=fillStyleCallback(bottom,top);c.fill();}// draw outlineif(drawLeft||drawRight||drawTop||drawBottom){c.beginPath();// FIXME: inline moveTo is buggy with excanvasc.moveTo(left,bottom+offset);if(drawLeft)c.lineTo(left,top+offset);elsec.moveTo(left,top+offset);if(drawTop)c.lineTo(right,top+offset);elsec.moveTo(right,top+offset);if(drawRight)c.lineTo(right,bottom+offset);elsec.moveTo(right,bottom+offset);if(drawBottom)c.lineTo(left,bottom+offset);elsec.moveTo(left,bottom+offset);c.stroke();}}functiondrawSeriesBars(series){functionplotBars(datapoints,barLeft,barRight,offset,fillStyleCallback,axisx,axisy){varpoints=datapoints.points,ps=datapoints.pointsize;for(vari=0;i<points.length;i+=ps){if(points[i]==null)continue;drawBar(points[i],points[i+1],points[i+2],barLeft,barRight,offset,fillStyleCallback,axisx,axisy,ctx,series.bars.horizontal);}}ctx.save();ctx.translate(plotOffset.left,plotOffset.top);// FIXME: figure out a way to add shadows (for instance along the right edge)ctx.lineWidth=series.bars.lineWidth;ctx.strokeStyle=series.color;varbarLeft=series.bars.align=="left"?0:-series.bars.barWidth/2;varfillStyleCallback=series.bars.fill?function(bottom,top){returngetFillStyle(series.bars,series.color,bottom,top);}:null;plotBars(series.datapoints,barLeft,barLeft+series.bars.barWidth,0,fillStyleCallback,series.xaxis,series.yaxis);ctx.restore();}functiongetFillStyle(filloptions,seriesColor,bottom,top){varfill=filloptions.fill;if(!fill)returnnull;if(filloptions.fillColor)returngetColorOrGradient(filloptions.fillColor,bottom,top,seriesColor);varc=$.color.parse(seriesColor);c.a=typeoffill=="number"?fill:0.4;c.normalize();returnc.toString();}functioninsertLegend(){placeholder.find(".legend").remove();if(!options.legend.show)return;varfragments=[],rowStarted=false,lf=options.legend.labelFormatter,s,label;for(i=0;i<series.length;++i){s=series[i];label=s.label;if(!label)continue;if(i%options.legend.noColumns==0){if(rowStarted)fragments.push('</tr>');fragments.push('<tr>');rowStarted=true;}if(lf)label=lf(label,s);fragments.push('<td class="legendColorBox"><div style="border:1px solid '+options.legend.labelBoxBorderColor+';padding:1px"><div style="width:4px;height:0;border:5px solid '+s.color+';overflow:hidden"></div></div></td>'+'<td class="legendLabel">'+label+'</td>');}if(rowStarted)fragments.push('</tr>');if(fragments.length==0)return;vartable='<table style="font-size:smaller;color:'+options.grid.color+'">'+fragments.join("")+'</table>';if(options.legend.container!=null)$(options.legend.container).html(table);else{varpos="",p=options.legend.position,m=options.legend.margin;if(m[0]==null)m=[m,m];if(p.charAt(0)=="n")pos+='top:'+(m[1]+plotOffset.top)+'px;';elseif(p.charAt(0)=="s")pos+='bottom:'+(m[1]+plotOffset.bottom)+'px;';if(p.charAt(1)=="e")pos+='right:'+(m[0]+plotOffset.right)+'px;';elseif(p.charAt(1)=="w")pos+='left:'+(m[0]+plotOffset.left)+'px;';varlegend=$('<div class="legend">'+table.replace('style="','style="position:absolute;'+pos+';')+'</div>').appendTo(placeholder);if(options.legend.backgroundOpacity!=0.0){// put in the transparent background// separately to avoid blended labels and// label boxesvarc=options.legend.backgroundColor;if(c==null){c=options.grid.backgroundColor;if(c&&typeofc=="string")c=$.color.parse(c);elsec=$.color.extract(legend,'background-color');c.a=1;c=c.toString();}vardiv=legend.children();$('<div style="position:absolute;width:'+div.width()+'px;height:'+div.height()+'px;'+pos+'background-color:'+c+';"> </div>').prependTo(legend).css('opacity',options.legend.backgroundOpacity);}}}// interactive featuresvarhighlights=[],redrawTimeout=null;// returns the data item the mouse is over, or null if none is foundfunctionfindNearbyItem(mouseX,mouseY,seriesFilter){varmaxDistance=options.grid.mouseActiveRadius,smallestDistance=maxDistance*maxDistance+1,item=null,foundPoint=false,i,j;for(i=0;i<series.length;++i){if(!seriesFilter(series[i]))continue;vars=series[i],axisx=s.xaxis,axisy=s.yaxis,points=s.datapoints.points,ps=s.datapoints.pointsize,mx=axisx.c2p(mouseX),// precompute some stuff to make the loop fastermy=axisy.c2p(mouseY),maxx=maxDistance/axisx.scale,maxy=maxDistance/axisy.scale;if(s.lines.show||s.points.show){for(j=0;j<points.length;j+=ps){varx=points[j],y=points[j+1];if(x==null)continue;// For points and lines, the cursor must be within a// certain distance to the data pointif(x-mx>maxx||x-mx<-maxx||y-my>maxy||y-my<-maxy)continue;// We have to calculate distances in pixels, not in// data units, because the scales of the axes may be differentvardx=Math.abs(axisx.p2c(x)-mouseX),dy=Math.abs(axisy.p2c(y)-mouseY),dist=dx*dx+dy*dy;// we save the sqrt// use <= to ensure last point takes precedence// (last generally means on top of)if(dist<=smallestDistance){smallestDistance=dist;item=[i,j/ps];}}}if(s.bars.show&&!item){// no other point can be nearbyvarbarLeft=s.bars.align=="left"?0:-s.bars.barWidth/2,barRight=barLeft+s.bars.barWidth;for(j=0;j<points.length;j+=ps){varx=points[j],y=points[j+1],b=points[j+2];if(x==null)continue;// for a bar graph, the cursor must be inside the barif(series[i].bars.horizontal?(mx<=Math.max(b,x)&&mx>=Math.min(b,x)&&my>=y+barLeft&&my<=y+barRight):(mx>=x+barLeft&&mx<=x+barRight&&my>=Math.min(b,y)&&my<=Math.max(b,y)))item=[i,j/ps];}}}if(item){i=item[0];j=item[1];ps=series[i].datapoints.pointsize;return{datapoint:series[i].datapoints.points.slice(j*ps,(j+1)*ps),dataIndex:j,series:series[i],seriesIndex:i};}returnnull;}functiononMouseMove(e){if(options.grid.hoverable)triggerClickHoverEvent("plothover",e,function(s){returns["hoverable"]!=false;});}functiononClick(e){triggerClickHoverEvent("plotclick",e,function(s){returns["clickable"]!=false;});}// trigger click or hover event (they send the same parameters// so we share their code)functiontriggerClickHoverEvent(eventname,event,seriesFilter){varoffset=eventHolder.offset(),pos={pageX:event.pageX,pageY:event.pageY},canvasX=event.pageX-offset.left-plotOffset.left,canvasY=event.pageY-offset.top-plotOffset.top;if(axes.xaxis.used)pos.x=axes.xaxis.c2p(canvasX);if(axes.yaxis.used)pos.y=axes.yaxis.c2p(canvasY);if(axes.x2axis.used)pos.x2=axes.x2axis.c2p(canvasX);if(axes.y2axis.used)pos.y2=axes.y2axis.c2p(canvasY);varitem=findNearbyItem(canvasX,canvasY,seriesFilter);if(item){// fill in mouse pos for any listeners out thereitem.pageX=parseInt(item.series.xaxis.p2c(item.datapoint[0])+offset.left+plotOffset.left);item.pageY=parseInt(item.series.yaxis.p2c(item.datapoint[1])+offset.top+plotOffset.top);}if(options.grid.autoHighlight){// clear auto-highlightsfor(vari=0;i<highlights.length;++i){varh=highlights[i];if(h.auto==eventname&&!(item&&h.series==item.series&&h.point==item.datapoint))unhighlight(h.series,h.point);}if(item)highlight(item.series,item.datapoint,eventname);}placeholder.trigger(eventname,[pos,item]);}functiontriggerRedrawOverlay(){if(!redrawTimeout)redrawTimeout=setTimeout(drawOverlay,30);}functiondrawOverlay(){redrawTimeout=null;// draw highlightsoctx.save();octx.clearRect(0,0,canvasWidth,canvasHeight);octx.translate(plotOffset.left,plotOffset.top);vari,hi;for(i=0;i<highlights.length;++i){hi=highlights[i];if(hi.series.bars.show)drawBarHighlight(hi.series,hi.point);elsedrawPointHighlight(hi.series,hi.point);}octx.restore();executeHooks(hooks.drawOverlay,[octx]);}functionhighlight(s,point,auto){if(typeofs=="number")s=series[s];if(typeofpoint=="number")point=s.data[point];vari=indexOfHighlight(s,point);if(i==-1){highlights.push({series:s,point:point,auto:auto});triggerRedrawOverlay();}elseif(!auto)highlights[i].auto=false;}functionunhighlight(s,point){if(s==null&&point==null){highlights=[];triggerRedrawOverlay();}if(typeofs=="number")s=series[s];if(typeofpoint=="number")point=s.data[point];vari=indexOfHighlight(s,point);if(i!=-1){highlights.splice(i,1);triggerRedrawOverlay();}}functionindexOfHighlight(s,p){for(vari=0;i<highlights.length;++i){varh=highlights[i];if(h.series==s&&h.point[0]==p[0]&&h.point[1]==p[1])returni;}return-1;}functiondrawPointHighlight(series,point){varx=point[0],y=point[1],axisx=series.xaxis,axisy=series.yaxis;if(x<axisx.min||x>axisx.max||y<axisy.min||y>axisy.max)return;varpointRadius=series.points.radius+series.points.lineWidth/2;octx.lineWidth=pointRadius;octx.strokeStyle=$.color.parse(series.color).scale('a',0.5).toString();varradius=1.5*pointRadius;octx.beginPath();octx.arc(axisx.p2c(x),axisy.p2c(y),radius,0,2*Math.PI,false);octx.stroke();}functiondrawBarHighlight(series,point){octx.lineWidth=series.bars.lineWidth;octx.strokeStyle=$.color.parse(series.color).scale('a',0.5).toString();varfillStyle=$.color.parse(series.color).scale('a',0.5).toString();varbarLeft=series.bars.align=="left"?0:-series.bars.barWidth/2;drawBar(point[0],point[1],point[2]||0,barLeft,barLeft+series.bars.barWidth,0,function(){returnfillStyle;},series.xaxis,series.yaxis,octx,series.bars.horizontal);}functiongetColorOrGradient(spec,bottom,top,defaultColor){if(typeofspec=="string")returnspec;else{// assume this is a gradient spec; IE currently only// supports a simple vertical gradient properly, so that's// what we support toovargradient=ctx.createLinearGradient(0,top,0,bottom);for(vari=0,l=spec.colors.length;i<l;++i){varc=spec.colors[i];if(typeofc!="string"){c=$.color.parse(defaultColor).scale('rgb',c.brightness);c.a*=c.opacity;c=c.toString();}gradient.addColorStop(i/(l-1),c);}returngradient;}}}$.plot=function(placeholder,data,options){varplot=newPlot($(placeholder),data,options,$.plot.plugins);/*var t0 = new Date(); var t1 = new Date(); var tstr = "time used (msecs): " + (t1.getTime() - t0.getTime()) if (window.console) console.log(tstr); else alert(tstr);*/returnplot;};$.plot.plugins=[];// returns a string with the date d formatted according to fmt$.plot.formatDate=function(d,fmt,monthNames){varleftPad=function(n){n=""+n;returnn.length==1?"0"+n:n;};varr=[];varescape=false;varhours=d.getUTCHours();varisAM=hours<12;if(monthNames==null)monthNames=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];if(fmt.search(/%p|%P/)!=-1){if(hours>12){hours=hours-12;}elseif(hours==0){hours=12;}}for(vari=0;i<fmt.length;++i){varc=fmt.charAt(i);if(escape){switch(c){case'h':c=""+hours;break;case'H':c=leftPad(hours);break;case'M':c=leftPad(d.getUTCMinutes());break;case'S':c=leftPad(d.getUTCSeconds());break;case'd':c=""+d.getUTCDate();break;case'm':c=""+(d.getUTCMonth()+1);break;case'y':c=""+d.getUTCFullYear();break;case'b':c=""+monthNames[d.getUTCMonth()];break;case'p':c=(isAM)?(""+"am"):(""+"pm");break;case'P':c=(isAM)?(""+"AM"):(""+"PM");break;}r.push(c);escape=false;}else{if(c=="%")escape=true;elser.push(c);}}returnr.join("");};// round to nearby lower multiple of basefunctionfloorInBase(n,base){returnbase*Math.floor(n/base);}})(jQuery);