Initial commit: существующий сайт + конфигурация разработки
Some checks failed
Deploy to Production / deploy (push) Failing after 4s

This commit is contained in:
2026-01-10 22:23:00 +03:00
parent 28b50d28c1
commit 5b11e61c8e
16 changed files with 1339 additions and 0 deletions

87
html/js/counter.js Normal file
View File

@@ -0,0 +1,87 @@
// Простой счетчик посещений
class SimpleCounter {
constructor() {
this.storageKey = 'arseny_site_counter';
this.serverFile = '/api/counter.txt'; // Файл для синхронизации
this.init();
}
async init() {
try {
// 1. Получаем локальное значение
let localCount = localStorage.getItem(this.storageKey);
if (!localCount) {
localCount = 1;
} else {
localCount = parseInt(localCount) + 1;
}
// 2. Сохраняем локально
localStorage.setItem(this.storageKey, localCount);
// 3. Пытаемся синхронизировать с сервером (в фоне, без ожидания)
this.syncWithServer(localCount);
// 4. Показываем на странице
this.display(localCount);
} catch (error) {
console.log('Счетчик работает в локальном режиме');
this.displayFallback();
}
}
async syncWithServer(count) {
try {
// Отправляем данные на сервер
await fetch(this.serverFile, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ count: count })
});
} catch (error) {
// Игнорируем ошибки - работает локальная версия
}
}
display(count) {
// Ищем все элементы для отображения счетчика
const elements = document.querySelectorAll('[data-counter]');
elements.forEach(element => {
const format = element.dataset.format || 'simple';
switch(format) {
case 'simple':
element.textContent = `Посетителей: ${this.formatNumber(count)}`;
break;
case 'number':
element.textContent = this.formatNumber(count);
break;
case 'message':
element.textContent = `Вы посетитель №${this.formatNumber(count)}`;
break;
}
element.classList.add('counter-loaded');
});
}
displayFallback() {
const elements = document.querySelectorAll('[data-counter]');
elements.forEach(element => {
element.textContent = 'Счетчик загружается...';
});
}
formatNumber(num) {
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' ');
}
}
// Автоматический запуск при загрузке страницы
document.addEventListener('DOMContentLoaded', () => {
new SimpleCounter();
});

68
html/js/script.js Normal file
View File

@@ -0,0 +1,68 @@
// Мобильное меню
document.addEventListener('DOMContentLoaded', function () {
const menuToggle = document.querySelector('.menu-toggle');
const navLinks = document.querySelector('.nav-links');
menuToggle.addEventListener('click', function () {
navLinks.classList.toggle('active');
});
// Закрытие меню при клике на ссылку
document.querySelectorAll('.nav-links a').forEach(link => {
link.addEventListener('click', () => {
navLinks.classList.remove('active');
});
});
// Плавная прокрутка
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const targetId = this.getAttribute('href');
if (targetId === '#') return;
const targetElement = document.querySelector(targetId);
if (targetElement) {
window.scrollTo({
top: targetElement.offsetTop - 80,
behavior: 'smooth'
});
}
});
});
// Форма обратной связи
const contactForm = document.querySelector('.contact-form');
if (contactForm) {
contactForm.addEventListener('submit', function (e) {
e.preventDefault();
// Здесь можно добавить отправку формы на сервер
alert('Спасибо за сообщение! Я свяжусь с вами в ближайшее время.');
this.reset();
});
}
// Анимация появления элементов при скролле
const observerOptions = {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.style.opacity = '1';
entry.target.style.transform = 'translateY(0)';
}
});
}, observerOptions);
// Наблюдаем за карточками
document.querySelectorAll('.fact-card, .interest-card, .project-card, .blog-card').forEach(card => {
card.style.opacity = '0';
card.style.transform = 'translateY(20px)';
card.style.transition = 'opacity 0.6s ease, transform 0.6s ease';
observer.observe(card);
});
});