schon/engine/core/static/maintenance.html
2025-12-28 17:05:20 +03:00

307 lines
14 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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('/static/Source%20Code%20Pro%20ExtraLight_Regular.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>