// This canvas was written partially by AI // @ts-ignore globalThis.canvas_cotyledon = function ( canvas: HTMLCanvasElement, panel: HTMLElement, ) { let running = true; const ctx = canvas.getContext("2d"); function resizeCanvas() { canvas.width = canvas.clientWidth; canvas.height = canvas.clientHeight; } resizeCanvas(); window.addEventListener("resize", resizeCanvas); const clover = new Path2D( "M18.9845 34.4839C20.4004 34.5218 21.8336 34.6883 23.2644 34.9578C20.1378 31.095 18.4268 27.1546 18.0555 23.2959C17.321 15.6622 21.9022 9.36595 28.8908 5.78535C34.6355 2.84212 40.258 2.98454 44.2809 5.96879C45.6605 6.99221 46.7683 8.2886 47.5877 9.78593C48.3054 8.50307 49.134 7.26623 50.0858 6.17951C51.8368 4.18037 54.1947 2.47127 57.2294 2.15019C60.2768 1.82766 63.467 2.9608 66.7548 5.52299C70.9834 8.81811 73.084 12.8864 73.5996 17.2135C74.1044 21.4504 73.0711 25.7433 71.4155 29.6117C70.6566 31.3849 69.7488 33.1106 68.7557 34.7506C70.3664 33.9983 72.0168 33.3376 73.6816 32.8312C77.2262 31.7528 81.0258 31.3024 84.8151 32.2149C88.6451 33.1371 92.2246 35.3946 95.3823 39.3157C98.4534 43.1293 99.9219 46.6818 99.997 49.9677C100.073 53.3033 98.7051 55.9829 96.8652 57.9789C95.0586 59.9387 92.7653 61.2872 90.7505 62.1315C90.692 62.1561 90.6334 62.1802 90.5746 62.2042L90.4465 62.256C91.4852 63.7304 92.4724 65.5955 93.0127 67.6979C93.5916 69.9509 93.6669 72.5285 92.674 75.1356C91.679 77.7482 89.7006 80.1559 86.5767 82.2161C86.5556 82.23 86.5342 82.2438 86.5126 82.2571C84.1333 83.7267 81.5504 84.7197 78.6932 84.9352C75.832 85.151 72.8634 84.5742 69.7337 83.1522C64.7667 80.8953 59.274 76.4525 52.8745 69.3645C52.8789 70.1568 52.8844 70.9254 52.9004 71.6677C52.9643 74.6226 53.1868 77.4534 54.0666 80.6265C55.2259 84.503 57.2821 88.4684 60.9561 92.3161C61.644 93.0366 61.8512 94.0908 61.4872 95.018L60.9919 96.2799C60.6464 97.16 59.8435 97.778 58.9041 97.8865C57.9647 97.9952 57.042 97.5769 56.5047 96.7985C52.5406 91.0574 50.3441 86.3289 49.1491 82.0434C48.0155 78.2319 47.6244 74.4579 47.5085 71.0024C45.418 73.6873 42.8696 76.4687 40.0618 78.9101C34.3517 83.8756 26.6803 88.1931 19.142 85.9955C15.5301 84.9425 12.8635 83.2751 11.0848 81.1179C9.2952 78.9474 8.5557 76.4627 8.4981 74.0631C8.43961 71.6256 9.07998 69.225 10.075 67.1703C7.76333 66.828 5.38011 65.9682 3.47071 64.2327C-0.339092 60.7699 -1.2199 54.8876 1.86982 46.4552C3.47011 42.0878 5.90372 38.9798 8.98328 37.0179C12.0444 35.0677 15.5215 34.3912 18.9845 34.4839Z", ); // Background const base = [0x14, 0x1a, 0x19]; let blobTextureCanvas: OffscreenCanvas; let blobTextureCtx: OffscreenCanvasRenderingContext2D; const blobSize = 1000; // Size of the noise texture { const blobTexture = new ImageData(blobSize, blobSize); const data = blobTexture.data; let x = 0; let y = 0; for (let i = 0; i < data.length; i += 4) { if (x >= blobSize) { x = 0; y++; } const noiseX = Math.sin(x * .2 + y * .1) * 0.03; const noiseY = Math.cos(y * .6 + x * .2) * 0.05; const centerX = blobSize / 2; const centerY = blobSize / 2; const dx = (x + noiseX) - centerX; const dy = (y + noiseY) - centerY; const distanceFromCenterRaw = (dx * dx + dy * dy) ** 0.5; const maxDistance = blobSize / 2; const distanceFromCenter = Math.min( 1, distanceFromCenterRaw / maxDistance, ); const noiseValue = (1 - 0.5 * Math.sin(x * 0.02 - y * 0.04)) * (1 - 0.5 * Math.cos(x * 0.03 + y * 0.04)) * 0.3; const gradient = (1 - distanceFromCenter) * (0.95 - distanceFromCenter * 0.4); const finalValue = Math.max( 0, Math.min(1, gradient * (Math.random() * 0.3 + 0.85 + noiseValue)), ); data[i] = 121; data[i + 1] = 219; data[i + 2] = 160; data[i + 3] = Math.floor(finalValue * 255.99); // Alpha x++; } blobTextureCanvas = new OffscreenCanvas(blobSize, blobSize); blobTextureCtx = blobTextureCanvas.getContext("2d")!; blobTextureCtx.putImageData(blobTexture, 0, 0); } class CotyledonParticle { x: number; y: number; size: number; velocityX: number; velocityY: number; rotation: number; rotationSpeed: number; color: string; constructor(positioning: "random" | "edge") { this.size = Math.random() * 0.1 + 0.6; if (positioning === "edge") { const edge = Math.floor(Math.random() * 5); if (edge === 0 || edge === 1) { // Right edge this.x = 1.05; this.y = Math.random(); this.velocityX = -Math.random() * 0.1 - 0.2; this.velocityY = 0; } else if (edge === 2 || edge === 3) { // Top edge this.x = Math.random(); this.y = -0.05; this.velocityX = -Math.random() * 0.1 - 0.1; this.velocityY = -Math.random() * 0.2 - 0.05; } else { // Bottom edge this.x = Math.random() * 0.5 + 0.5; this.y = 1.05; this.velocityX = Math.random() * 0.1 + 0.1; this.velocityY = -Math.random() * 0.3 - 0.1; } } else { let tries = 0; do { this.x = Math.random(); this.y = Math.random(); this.velocityX = -Math.random() * 0.05 - 0.1; this.velocityY = -Math.random() * 0.2 + 0.1; } while (this.tooCloseToAnyOtherParticle() && (tries++ < 10)); } this.rotation = Math.random() * Math.PI * 2; this.rotationSpeed = (Math.random() * 0.003 - 0.0015) * (Math.random() > 0.5 ? 1 : -1); const opacity = Math.random() * 0.4 + 0.2; this.color = `rgba(${ base.map((x) => x + Math.floor(x * opacity)).join(",") }, 1)`; } tooCloseToAnyOtherParticle() { for (let i = 0; i < cotyledonParticles.length; i++) { const otherParticle = cotyledonParticles[i]; const distance = Math.sqrt( (this.x - otherParticle.x) ** 2 + (this.y - otherParticle.y) ** 2, ); if (distance < 0.1) { return true; } } } update() { if (this.velocityY < 0.01) { this.velocityY += 0.00025; } this.velocityX -= 0.0001; this.x += this.velocityX / 1300; this.y += this.velocityY / 1000; this.rotation += this.rotationSpeed; return this.x < -0.05 || (this.y > 1 && this.velocityY < 0); } draw() { if (!ctx) return; ctx.save(); ctx.translate(this.x * canvas.width, this.y * canvas.height); ctx.rotate(this.rotation); ctx.scale(this.size, this.size); ctx.translate(-50, -50); ctx.fillStyle = this.color; ctx.fill(clover); ctx.restore(); } } class BlobParticle { x: number; y: number; opacity: number; state: 0 | 1 | 2; stateTime: number; stayDuration: number; innerColor: string; rot: number = Math.random() * Math.PI * 2; constructor() { this.x = Math.random() * 0.6 + 0.2; this.y = Math.random() * 0.6 + 0.2; this.opacity = 0; // Start fully transparent this.state = 0; this.stateTime = 0; this.stayDuration = Math.random() * 10000 + 5000; // Random stay duration between 5-15 seconds const colorMultiplier = Math.random() * 0.5 + 1.5; // 0.5-1.0 multiplier const colorValues = base.map((x) => Math.floor(x * colorMultiplier)); this.innerColor = `rgba(${colorValues.join(",")}, 1)`; } update(deltaTime: number) { this.stateTime += deltaTime; if (this.state === 0) { this.opacity = Math.min(1, this.stateTime / 15000); if (this.stateTime >= 15000) { this.state = 1; this.stateTime = 0; } } else if (this.state === 1) { if (this.stateTime >= this.stayDuration) { this.state = 2; this.stateTime = 0; } } else if (this.state === 2) { this.opacity = Math.max(0, 1 - (this.stateTime / 15000)); if (this.stateTime >= 15000) { return true; } } return false; } draw() { if (!ctx) return; const screenX = this.x * canvas.width; const screenY = this.y * canvas.height; const screenSize = (Math.min(canvas.width, canvas.height) * 2) / blobSize; ctx.save(); ctx.translate(screenX - screenSize * 0.5, screenY - screenSize * 0.5); ctx.scale(screenSize, screenSize); ctx.rotate(this.rot); ctx.globalAlpha = this.opacity * 0.2; ctx.globalCompositeOperation = "overlay"; ctx.drawImage(blobTextureCanvas, 0, 0); ctx.restore(); } } const cotyledonParticles: CotyledonParticle[] = []; const blobParticles: BlobParticle[] = []; let blobParticleTop: BlobParticle = new BlobParticle(); for (let i = 0; i < 80; i++) { cotyledonParticles.push(new CotyledonParticle("random")); } for (let i = 0; i < 9; i++) { const blobParticle = new BlobParticle(); if (i < 4) { blobParticle.state = 1; blobParticle.opacity = 1; blobParticle.stayDuration = Math.random() * 10000; } else { blobParticle.state = i < 7 ? 2 : 0; blobParticle.stateTime = Math.random() * 15000; } if (i > 0) { do { blobParticle.x = Math.random(); blobParticle.y = Math.random(); } while ( blobParticles.some((p) => Math.sqrt((p.x - blobParticle.x) ** 2 + (p.y - blobParticle.y) ** 2) < 0.1 ) ); } blobParticles.push(blobParticle); } let lastTime = performance.now(); function animate(currentTime: number) { if (!running) return; if (!ctx) return; const deltaTime = currentTime - lastTime; lastTime = currentTime; ctx.clearRect(0, 0, canvas.width, canvas.height); for (let i = blobParticles.length - 1; i >= 0; i--) { const shouldRemove = blobParticles[i].update(deltaTime); if (shouldRemove) { blobParticles[i] = new BlobParticle(); } else { blobParticles[i].draw(); } } for (let i = cotyledonParticles.length - 1; i >= 0; i--) { const shouldRemove = cotyledonParticles[i].update(); if (shouldRemove) { cotyledonParticles[i] = new CotyledonParticle("edge"); } else { cotyledonParticles[i].draw(); } } if (blobParticleTop.update(deltaTime)) { blobParticleTop = new BlobParticle(); } blobParticleTop.draw(); requestAnimationFrame(animate); } let clickedButton = false; const enterButton = panel.querySelector("button#enter")!; enterButton.addEventListener("click", () => { if (clickedButton) return; clickedButton = true; const first = panel.querySelector("#first")! as HTMLElement; const second = panel.querySelector("#captcha")! as HTMLElement; first.style.transition = second.style.transition = "opacity 1s ease-in-out"; first.style.opacity = "0"; second.style.opacity = "0"; setTimeout(() => { first.style.display = "none"; second.style.display = "block"; setTimeout(() => { second.style.opacity = "1"; document.getElementById("enter2")?.addEventListener("click", () => { second.style.opacity = "0"; let p = fetch("/file/cotyledon", { method: "POST", body: "I AGREE", }); setTimeout(() => { p.then(() => { location.reload(); }); }, 1000); }); }, 10); }, 1000); }); const imageButtons = panel.querySelectorAll(".image-grid button")!; imageButtons.forEach((button) => { let canClick = true; button.addEventListener("click", () => { if (!canClick) return; canClick = false; const image = button.querySelector("img")!; image.style.transition = "opacity 0.05s linear"; image.style.opacity = "0"; setTimeout(() => { image.style.transition = "opacity 2s linear"; let newNum; do { newNum = Math.floor(Math.random() * 18); // 0-17 inclusive } while ( document.querySelector(`img[src="/captcha/image/${newNum}.jpeg"]`) ); image.setAttribute("src", `/captcha/image/${newNum}.jpeg`); setTimeout(() => { image.style.opacity = "0.75"; canClick = true; }, 50); }, 300); }); }); // Start animation animate(performance.now()); return () => { window.removeEventListener("resize", resizeCanvas); running = false; }; };