PageSourceSearch

https://louisdecharson.github.io/js/grapher.min.js

js louisdecharson.github.io collected 2026-10-03 09:32:11 UTC 17,470 bytes, 43 lines download raw bytes

1class Grapher{constructor(id,options={},width=null,height=null){this.id=id;this.el=document.getElementById(this.id);this.width=width||this.el.offsetWidth;this.height=height||this.el.offsetHeight;this._fontSize=parseFloat(window.getComputedStyle(this.el).fontSize);this.container=d3.select(`#${this.id}`).append('span').attr('class','grapherContainer');this.svg=this.container.append("svg").attr("id",this.id+'_svg').attr("width",this.width).attr("height",this.height);if(options.type!=undefined&&options.type=="sparkline"){this.isSparkline=!0}else{this.isSparkline=!1}
2this._margin={};this.margin={};this._options={"data":[],"x":{"name":"x","scale":"scaleLinear","tickFormat":d3.format('.3s'),"parse":null,"label":null,"domain":null},"y":{"name":"y","scale":"scaleLinear","tickFormat":d3.format('.3s'),"parse":null,"label":null,"domain":null},"category":{"name":null,"parse":d=>d},"categories":[],"type":"line","style":{"colors":["#1abb9b","#3497da","#9a59b5","#f0c30f","#e57e22","#e64c3c","#7f8b8c","#CC6666","#9999CC","#66CC99"],"barWidth":0.8,"strokeWidth":3,"dotSize":4,"grid":!0,"tooltipColor":"#181818","tooltipBackgroundColor":"#ffffff","tooltipOpacity":"0.8","tooltipLineColor":"#000000"},"legend":{"hide":!1,"x":15,"y":this._margin.top,"interstice":25,"backgroundColor":"#ffffff","opacity":"0.9"},"download":{"filename":`data_${this.id}_${Date.now()}.csv`},"sparkline":{"range":null,"textLastPoint":!0,"strokeWidth":1,"lineColor":"#000000","circleColor":"#f00","textColor":"#f00","textFontSize":"85%","textFontWeight":"600","rangeFillColor":"#ccc"}};this.options=options}
3get margin(){return this._margin}
4set margin({top=10,right=((x)=>x<400?20:30),bottom,left=(x)=>x<400?40:60}={}){if(this.isSparkline){this._margin={"top":0,"right":0,"bottom":0,"left":0}}else{this._margin={"top":top,"right":typeof right=="function"?right(this.width):right,"bottom":this._fontSize*2+20,"left":typeof left=="function"?left(this.width):left}}
5this._innerDimensions()}
6_innerDimensions(){this.innerWidth=this.width-this._margin.left-this._margin.right;this.innerHeight=this.height-this._margin.top-this._margin.bottom;this._createSVG()};_createSVG(){this.svg.select(`#${this.id}_g`).remove();this.g=this.svg.append("g").attr('id',this.id+'_g').attr('transform',`translate(${this._margin.left},${this._margin.top})`);this._addOverlay()};_addOverlay(){this.g.append("rect").attr("class","overlay").attr("fill","none").attr("style","pointer-events:all;").attr("width",this.innerWidth).attr("height",this.innerHeight)};get options(){return this._options}
7set options(opt){for(const key of Object.keys(opt)){if(Object.keys(this._options).indexOf(key)>-1){if(this._options[key].constructor==Object){for(const subkey of Object.keys(opt[key])){if(Object.keys(this._options[key]).indexOf(subkey)>-1){this._options[key][subkey]=opt[key][subkey]}}}else{this._options[key]=opt[key]}}}
8if(!opt.category&&!this._options.category.name){opt.categories=['category']}
9if(!opt.categories||this._options.categories.length==0){this._getCategoriesList()}
10if(opt.x&&!opt.x.scale&&opt.x.parse&&opt.x.parse.toString().includes('d3.timeParse')){this._options.x.scale="scaleTime"}
11if(opt.y&&!opt.y.scale&&opt.y.parse&&opt.y.parse.toString().includes('d3.timeParse')){this._options.y.scale="scaleTime"}
12if(opt.x&&!opt.x.tickFormat&&opt.x.parse&&opt.x.parse.toString().includes('d3.timeParse')){this._options.x.tickFormat=d3.timeFormat(Grapher.findTimeFormat(opt.x.parse.toString()))}
13if(opt.y&&!opt.y.tickFormat&&opt.y.parse&&opt.y.parse.toString().includes('d3.timeParse')){this._options.y.tickFormat=d3.timeFormat(Grapher.findTimeFormat(opt.y.parse.toString()))}
14this.color=d3.scaleOrdinal().domain(this._options.categories).range(this._options.style.colors);this._parseData();this.margin={left:(this._options.y.label?80:60)}}
15_getCategoriesList(data=this._options.data,category=this._options.category.name){if(!this._options.category.name){this._options.categories=this._options.y.label?[this._options.y.label]:[this._options.y.name]}else{this._options.categories=d3.set(data.map(d=>d[category])).values()}};_parseData(){if(this._options.x.parse||this._options.y.parse){let data=[];for(const d of this._options.data){let _={};_[this._options.x.name]=this._options.x.parse?this._options.x.parse(d[this._options.x.name]):d[this._options.x.name];_[this._options.y.name]=this._options.y.parse?this._options.y.parse(d[this._options.y.name]):d[this._options.y.name];if(this._options.category.name){_[this._options.category.name]=d[this._options.category.name]}else{_.category=this._options.y.label?this._options.y.label:this._options.y.name}
16data.push(_)}
17this._options.data=data;this._options.category.name=this._options.category.name||'category'}}
18extent(variable,scale,enforceMinimumAt0=!1,data=this._options.data){if(scale=="scaleLog"){let min=d3.min(data.filter(d=>d[variable]>0),d=>d[variable]),max=d3.max(data,d=>d[variable]);return[min,max]}else{if(enforceMinimumAt0){return[Math.min(d3.min(data,d=>d[variable]),0),d3.max(data,d=>d[variable])]}else{return d3.extent(data,d=>d[variable])}}}
19static findTimeFormat(str){let a=str.indexOf('d3.timeParse')+14,b=str.slice(a),c=b.indexOf(')')-1;return b.slice(0,c)}
20static unique(arr){if(arr.every(e=>e instanceof Date)){let sortDate=(a,b)=>a==b?0:a>b?1:-1;return[...new Set(arr.map(r=>r.getTime()))].map((r)=>(new Date(r))).sort(sortDate)}else{return Array.from(new Set(arr)).sort()}}
21static getOptimalPrecision(n){return `.${Math.abs(Math.floor(Math.log10((n > 0 ? n : 1))))+1}%`}
22sparkline(){this.x=d3.scaleLinear().range([0,this.width-2]).domain((this._options.x.domain?this._options.x.domain:this.extent(this._options.x.name,this._options.x.scale)));this.xValues=Grapher.unique(this._options.data.map(d=>d[this._options.x.name]));this.y=d3.scaleLinear().range([this.height-4,0]).domain(this._options.y.domain?this._options.y.domain:this.extent(this._options.y.name,this._options.y.scale));this.svg.selectAll('g.sparkline').remove();this.container.attr('style','vertical-align:middle; display:inline-block;');this.g.attr('transform','translate(0,2)').attr('class','sparkline');if(this._options.sparkline.range){let dataRange=this._options.data.map(d=>{d.minRange=this._options.sparkline.range[0];d.maxRange=this._options.sparkline.range[1];return d});this.g.append('path').datum(dataRange).attr('fill',this._options.sparkline.rangeFillColor).attr('stroke','none').attr('d',d3.area().x(d=>this.x(d[this._options.x.name])).y0(d=>this.y(d.minRange)).y1(d=>this.y(d.maxRange)))}
23this.sparkLine=this.g.append('path').datum(this._options.data).attr('class','sparkLine').attr('fill','none').attr('stroke-width',this._options.sparkline.strokeWidth).attr('stroke',this._options.sparkline.lineColor).attr('d',d3.line().curve(d3.curveBasis).x(d=>this.x(d[this._options.x.name])).y(d=>this.y(d[this._options.y.name])));this.lastPoint=this._options.data.slice(-1)[0];this.sparkCircle=this.g.append('circle').attr('class','sparkCircle').attr('cx',this.x(this.lastPoint[this._options.x.name])).attr('cy',this.y(this.lastPoint[this._options.y.name])).attr('r',1.5).attr('fill',this._options.sparkline.circleColor).attr('stroke','none');if(this._options.sparkline.textLastPoint){this.sparkText=d3.select(`#${this.id}`).append('span').attr('class','sparkText').text(this._options.y.tickFormat(this.lastPoint[this._options.y.name])).attr('style',`font-size:${this._options.sparkline.textFontSize}; font-weight:${this._options.sparkline.textFontWeight}; color: ${this._options.sparkline.textColor}; margin-bottom:${this.height/2} vertical-align:middle; display:inline-block;`)}
24if(this._options.y.label){this.sparkText=d3.select(`#${this.id}`).append('span').attr('class','sparkText').text(this._options.y.label).attr('style',`font-size:${this._options.sparkline.textFontSize}; font-weight:${this._options.sparkline.textFontWeight}; margin-bottom:${this.height/2} vertical-align:middle; display:inline-block; padding-left: 0.4rem;`)}}
25draw(options){if(options){this.options=options}
26this.g.selectAll('g.x.axis').remove();this.g.selectAll('g.y.axis').remove();this.g.selectAll('g.yGrid').remove();if(this._options.type=="sparkline"){this.sparkline();this.addTooltip()}else{this.addX();if(this._options.type=="bar"){this.addX2()}
27if(this._options.x.label){this.addXLabel()}
28this.addY();if(this._options.y.label){this.addYLabel()}
29this._draw();this.addTooltip();this.addLegend()}}
30static downloadData(){let domEl=document.createElement('a');domEl.id="download";domEl.download=this._options.download.name;domEl.href=URL.createObjectURL(new Blob([to_csv(this._options.data)]));domEl.click()};addX(){this.xValues=Grapher.unique(this._options.data.map(d=>d[this._options.x.name]));this.xNbValues=this.xValues.length;this.xWidth=this.innerWidth/this.xNbValues;this.xRange=this._options.type=="bar"?[this.xWidth/2,this.innerWidth-this.xWidth/2]:[0,this.innerWidth];this.xNbTicks=d3.min([parseInt((this.width-100)/100),this.xNbValues]);this.x=d3[this._options.x.scale]().domain((this._options.x.domain?this._options.x.domain:this.extent(this._options.x.name,this._options.x.scale))).nice().range(this.xRange);this.g.append("g").attr("class","x axis").attr("transform",`translate(0, ${this.innerHeight}
30)`).call(d3.axisBottom(this.x).ticks(this.xNbTicks).tickFormat(this._options.x.tickFormat));if(!this._options.style.grid){this.g.append("g").attr("class","x axis").attr("transform",`translate(0, ${this.innerHeight + 0.5})`).attr("opacity","1").append('line').attr('stroke','currentColor').attr('x2',this.innerWidth)}}
31addXLabel(){this.g.append("text").attr("class","xLabel axisLabel").attr("transform",`translate(${this.innerWidth/2},${this.height-this._margin.bottom/2})`).style("text-anchor","middle").text(this._options.x.label)};addX2(){this.x2=d3.scaleBand().domain(this._options.categories).rangeRound([-this.xWidth/2*this._options.style.barWidth,this.xWidth/2*this._options.style.barWidth])}
32addY(){this.yVar=this._options.y.name;this.y=d3[this._options.y.scale]().domain(this._options.y.domain?this._options.y.domain:this.extent(this._options.y.name,this._options.y.scale,this._options.type=="bar")).nice().range([this.innerHeight,0]);this.g.append("g").attr('class','y axis').call(d3.axisLeft(this.y).tickFormat(this._options.y.tickFormat));if(this._options.style.grid){this.addYGrid()}
33if(this.y.domain()[0]<0&&this._options.type=="bar"){this.g.append("g").attr("class","x axis").attr("transform",`translate(0, ${this.y(0)})`).attr("opacity","1").append('line').attr('stroke','currentColor').attr('x2',this.innerWidth)}}
34addYGrid(){this.yGrid=svg=>svg.call(d3.axisRight(this.y).tickSize(this.innerWidth).tickFormat(this._options.y.tickFormat)).call(g=>g.selectAll('.domain').remove()).call(g=>g.selectAll(".tick:not(:first-of-type) line").attr("stroke-opacity",0.5).attr("stroke-dasharray","2,2")).call(g=>g.selectAll(".tick text").remove());this.g.append("g").attr('class','yGrid').call(this.yGrid)}
35addYLabel(){this.g.append("text").attr("transform","rotate(-90)").attr("y",0-this._margin.left).attr("x",0-(this.innerHeight/2)).attr("dy","1em").attr("class","yLabel axisLabel").style("text-anchor","middle").text(this._options.y.label)}
36_draw(){this.g.selectAll('path.lines').remove();this.g.selectAll(`rect.bar`).remove();this.g.selectAll(`circle.dots`).remove();for(const category of this._options.categories){if(['line','dotted-line'].indexOf(this._options.type)>-1){this.g.append('path').datum(this._options.data.filter(d=>d[this._options.category.name]===category)).attr('class','lines').attr('fill','none').attr('stroke',d=>this.color(category)).attr('stroke-width',this._options.style.strokeWidth).attr('d',d3.line().y(d=>this.y(d[this._options.y.name])).defined(d=>d[this._options.y.name]).x(d=>this.x(d[this._options.x.name])))};if(this._options.type=="bar"){this.g.selectAll(`rect.bar.${category.replace(/ /g,'_')}`).data(this._options.data.filter(d=>d[this._options.category.name]===category)).join('rect').attr('class',`bar ${category.replace(/ /g,'_')}`).attr('fill',d=>this.color(category)).attr('x',d=>this.x(d[this._options.x.name])+this.x2(category)).attr('width',this.x2.bandwidth()).attr('y',d=>d[this._options.y.name]>0?this.y(d[this._options.y.name]):this.y(0)).attr('height',d=>Math.abs(this.y(0)-this.y(d[this._options.y.name]))).on("mouseover",function(d){d3.select(this).attr("fill",function(){return d3.rgb(d3.select(this).style("fill")).darker(0.5)})}).on("mouseout",function(d){d3.select(this).attr("fill",function(){return d3.rgb(d3.select(this).style("fill")).brighter(0.5)})})}};if(['dotted-line','dot'].indexOf(this._options.type)>-1){this.g.selectAll("circle").data(this._options.data).join("circle").style("fill",d=>this.color(d[this._options.category.name])).attr("class","dots").attr("r",this._options.style.dotSize).attr("cx",(d,i)=>this.x(d[this._options.x.name])).attr("cy",(d,i)=>this.y(d[this._options.y.name]))}}
37_buildTooltip(g,data,mouseX,mouseY){if(!data)return g.style("display","none");g.style("display",null);
37const xLine=g.selectAll("line").data([null]).join("line").attr("class","tooltip_line").attr('style',`stroke: ${this._options.style.tooltipLineColor};`).attr("x1",mouseX).attr("x2",mouseX).attr("y1",0).attr("y2",this.innerHeight);if(this._options.type!="bar"){const dots=g.selectAll("circle").data(data.slice(1)).join("circle").style("fill",d=>this.isSparkline?this._options.sparkline["circle-color"]:this.color(d[this._options.category.name])).attr("r",this.isSparkline?2:5).attr("cx",(d,i)=>this.x(d[this._options.x.name])).attr("cy",(d,i)=>this.y(d[this._options.y.name]))}
38const path=g.selectAll("rect").data([null]).join("rect").attr("class","rect_tooltip").attr("style",`fill:${this._options.style.tooltipBackgroundColor}; fill-opacity: ${this._options.style.tooltipOpacity};`);let text;if(!this.isSparkline){text=g.selectAll("text").data([null]).join("text").call(text=>text.selectAll('tspan').data(data).join("tspan").attr("x",0).attr("y",(d,i)=>`${i * 1.1}em`).attr("class","tooltip_text").style("font-weight","bold").style("fill",(d,i)=>i===0?this._options.style.tooltipColor:this.color(d[this._options.category.name])).text((d,i)=>i===0?this._options.x.tickFormat(d):`${this._options.category.parse(d[this._options.category.name])}: ${this._options.y.tickFormat(d[this._options.y.name])}`))}else{text=g.selectAll("text").data([null]).join("text").call(text=>text.selectAll('tspan').data(data.slice(1)).join("tspan").attr("x",0).attr("y",(d,i)=>`${i * 1.1}em`).attr("class","tooltip_text").style("fill",(d,i)=>this._options.style.tooltipColor).text((d,i)=>`${this._options.y.tickFormat(d[this._options.y.name])}`).attr('style',`font-size:${this._options.sparkline.textFontSize};`))}
39const{xx,yy,width:w,height:h}=text.node().getBBox();let text_x=w+mouseX+10>this.innerWidth?mouseX-w-10:mouseX+10,text_y=h+mouseY-20>this.innerHeight?mouseY-h:mouseY;text.attr("transform",`translate(${text_x},${text_y})`);path.attr("x",text_x-5).attr("y",text_y-20).attr("rx",5).attr("width",w+10).attr("height",h+10);return!0}
40_getMouseData(mouseX){let mouseXValue_=this.x.invert(mouseX),mouseIndex=d3.bisector(d=>d).left(this.xValues,mouseXValue_),a=mouseIndex>0?this.xValues[mouseIndex-1]:0,b=mouseIndex>this.xValues.length-1?this.xValues.slice(-1)[0]:this.xValues[mouseIndex];let mouseXValue=mouseXValue_-a>b-mouseXValue_?b:a,mouseYValues=this._options.data.filter(d=>d[this._options.x.name].toString()==mouseXValue.toString()).sort((a,b)=>b[this._options.y.name]-a[this._options.y.name]);return[mouseXValue].concat(mouseYValues)}
41addTooltip(){this.g.selectAll("g.tooltip_container").remove();this.tooltip=this.g.append("g").attr("class","tooltip_container");let that=this;this.g.on("touchmove mousemove",function(){let mouse_x=d3.mouse(this)[0],mouse_y=d3.mouse(this)[1];if(mouse_x>0){let mouseData=that._getMouseData(mouse_x);that._buildTooltip(that.tooltip,mouseData,that.x(mouseData[0]),mouse_y+30)}});this.g.on("touchend mouseleave",()=>{this.tooltip.call(this._buildTooltip,null)})}
42addLegend(){this.g.selectAll('.legend').remove();this.legend=this.g.append('g').attr('class','legend');this.legendBackground=this.legend.selectAll('rect').data([null]).join("rect").attr("class","rect_legend").attr("style",`fill:${this._options.legend.backgroundColor}; fill-opacity: ${this._options.legend.opacity};`);this.legendDots=this.legend.selectAll(".dots-legend").data(this._options.categories);this.legendDots.exit().remove();this.legendDots.enter().append("circle").merge(this.legendDots).attr("cx",this._options.legend.x+10).attr("cy",(d,i)=>this._options.legend.y+i*this._options.legend.interstice).attr("r",3).attr('class','dots-legend').style("fill",d=>this.color(d));this.legendLabels=this.legend.selectAll(".labels-legend").data(this._options.categories);this.legendLabels.exit().remove();this.legendLabels.enter().append("text").merge(this.legendLabels).attr("x",this._options.legend.x+30).attr("y",(d,i)=>this._options.legend.y+i*this._options.legend.interstice).style("fill",d=>this.color(d)).text(t=>this._options.category.parse(t)).attr("text-anchor","left").style("alignment-baseline","middle").attr('class','labels-legend');let x,y,w,h;try{let _=this.legend.node().getBBox();x=_.x,y=_.y,w=_.width,h=_.height}catch(err){x=85,y=5,w=0,h=0}
43this.legendBackground.attr('x',this._options.legend.x-5).attr('y',this._options.legend.y-10).attr('width',(w>0?w+20:d3.max(this._options.categories.map(d=>d.length))*10+10)).attr('height',(h>0?h+10:this._options.categories.length*this._options.legend.interstice+10))}}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.