Deleted `Source Code Pro ExtraLight_Regular.json` to clean up unused assets in the core engine. No functional changes.
307 lines
14 KiB
HTML
307 lines
14 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8"/>
|
||
<title>Maintenance</title>
|
||
<meta name="viewport"
|
||
content="width=device-height, width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||
<link href="https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@500&display=swap" rel="stylesheet">
|
||
<style>
|
||
html, body {
|
||
margin: 0;
|
||
padding: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
overflow: hidden;
|
||
font-family: 'Source Code Pro', 'Courier New', Courier, monospace;
|
||
}
|
||
|
||
canvas {
|
||
display: block;
|
||
}
|
||
|
||
/* Dark theme (default) */
|
||
body {
|
||
background: #2a2a3a;
|
||
color: #fff;
|
||
}
|
||
|
||
/* Light theme */
|
||
@media (prefers-color-scheme: light) {
|
||
body {
|
||
background: #f5f5f7;
|
||
color: #1d1d1f;
|
||
}
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
||
<script>
|
||
(function () {
|
||
let scene, camera, renderer;
|
||
let particleSystem, textGroup, fontLoader, loadedFont;
|
||
const clock = new THREE.Clock();
|
||
let targetRotX = 0, targetRotY = 0;
|
||
let currentRotX = 0, currentRotY = 0;
|
||
let isMobile = 'ontouchstart' in window;
|
||
let isDarkTheme = !window.matchMedia('(prefers-color-scheme: light)').matches;
|
||
|
||
const translations = {
|
||
'ar-ar': {title: 'سنعود قريباً', subtitle: 'جارٍ التحديث. يرجى التحقق مرة أخرى لاحقاً.'},
|
||
'cs-cz': {title: 'Brzy se vrátíme', subtitle: 'Probíhá aktualizace. Zkuste to prosím později.'},
|
||
'da-dk': {title: 'Vi er snart tilbage', subtitle: 'En opdatering er i gang. Prøv igen senere.'},
|
||
'de-de': {title: 'Wir sind bald zurück', subtitle: 'Ein Update wird durchgeführt. Bitte später erneut versuchen.'},
|
||
'en-gb': {title: 'We\'ll Be Back Soon', subtitle: 'An update is in progress. Please check back later.'},
|
||
'en-us': {title: 'We\'ll Be Back Soon', subtitle: 'An update is in progress. Please check back later.'},
|
||
'es-es': {title: 'Volveremos pronto', subtitle: 'Actualización en curso. Por favor, vuelva más tarde.'},
|
||
'fa-ir': {title: 'به زودی برمیگردیم', subtitle: 'بهروزرسانی در حال انجام است. لطفاً بعداً بررسی کنید.'},
|
||
'fr-fr': {title: 'Nous revenons bientôt', subtitle: 'Une mise à jour est en cours. Revenez plus tard.'},
|
||
'he-il': {title: 'נחזור בקרוב', subtitle: 'עדכון מתבצע. אנא בדוק שוב מאוחר יותר.'},
|
||
'hi-in': {title: 'हम जल्द वापस आएंगे', subtitle: 'एक अपडेट प्रगति पर है। कृपया बाद में जांचें।'},
|
||
'hr-hr': {title: 'Uskoro se vraćamo', subtitle: 'Ažuriranje je u tijeku. Molimo provjerite kasnije.'},
|
||
'id-id': {title: 'Kami akan segera kembali', subtitle: 'Pembaruan sedang berlangsung. Silakan periksa lagi nanti.'},
|
||
'it-it': {title: 'Torneremo presto', subtitle: 'È in corso un aggiornamento. Si prega di tornare più tardi.'},
|
||
'ja-jp': {title: 'すぐに戻ります', subtitle: 'アップデート中です。後でもう一度お試しください。'},
|
||
'kk-kz': {title: 'Біз жақында қайтамыз', subtitle: 'Жаңарту жүріп жатыр. Кейінірек тексеріңіз.'},
|
||
'ko-kr': {title: '곧 돌아오겠습니다', subtitle: '업데이트가 진행 중입니다. 나중에 다시 확인해주세요.'},
|
||
'nl-nl': {title: 'We zijn zo terug', subtitle: 'Er wordt een update uitgevoerd. Kom later terug.'},
|
||
'no-no': {title: 'Vi er snart tilbake', subtitle: 'En oppdatering pågår. Vennligst sjekk igjen senere.'},
|
||
'pl-pl': {title: 'Wrócimy wkrótce', subtitle: 'Trwa aktualizacja. Sprawdź ponownie później.'},
|
||
'pt-br': {title: 'Voltaremos em breve', subtitle: 'Uma atualização está em andamento. Volte mais tarde.'},
|
||
'ro-ro': {title: 'Revenim în curând', subtitle: 'O actualizare este în curs. Vă rugăm să reveniți mai târziu.'},
|
||
'ru-ru': {title: 'Скоро вернёмся', subtitle: 'Идёт обновление. Пожалуйста, проверьте позже.'},
|
||
'sv-se': {title: 'Vi är snart tillbaka', subtitle: 'En uppdatering pågår. Kontrollera igen senare.'},
|
||
'th-th': {title: 'เราจะกลับมาเร็วๆ นี้', subtitle: 'กำลังอัปเดต โปรดตรวจสอบอีกครั้งในภายหลัง'},
|
||
'tr-tr': {title: 'Yakında geri döneceğiz', subtitle: 'Güncelleme devam ediyor. Lütfen daha sonra tekrar kontrol edin.'},
|
||
'vi-vn': {title: 'Chúng tôi sẽ sớm trở lại', subtitle: 'Đang cập nhật. Vui lòng quay lại sau.'},
|
||
'zh-hans': {title: '我们很快就会回来', subtitle: '正在更新中。请稍后再查看。'}
|
||
};
|
||
|
||
function detectUserLanguage() {
|
||
const browserLang = (navigator.language || navigator.userLanguage || 'en-gb').toLowerCase();
|
||
|
||
if (translations[browserLang]) {
|
||
return browserLang;
|
||
}
|
||
|
||
const langCode = browserLang.split('-')[0];
|
||
for (let key in translations) {
|
||
if (key.startsWith(langCode)) {
|
||
return key;
|
||
}
|
||
}
|
||
|
||
return 'en-gb';
|
||
}
|
||
|
||
const userLanguage = detectUserLanguage();
|
||
|
||
function init() {
|
||
scene = new THREE.Scene();
|
||
camera = new THREE.PerspectiveCamera(65, window.innerWidth / window.innerHeight, 0.1, 2000);
|
||
camera.position.z = 70;
|
||
renderer = new THREE.WebGLRenderer({antialias: true});
|
||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||
renderer.setPixelRatio(window.devicePixelRatio);
|
||
document.body.appendChild(renderer.domElement);
|
||
|
||
createGradientBackground();
|
||
createParticleField();
|
||
|
||
fontLoader = new THREE.FontLoader();
|
||
fontLoader.load('https://wiseless.xyz/fonts/source-code-pro/source_code_pro.json', f => {
|
||
loadedFont = f;
|
||
create3DText();
|
||
fitCameraToText();
|
||
});
|
||
|
||
window.addEventListener('resize', onWindowResize);
|
||
if (isMobile) {
|
||
window.addEventListener('touchmove', onTouchMove, {passive: false});
|
||
} else {
|
||
window.addEventListener('mousemove', onMouseMove);
|
||
}
|
||
onWindowResize();
|
||
animate();
|
||
}
|
||
|
||
function createGradientBackground() {
|
||
const c = document.createElement('canvas');
|
||
c.width = 2;
|
||
c.height = 2;
|
||
const ctx = c.getContext('2d');
|
||
const gradient = ctx.createLinearGradient(0, 0, 0, 2);
|
||
|
||
if (isDarkTheme) {
|
||
gradient.addColorStop(0, '#1E1E2A');
|
||
gradient.addColorStop(1, '#4C4C6A');
|
||
} else {
|
||
gradient.addColorStop(0, '#E8E8ED');
|
||
gradient.addColorStop(1, '#F5F5F7');
|
||
}
|
||
|
||
ctx.fillStyle = gradient;
|
||
ctx.fillRect(0, 0, 2, 2);
|
||
const texture = new THREE.Texture(c);
|
||
texture.needsUpdate = true;
|
||
scene.background = texture;
|
||
}
|
||
|
||
function createParticleField() {
|
||
const particleCount = 15000;
|
||
const geometry = new THREE.BufferGeometry();
|
||
const positions = new Float32Array(particleCount * 3);
|
||
const colors = new Float32Array(particleCount * 3);
|
||
|
||
let colorStart, colorEnd;
|
||
if (isDarkTheme) {
|
||
colorStart = new THREE.Color(0x34000d);
|
||
colorEnd = new THREE.Color(0x02066F);
|
||
} else {
|
||
colorStart = new THREE.Color(0x7FB3D5);
|
||
colorEnd = new THREE.Color(0xC9ADA7);
|
||
}
|
||
|
||
for (let i = 0; i < particleCount; i++) {
|
||
positions[i * 3] = (Math.random() - 0.5) * 400;
|
||
positions[i * 3 + 1] = (Math.random() - 0.5) * 400;
|
||
positions[i * 3 + 2] = (Math.random() - 0.5) * 400;
|
||
const mixRatio = i / particleCount;
|
||
const color = colorStart.clone().lerp(colorEnd, mixRatio);
|
||
colors[i * 3] = color.r;
|
||
colors[i * 3 + 1] = color.g;
|
||
colors[i * 3 + 2] = color.b;
|
||
}
|
||
|
||
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
|
||
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
|
||
const material = new THREE.PointsMaterial({
|
||
size: 0.7,
|
||
vertexColors: true,
|
||
transparent: true,
|
||
opacity: isDarkTheme ? 0.7 : 0.5,
|
||
blending: THREE.AdditiveBlending
|
||
});
|
||
|
||
particleSystem = new THREE.Points(geometry, material);
|
||
scene.add(particleSystem);
|
||
}
|
||
|
||
function create3DText() {
|
||
if (textGroup) {
|
||
scene.remove(textGroup);
|
||
textGroup.traverse(obj => {
|
||
if (obj.geometry) obj.geometry.dispose();
|
||
if (obj.material) obj.material.dispose();
|
||
});
|
||
}
|
||
|
||
textGroup = new THREE.Group();
|
||
const currentLang = translations[userLanguage];
|
||
|
||
const textColor = isDarkTheme ? 0xffffff : 0x1d1d1f;
|
||
const emissiveColor = isDarkTheme ? 0x111111 : 0x555555;
|
||
|
||
const text1 = new THREE.TextGeometry(currentLang.title, {
|
||
font: loadedFont,
|
||
size: 5,
|
||
height: 1,
|
||
curveSegments: 12,
|
||
bevelEnabled: true,
|
||
bevelThickness: 0.2,
|
||
bevelSize: 0.1,
|
||
bevelSegments: 5
|
||
});
|
||
const material1 = new THREE.MeshPhongMaterial({color: textColor, emissive: emissiveColor});
|
||
const mesh1 = new THREE.Mesh(text1, material1);
|
||
mesh1.geometry.center();
|
||
mesh1.position.y = 5;
|
||
textGroup.add(mesh1);
|
||
|
||
const text2 = new THREE.TextGeometry(currentLang.subtitle, {
|
||
font: loadedFont,
|
||
size: 2,
|
||
height: 0.5,
|
||
curveSegments: 12,
|
||
bevelEnabled: false
|
||
});
|
||
const material2 = new THREE.MeshPhongMaterial({color: textColor, emissive: emissiveColor});
|
||
const mesh2 = new THREE.Mesh(text2, material2);
|
||
mesh2.geometry.center();
|
||
mesh2.position.y = -5;
|
||
textGroup.add(mesh2);
|
||
|
||
scene.add(textGroup);
|
||
|
||
if (!scene.getObjectByName('ambientLight')) {
|
||
const ambientLight = new THREE.AmbientLight(isDarkTheme ? 0x404040 : 0x808080, 2);
|
||
ambientLight.name = 'ambientLight';
|
||
scene.add(ambientLight);
|
||
}
|
||
|
||
if (!scene.getObjectByName('spotLight')) {
|
||
const spotLight = new THREE.SpotLight(isDarkTheme ? 0xffffff : 0xf0f0f0, 1);
|
||
spotLight.position.set(100, 100, 100);
|
||
spotLight.name = 'spotLight';
|
||
scene.add(spotLight);
|
||
}
|
||
}
|
||
|
||
function fitCameraToText() {
|
||
if (!textGroup) return;
|
||
const box = new THREE.Box3().setFromObject(textGroup);
|
||
const size = box.getSize(new THREE.Vector3());
|
||
const center = box.getCenter(new THREE.Vector3());
|
||
|
||
const halfSizeToFitOnScreen = size.length() * 0.5;
|
||
const halfFov = THREE.MathUtils.degToRad(camera.fov * 0.5);
|
||
let distance = halfSizeToFitOnScreen / Math.sin(halfFov);
|
||
distance *= 1.5;
|
||
camera.position.set(center.x, center.y, distance);
|
||
camera.lookAt(center);
|
||
}
|
||
|
||
function animate() {
|
||
requestAnimationFrame(animate);
|
||
const delta = clock.getDelta();
|
||
if (particleSystem) {
|
||
particleSystem.rotation.y += 0.02 * delta;
|
||
}
|
||
currentRotX += (targetRotX - currentRotX) * 0.1;
|
||
currentRotY += (targetRotY - currentRotY) * 0.1;
|
||
if (textGroup) {
|
||
textGroup.rotation.x = currentRotY;
|
||
textGroup.rotation.y = currentRotX;
|
||
}
|
||
renderer.render(scene, camera);
|
||
}
|
||
|
||
function onWindowResize() {
|
||
camera.aspect = window.innerWidth / window.innerHeight;
|
||
camera.updateProjectionMatrix();
|
||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||
fitCameraToText();
|
||
}
|
||
|
||
function onMouseMove(event) {
|
||
targetRotX = ((event.clientX / window.innerWidth) - 0.5) * 2 * 0.3;
|
||
targetRotY = ((event.clientY / window.innerHeight) - 0.5) * 2 * 0.3;
|
||
}
|
||
|
||
function onTouchMove(event) {
|
||
if (event.touches.length > 0) {
|
||
const touch = event.touches[0];
|
||
targetRotX = ((touch.clientX / window.innerWidth) - 0.5) * 2 * 0.3;
|
||
targetRotY = ((touch.clientY / window.innerHeight) - 0.5) * 2 * 0.3;
|
||
}
|
||
event.preventDefault();
|
||
}
|
||
|
||
init();
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|