1import{P as t}from"./CBEthj9I.js";function e(t){const{radius:e,change:i,symbolText:s,changeText:n,fontBold:a,fontNormal:r,image:o,iconDY:h,textColor:c,positiveColor:l,negativeColor:d,dpr:u=1}=t,g=2*(e+2),b=e+2,p=document.createElement("canvas");p.width=Math.ceil(g*u),p.height=Math.ceil(g*u);const m=p.getContext("2d");m.scale(u,u);const f=function(t){const e=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(t);return e?{r:parseInt(e[1],16),g:parseInt(e[2],16),b:parseInt(e[3],16)}:{r:144,g:144,b:160}}(i>0?l:i<0?d:"#9090a8"),{r:v,g:y,b:_}=f,T=m.createRadialGradient(b-.3*e,b-.3*e,.05*e,b,b,e);T.addColorStop(0,"rgba(255,255,255,0.22)"),T.addColorStop(.35,`rgba(${v},${y},${_},0.05)`),T.addColorStop(.78,`rgba(${v},${y},${_},0.16)`),T.addColorStop(1,`rgba(${v},${y},${_},0.50)`),m.beginPath(),m.arc(b,b,e,0,2*Math.PI),m.closePath(),m.fillStyle=T,m.fill(),m.strokeStyle=`rgba(${v},${y},${_},0.80)`,m.lineWidth=1.5,m.stroke(),m.save(),m.beginPath(),m.arc(b,b,e,0,2*Math.PI),m.clip();const w=m.createRadialGradient(b-.3*e,b-.36*e,0,b-.3*e,b-.36*e,.5*e);if(w.addColorStop(0,"rgba(255,255,255,0.35)"),w.addColorStop(.45,"rgba(255,255,255,0.06)"),w.addColorStop(1,"rgba(255,255,255,0)"),m.fillStyle=w,m.fillRect(0,0,g,g),m.restore(),o&&!o._broken&&o.complete){const t=.62*e;m.save(),m.beginPath(),m.arc(b,b+h,t/2,0,2*Math.PI),m.clip(),m.imageSmoothingEnabled=!0,m.imageSmoothingQuality="high",m.drawImage(o,b-t/2,b+h-t/2,t,t),m.restore()}const x=b+.4*e,C=b+.64*e;return m.textAlign="center",m.imageSmoothingEnabled=!0,m.imageSmoothingQuality="high",m.font=a,m.fillStyle=c,m.fillText(s,b,x),m.font=r,m.fillStyle=i>0?l:i<0?d:c,m.fillText(n,b,C),p}class i{canvas;opts;isInIframe;wheelVelocity=0;wheelRafId=0;touchMode="none";activeTouchId=null;touchStartPos={x:0,y:0};touchStartClient={x:0,y:0};touchStartTime=0;touchHitResult=null;inertiaVX=0;inertiaVY=0;inertiaRafId=0;lastMoveTime=0;_handleWheel;_handleTouchStart;_handleTouchMove;_handleTouchEnd;constructor(t){this.canvas=t.canvas,this.opts=t;try{this.isInIframe=window.self!==window.top}catch{this.isInIframe=!0}this._handleWheel=this.handleWheel.bind(this),this._handleTouchStart=this.handleTouchStart.bind(this),this._handleTouchMove=this.handleTouchMove.bind(this),this._handleTouchEnd=this.handleTouchEnd.bind(this),this.canvas.addEventListener("wheel",this._handleWheel,{passive:!0}),this.canvas.addEventListener("touchstart",this._handleTouchStart,{passive:!1}),this.canvas.addEventListener("touchmove",this._handleTouchMove,{passive:!1}),this.canvas.addEventListener("touchend",this._handleTouchEnd,{passive:!1}),this.canvas.addEventListener("touchcancel",this._handleTouchEnd,{passive:!1}),this.isInIframe&&(this.canvas.style.touchAction="pan-y")}destroy(){this.canvas.removeEventListener("wheel",this._handleWheel),this.canvas.removeEventListener("touchstart",this._handleTouchStart),this.canvas.removeEventListener("touchmove",this._handleTouchMove),this.canvas.removeEventListener("touchend",this._handleTouchEnd),this.canvas.removeEventListener("touchcancel",this._handleTouchEnd),cancelAnimationFrame(this.wheelRafId),this.stopInertia()}pageScrollBy(t,e){window.scrollBy(t,e)}flushWheel=()=>{if(Math.abs(this.wheelVelocity)<.5)return this.wheelVelocity=0,void(this.wheelRafId=0);this.pageScrollBy(0,this.wheelVelocity),this.wheelVelocity*=.82,this.wheelRafId=requestAnimationFrame(this.flushWheel)};handleWheel(t){}runInertia=()=>{if(Math.abs(this.inertiaVX)<.4&&Math.abs(this.inertiaVY)<.4)return this.inertiaVX=0,this.inertiaVY=0,void(this.inertiaRafId=0);this.pageScrollBy(this.inertiaVX,this.inertiaVY),this.inertiaVX*=.93,this.inertiaVY*=.93,this.inertiaRafId=requestAnimationFrame(this.runInertia)};stopInertia(){this.inertiaRafId&&(cancelAnimationFrame(this.inertiaRafId),this.inertiaRafId=0),this.inertiaVX=0,this.inertiaVY=0}getTouchPos(t){const e=this.canvas.getBoundingClientRect(),i=Math.min(window.devicePixelRatio||1,2);return{x:(t.clientX-e.left)*i,y:(t.clientY-e.top)*i}}handleTouchStart(t){if(null!==this.activeTouchId)return;this.stopInertia();const e=t.changedTouches[0];this.activeTouchId=e.identifier;const i=this.getTouchPos(e);this.touchStartPos={...i},this.touchStartClient={x:e.clientX,y:e.clientY},this.touchStartTime=Date.now(),this.lastMoveTime=performance.now(),this.touchHitResult=this.opts.hitTest(i.x,i.y),this.touchMode="none"}handleTouchMove(t){const e=Array.from(t.changedTouches).find(t=>
1t.identifier===this.activeTouchId);if(!e)return;const i=this.getTouchPos(e),s=Math.abs(e.clientX-this.touchStartClient.x),n=Math.abs(e.clientY-this.touchStartClient.y);if("none"===this.touchMode){if(s+n<8)return;n>s&&n>10?this.touchMode="scroll":this.touchHitResult?(this.touchMode="bubble",this.opts.onDragStart(this.touchStartPos.x,this.touchStartPos.y,this.touchHitResult)):this.touchMode="scroll"}if("bubble"===this.touchMode)t.preventDefault(),this.opts.onDragMove(i.x,i.y);else if("scroll"===this.touchMode){if(this.isInIframe)return;const t=this.touchStartClient.x-e.clientX,i=this.touchStartClient.y-e.clientY;this.pageScrollBy(t,i);const s=performance.now(),n=Math.max(1,s-this.lastMoveTime);this.inertiaVX=t/n*16,this.inertiaVY=i/n*16,this.lastMoveTime=s,this.touchStartClient.x=e.clientX,this.touchStartClient.y=e.clientY}}handleTouchEnd(t){const e=Array.from(t.changedTouches).find(t=>t.identifier===this.activeTouchId);if(!e)return;const i=this.getTouchPos(e);if("bubble"===this.touchMode&&(t.preventDefault(),this.opts.onDragEnd(i.x,i.y),this.touchHitResult)){const t=i.x-this.touchStartPos.x,e=i.y-this.touchStartPos.y,s=Math.sqrt(t*t+e*e),n=Date.now()-this.touchStartTime;s<this.opts.clickDistance&&n<this.opts.clickTime&&this.opts.onBubbleTap(this.touchHitResult)}!this.isInIframe&&"scroll"===this.touchMode&&(Math.abs(this.inertiaVX)>.4||Math.abs(this.inertiaVY)>.4)&&(this.inertiaRafId=requestAnimationFrame(this.runInertia)),this.touchMode="none",this.activeTouchId=null,this.touchHitResult=null}}const s=new Map;function n(t){return t?"/api/image/coingecko/"+t.replace("https://coin-images.coingecko.com/","").replace("/large/","/small/"):""}function a(t,e,i){if(t.measureText(e).width<=i)return e;const s="...";let n=e;for(;n.length>0;)if(n=n.slice(0,-1),t.measureText(n+s).width<=i)return n+s;return s}class r extends t.Scene{config;bubbleSprites=[];bubbleDataMap=new Map;scrollHandler=null;lastBuiltW=0;lastBuiltH=0;resizeTimeout=null;rebuildGeneration=0;measuring=null;_lastCoins=[];dpr=1;dragConstraint=null;draggedBubble=null;_forceVec={x:0,y:0};constructor(){super({key:"BubbleScene"})}init(t){this.config=t.config}create(){this.dpr=Math.min(window.devicePixelRatio||1,2);const t=document.createElement("canvas");this.measuring=t.getContext("2d"),this.matter.world.setGravity(0,0),this.input.on("pointerdown",t=>{if(!t.wasTouch){const e=this.findBubbleAt(t.worldX,t.worldY);e&&(e.sprite.setData("_downX",t.worldX),e.sprite.setData("_downY",t.worldY),e.sprite.setData("_downTime",Date.now()),this.draggedBubble=e.data,this.dragConstraint=this.matter.add.worldConstraint(e.sprite.body,0,.12,{damping:.3,pointA:{x:t.worldX,y:t.worldY}}),this.game.canvas.style.cursor="grabbing")}}),this.input.on("pointermove",t=>{if(!t.wasTouch)if(this.dragConstraint)this.dragConstraint.pointA.x=t.worldX,this.dragConstraint.pointA.y=t.worldY;else{const e=this.findBubbleAt(t.worldX,t.worldY);this.game.canvas.style.cursor=e?"pointer":"default"}}),this.input.on("pointerup",t=>{if(!t.wasTouch){this.dragConstraint&&(this.draggedBubble&&(this.draggedBubble.idle.anchor.x=t.worldX,this.draggedBubble.idle.anchor.y=t.worldY,this.draggedBubble=null),this.matter.world.removeConstraint(this.dragConstraint,!0),this.dragConstraint=null),this.game.canvas.style.cursor="default";const e=this.findBubbleAt(t.worldX,t.worldY);if(!e)return;const i=e.sprite.getData("_downX"),s=e.sprite.getData("_downY"),n=e.sprite.getData("_downTime");if(null==i)return;const a=t.worldX-i,r=t.worldY-s,o=Math.sqrt(a*a+r*r),h=Date.now()-n;o<6&&h<250&&this.config.onCoinClick(e.data.coin)}}),this.scrollHandler=new i({canvas:this.game.canvas,hitTest:(t,e)=>{const i=this.findBubbleAt(t,e);return i?i.data:null},onBubbleTap:t=>{this.config.onCoinClick(t.coin)},onDragStart:(t,e)=>{const i=this.findBubbleAt(t,e);i&&(this.draggedBubble=i.data,this.dragConstraint=this.matter.add.worldConstraint(i.sprite.body,0,.12,{damping:.3,pointA:{x:t,y:e}}),this.game.canvas.style.cursor="grabbing")},onDragMove:(t,e)=>{this.dragConstraint&&(this.dragConstraint.pointA.x=t,this.dragConstraint.pointA.y=e)},onDragEnd:()=>{if(this.dragConstraint){if(this.draggedBubble){const t=this.dragConstraint.bodyB;this.draggedBubble.idle.anchor.x=t.position.x,this.draggedBubble.idle.anchor.y=t.position.y,this.draggedBubble=null}this.matter.world.removeConstraint(this.dragConstraint,!0),this.dragConstraint=null}this.game.canvas.style.cursor="default"},clickDistance:6,clickTime:250}),this.scale.on("resize",t=>{const e=Math.abs(t.width-this.lastBuiltW),i=Math.abs(t.height-this.lastBuiltH);e<2&&i<80||(this.resizeTimeout&&clearTimeout(this.resizeTimeout),this.resizeTimeout=window.setTimeout(()=>{this.rebuildBubbles(this._lastCoins)},150))}),this.config.onReady(this)}findBubbleAt(t,e){for(const i of this.bubbleSprites){const s=this.bubbleDataMap.get(i);if(!s)continue;const n=t-i.x,a=e-i.y;if(n*n+a*a<=s.radius*s.radius)return{sprite:i,data:s}}return null}async rebuildBubbles(t){if(this._lastCoins=t,!t||!t.length)return;const i=++this.rebuildGeneration,r=this.scale.width,o=this.scale.height;if(r<10||o<10)return;this.lastBuiltW=r,this.lastBuiltH=o;for(const e of this.bubbleSprites)e.destroy();this.bubbleSprites=[],this.bubbleDataMap.clear();for(const e of this.textures.getTextureKeys())e.startsWith("bubble-")&&this.textures.remove(e);const h=this.config.getColors(),c=this.config.getTimeframe(),l=this.config.getBubbleSizing(
1);"transparent"===h.background?(this.cameras.main.setBackgroundColor("rgba(0,0,0,0)"),this.cameras.main.transparent=!0):(this.cameras.main.transparent=!1,this.cameras.main.setBackgroundColor(h.background));const d=r*o,u=r<640?1.08:1,g=t.map(t=>{if("marketcap"===l)return t.market_cap||1;let e=0;return"1h"===c?e=t.price_change_percentage_1h_in_currency??0:"24h"===c?e=t.price_change_percentage_24h_in_currency??0:"7d"===c?e=t.price_change_percentage_7d_in_currency??0:"30d"===c?e=t.price_change_percentage_30d_in_currency??0:"1y"===c&&(e=t.price_change_percentage_1y_in_currency??0),Math.abs(e)+1}),b=g.reduce((t,e)=>t+e,0),p=Math.sqrt(.65*d*u/(Math.PI*b)),m=.14*Math.min(r,o),f=.025*Math.min(r,o);this.matter.world.setBounds(0,0,r,o,120);const v=this.measuring;for(let y=0;y<t.length;y++){const l=t[y];let d=0;"1h"===c?d=l.price_change_percentage_1h_in_currency??0:"24h"===c?d=l.price_change_percentage_24h_in_currency??0:"7d"===c?d=l.price_change_percentage_7d_in_currency??0:"30d"===c?d=l.price_change_percentage_30d_in_currency??0:"1y"===c&&(d=l.price_change_percentage_1y_in_currency??0);const u=Math.min(m,Math.max(f,Math.sqrt(g[y])*p));let b=s.get(l.id);if(b||(b=new Image,b.src=n(l.image),b.crossOrigin="anonymous",b.onerror=()=>{b._broken=!0},s.set(l.id,b),s.size>200&&s.delete(s.keys().next().value)),!b.complete&&!b._broken){const t=b,e=l.id;t.addEventListener("load",()=>{this.regenerateSpriteTexture(e)},{once:!0})}const _=`bold ${.34*u}px Inter`,T=.26*u+"px Inter";v.font=_;const w=a(v,l.symbol.toUpperCase(),1.5*u);v.font=T;const x=a(v,`${d>0?"+":""}${d.toFixed(2)}%`,1.6*u),C=`bubble-${l.id}-${i}`,M=e({radius:u,change:d,symbolText:w,changeText:x,fontBold:_,fontNormal:T,image:b,iconDY:Math.round(.14*-u),textColor:h.textColor,positiveColor:h.positiveColor,negativeColor:h.negativeColor,dpr:this.dpr});this.textures.addCanvas(C,M);const S=60+Math.random()*(r-120),I=60+Math.random()*(o-120),B=this.matter.add.image(S,I,C);1!==this.dpr&&B.setScale(1/this.dpr),B.setCircle(u+1),B.setFrictionAir(.06),B.setBounce(.15),B.setFixedRotation();const D={coin:l,change:d,radius:u,idle:{anchor:{x:S,y:I},angle:Math.random()*Math.PI*2,radius:6+12*Math.random(),speed:.003+.004*Math.random()},textureKey:C};this.bubbleSprites.push(B),this.bubbleDataMap.set(B,D)}this.presettle(i).catch(()=>{})}async presettle(t){const e=this.matter.world.engine;e.positionIterations=10,e.velocityIterations=6;for(let i=0;i<120;i++){if(t!==this.rebuildGeneration)return;this.matter.world.step(16),i%30==29&&await new Promise(t=>setTimeout(t,0))}e.positionIterations=6,e.velocityIterations=4;for(const i of this.bubbleSprites){const t=this.bubbleDataMap.get(i);t&&(t.idle.anchor={x:i.x,y:i.y})}}rebuildSpriteTexture(t,i){const n=this.config.getColors(),r=this.measuring,{radius:o,change:h,coin:c,textureKey:l}=i,d=`bold ${.34*o}px Inter`,u=.26*o+"px Inter";r.font=d;const g=a(r,c.symbol.toUpperCase(),1.5*o);r.font=u;const b=a(r,`${h>0?"+":""}${h.toFixed(2)}%`,1.6*o);this.textures.exists(l)&&this.textures.remove(l);const p=e({radius:o,change:h,symbolText:g,changeText:b,fontBold:d,fontNormal:u,image:s.get(c.id),iconDY:Math.round(.14*-o),textColor:n.textColor,positiveColor:n.positiveColor,negativeColor:n.negativeColor,dpr:this.dpr});this.textures.addCanvas(l,p),t.setTexture(l)}regenerateSpriteTexture(t){for(const e of this.bubbleSprites){const i=this.bubbleDataMap.get(e);if(i&&i.coin.id===t)return void this.rebuildSpriteTexture(e,i)}}
1updateBackground(t){"transparent"===t?(this.cameras.main.setBackgroundColor("rgba(0,0,0,0)"),this.cameras.main.transparent=!0,this.game.renderer.config.transparent=!0):(this.cameras.main.transparent=!1,this.game.renderer.config.transparent=!1,this.cameras.main.setBackgroundColor(t))}updateColors(){for(const t of this.bubbleSprites){const e=this.bubbleDataMap.get(t);e&&this.rebuildSpriteTexture(t,e)}}update(t,e){const i=this._forceVec;for(const s of this.bubbleSprites){const t=this.bubbleDataMap.get(s);if(!t)continue;const e=t.idle;e.angle+=e.speed,i.x=35e-6*(e.anchor.x+Math.cos(e.angle)*e.radius-s.x),i.y=35e-6*(e.anchor.y+Math.sin(e.angle)*e.radius-s.y),s.applyForce(i)}}shutdown(){this.scrollHandler?.destroy(),this.scrollHandler=null,this.dragConstraint&&(this.matter.world.removeConstraint(this.dragConstraint,!0),this.dragConstraint=null,this.draggedBubble=null),this.resizeTimeout&&clearTimeout(this.resizeTimeout)}}export{r as BubbleScene};
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.