Child Performer Protection Timeline

Drag the playhead · Pin a year · Get a Protection Gap Report

Super generates helpful tools and automates fact-checking across the internet proactively. If you enjoyed this tool, build your own with Super and share it with a friend.
`; const blob=new Blob([html],{type:"text/html"}); download(blob,`protection-gap-${report.year}.html`) } function download(blob,filename){ const url=URL.createObjectURL(blob); const a=document.createElement("a"); a.href=url;a.download=filename;a.click(); URL.revokeObjectURL(url) } function saveState(){ localStorage.setItem("cppt-state",JSON.stringify({pinnedYear,currentYear})) } function loadState(){ try{const s=JSON.parse(localStorage.getItem("cppt-state")||"{}"); if(s.currentYear)currentYear=s.currentYear; if(s.pinnedYear){pinnedYear=s.pinnedYear;renderReport(buildReport(pinnedYear))} }catch(e){} } function yearToX(year){ return timelineLeft+((year-YEAR_MIN)/(YEAR_MAX-YEAR_MIN))*(timelineRight-timelineLeft) } function xToYear(x){ return Math.round(YEAR_MIN+((x-timelineLeft)/(timelineRight-timelineLeft))*(YEAR_MAX-YEAR_MIN)) } function drawTimeline(p){ p.background(26,26,46); p.stroke(58,58,94);p.strokeWeight(1); p.line(timelineLeft,timelineBottom,timelineRight,timelineBottom); for(let y=1930;y<=2025;y+=10){ const x=yearToX(y); p.line(x,timelineBottom-5,x,timelineBottom+5); p.fill(138,138,154);p.noStroke();p.textAlign(p.CENTER,p.TOP);p.textSize(11);p.textFont("IBM Plex Mono"); p.text(y,x,timelineBottom+8) } PROTECTIONS.forEach((prot,i)=>{ const y=timelineTop+i*36+18; const activeStart=Math.max(prot.start,YEAR_MIN),activeEnd=Math.min(prot.end,YEAR_MAX); if(activeStart<=activeEnd){ const x1=yearToX(activeStart),x2=yearToX(activeEnd); p.fill(prot.color==="amber"?[255,183,0,180]:[78,205,196,180]); p.noStroke();p.rect(x1,y-12,x2-x1,24,4) } p.fill(245,245,240);p.noStroke();p.textAlign(p.LEFT,p.CENTER);p.textSize(12);p.textFont("IBM Plex Sans"); p.text(prot.name,timelineRight+12,y) }); INCIDENTS.forEach(inc=>{ const x=yearToX(inc.year); if(xtimelineRight)return; p.fill(255,107,107);p.noStroke();p.ellipse(x,timelineBottom-30,10,10); p.fill(255,107,107,200);p.textAlign(p.CENTER,p.BOTTOM);p.textSize(10);p.textFont("IBM Plex Mono"); p.text(inc.name,x,timelineBottom-40) }); const playheadX=yearToX(currentYear); p.stroke(255,183,0);p.strokeWeight(3); p.line(playheadX,timelineTop,playheadX,timelineBottom); p.fill(255,183,0);p.noStroke();p.ellipse(playheadX,timelineTop-8,16,16); p.fill(26,26,46);p.textAlign(p.CENTER,p.CENTER);p.textSize(10);p.textFont("IBM Plex Mono"); p.text(currentYear,playheadX,timelineTop-8); if(pinnedYear!==null&&pinnedYear!==currentYear){ const pinX=yearToX(pinnedYear); p.stroke(78,205,196);p.strokeWeight(2);p.drawingContext.setLineDash([6,4]); p.line(pinX,timelineTop,pinX,timelineBottom); p.drawingContext.setLineDash([]); p.fill(78,205,196);p.noStroke();p.triangle(pinX,timelineTop-12,pinX-6,timelineTop,pinX+6,timelineTop) } } function setup(){ const wrap=document.getElementById("canvas-wrap"); canvasW=wrap.clientWidth; canvasH=Math.min(420,Math.max(300,window.innerHeight*0.45)); const cnv=createCanvas(canvasW,canvasH); cnv.parent("canvas-wrap"); timelineLeft=60;timelineRight=canvasW-60;timelineTop=40;timelineBottom=canvasH-60;bandAreaH=timelineBottom-timelineTop; loadState(); textFont("IBM Plex Sans"); const report=buildReport(pinnedYear||currentYear); if(pinnedYear)renderReport(report); announce(`Timeline loaded. Year ${currentYear}. ${getActiveProtections(currentYear).length} active protections.`) } function draw(){ drawTimeline(this); if(isReplaying){ replayProgress+=0.003; if(replayProgress>=1){replayProgress=0;isReplaying=false;announce("Replay complete")} else{currentYear=Math.round(YEAR_MIN+replayProgress*(YEAR_MAX-YEAR_MIN))} } } function mousePressed(){ if(mouseX>=timelineLeft&&mouseX<=timelineRight&&mouseY>=timelineTop&&mouseY<=timelineBottom){ isDragging=true;currentYear=clamp(xToYear(mouseX),YEAR_MIN,YEAR_MAX);announce(`Year ${currentYear}`) } } function mouseDragged(){ if(isDragging){currentYear=clamp(xToYear(mouseX),YEAR_MIN,YEAR_MAX);announce(`Year ${currentYear}`);return false} } function mouseReleased(){isDragging=false} function touchStarted(){ if(touches.length===1){ const t=touches[0]; if(t.x>=timelineLeft&&t.x<=timelineRight&&t.y>=timelineTop&&t.y<=timelineBottom){ isDragging=true;currentYear=clamp(xToYear(t.x),YEAR_MIN,YEAR_MAX);announce(`Year ${currentYear}`);return false } } } function touchMoved(){ if(isDragging&&touches.length===1){ currentYear=clamp(xToYear(touches[0].x),YEAR_MIN,YEAR_MAX);announce(`Year ${currentYear}`);return false } } function touchEnded(){isDragging=false} function keyPressed(){ if(keyCode===LEFT_ARROW){currentYear=max(currentYear-1,YEAR_MIN);announce(`Year ${currentYear}`);return false} if(keyCode===RIGHT_ARROW){currentYear=min(currentYear+1,YEAR_MAX);announce(`Year ${currentYear}`);return false} if(keyCode===ENTER&&pinnedYear===null){pinYear();return false} } function clamp(v,min,max){return Math.max(min,Math.min(max,v))} function pinYear(){ pinnedYear=currentYear;const report=buildReport(pinnedYear);renderReport(report);saveState();announce(`Pinned ${pinnedYear}`) } function resetAll(){ currentYear=DEFAULT_YEAR;pinnedYear=null;isReplaying=false;replayProgress=0; document.getElementById("report").hidden=true; document.getElementById("exportJsonBtn").hidden=document.getElementById("exportHtmlBtn").hidden=true; document.getElementById("exportJsonBtn2").hidden=document.getElementById("exportHtmlBtn2").hidden=true; localStorage.removeItem("cppt-state");announce(`Reset to ${DEFAULT_YEAR}`) } function startReplay(){ isReplaying=true;replayProgress=0;currentYear=YEAR_MIN;announce("Replay started") } function windowResized(){ const wrap=document.getElementById("canvas-wrap"); canvasW=wrap.clientWidth; canvasH=Math.min(420,Math.max(300,window.innerHeight*0.45)); resizeCanvas(canvasW,canvasH); timelineLeft=60;timelineRight=canvasW-60;timelineBottom=canvasH-60;bandAreaH=timelineBottom-timelineTop; } document.getElementById("pinBtn").addEventListener("click",pinYear); document.getElementById("resetBtn").addEventListener("click",resetAll); document.getElementById("replayBtn").addEventListener("click",startReplay); document.getElementById("closeReport").addEventListener("click",()=>{document.getElementById("report").hidden=true;announce("Report closed")}); document.getElementById("exportJsonBtn").addEventListener("click",()=>exportJSON(buildReport(pinnedYear))); document.getElementById("exportHtmlBtn").addEventListener("click",()=>exportHTML(buildReport(pinnedYear))); document.getElementById("exportJsonBtn2").addEventListener("click",()=>exportJSON(buildReport(pinnedYear))); document.getElementById("exportHtmlBtn2").addEventListener("click",()=>exportHTML(buildReport(pinnedYear)));