214 lines
7.8 KiB
JavaScript
214 lines
7.8 KiB
JavaScript
// --- Toggle Mobile Menu ---
|
|
// --- Mobile Navigation ---
|
|
function toggleMenu() {
|
|
document.getElementById('mobile-nav').classList.toggle('open');
|
|
}
|
|
|
|
// --- Intersection Observer for Scroll Animations ---
|
|
const observer = new IntersectionObserver((entries) => {
|
|
entries.forEach(entry => {
|
|
if (entry.isIntersecting) entry.target.classList.add('active');
|
|
});
|
|
}, { threshold: 0.1 });
|
|
|
|
document.querySelectorAll('.reveal').forEach(el => observer.observe(el));
|
|
|
|
// --- Generic Horizontal Scroll Logic (Services, Team, Testimonials) ---
|
|
function scrollCarousel(trackId, direction) {
|
|
const track = document.getElementById(trackId);
|
|
if (!track) return;
|
|
const firstItem = track.querySelector('.carousel-item');
|
|
if (!firstItem) return;
|
|
const itemWidth = firstItem.offsetWidth;
|
|
const gap = 24;
|
|
const scrollAmount = itemWidth + gap;
|
|
|
|
// Adjust direction for RTL
|
|
let scrollDir = direction;
|
|
if (document.dir === 'rtl') {
|
|
scrollDir = direction * -1;
|
|
}
|
|
|
|
if (scrollDir === -1) {
|
|
track.scrollBy({ left: -scrollAmount, behavior: 'smooth' });
|
|
} else {
|
|
track.scrollBy({ left: scrollAmount, behavior: 'smooth' });
|
|
}
|
|
}
|
|
|
|
// --- Product Slider Logic (Fade) ---
|
|
const slides = document.querySelectorAll('.product-slide');
|
|
const indicators = document.querySelectorAll('.indicator');
|
|
const prevBtn = document.getElementById('prev-btn');
|
|
const nextBtn = document.getElementById('next-btn');
|
|
const productWrapper = document.querySelector('.product-slider-wrapper');
|
|
|
|
let currentSlide = 0;
|
|
const totalSlides = slides.length;
|
|
let slideInterval;
|
|
|
|
if(totalSlides > 0) {
|
|
function showSlide(index) {
|
|
if (index >= totalSlides) currentSlide = 0;
|
|
else if (index < 0) currentSlide = totalSlides - 1;
|
|
else currentSlide = index;
|
|
|
|
slides.forEach(slide => slide.classList.remove('active'));
|
|
indicators.forEach(ind => ind.classList.remove('active'));
|
|
|
|
slides[currentSlide].classList.add('active');
|
|
indicators[currentSlide].classList.add('active');
|
|
}
|
|
|
|
function nextSlide() {
|
|
showSlide(currentSlide + 1);
|
|
}
|
|
|
|
function prevSlide() {
|
|
showSlide(currentSlide - 1);
|
|
}
|
|
|
|
if(nextBtn) {
|
|
nextBtn.addEventListener('click', () => {
|
|
nextSlide();
|
|
resetTimer();
|
|
});
|
|
}
|
|
|
|
if(prevBtn) {
|
|
prevBtn.addEventListener('click', () => {
|
|
prevSlide();
|
|
resetTimer();
|
|
});
|
|
}
|
|
|
|
indicators.forEach((ind, index) => {
|
|
ind.addEventListener('click', () => {
|
|
showSlide(index);
|
|
resetTimer();
|
|
});
|
|
});
|
|
|
|
function startTimer() {
|
|
slideInterval = setInterval(nextSlide, 5000);
|
|
}
|
|
|
|
function resetTimer() {
|
|
clearInterval(slideInterval);
|
|
startTimer();
|
|
}
|
|
|
|
if(productWrapper) {
|
|
productWrapper.addEventListener('mouseenter', () => clearInterval(slideInterval));
|
|
productWrapper.addEventListener('mouseleave', startTimer);
|
|
startTimer();
|
|
}
|
|
}
|
|
|
|
// --- Form Submission (AJAX) ---
|
|
function showToast(message) {
|
|
const toast = document.getElementById('toast');
|
|
toast.textContent = message;
|
|
toast.style.transform = 'translateY(0)';
|
|
|
|
setTimeout(() => {
|
|
toast.style.transform = 'translateY(150%)';
|
|
}, 3000);
|
|
}
|
|
|
|
document.getElementById('contactForm').addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
const formData = new FormData(this);
|
|
const submitBtn = this.querySelector('button[type="submit"]');
|
|
const originalText = submitBtn.textContent;
|
|
|
|
submitBtn.textContent = '{% trans "Sending..." %}';
|
|
submitBtn.disabled = true;
|
|
|
|
fetch("{% url 'submit_inquiry' %}", {
|
|
method: 'POST',
|
|
body: formData,
|
|
headers: {
|
|
'X-Requested-With': 'XMLHttpRequest'
|
|
}
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if(data.status === 'success') {
|
|
showToast(data.message);
|
|
document.getElementById('contactForm').reset();
|
|
} else {
|
|
showToast('Error: ' + (data.message || 'Something went wrong'));
|
|
}
|
|
})
|
|
.catch(error => {
|
|
showToast('Error: Could not submit form.');
|
|
console.error('Error:', error);
|
|
})
|
|
.finally(() => {
|
|
submitBtn.textContent = originalText;
|
|
submitBtn.disabled = false;
|
|
});
|
|
});
|
|
|
|
// --- Marquee Width & Speed Fix ---
|
|
|
|
// Helper: Debounce function prevents the code from running too often
|
|
// (e.g. while scrolling on mobile which triggers resize events)
|
|
function debounce(func, wait) {
|
|
let timeout;
|
|
return function executedFunction(...args) {
|
|
const later = () => {
|
|
clearTimeout(timeout);
|
|
func(...args);
|
|
};
|
|
clearTimeout(timeout);
|
|
timeout = setTimeout(later, wait);
|
|
};
|
|
}
|
|
|
|
function adjustMarqueeWidth() {
|
|
const track = document.getElementById('marquee-track');
|
|
if (!track) return;
|
|
|
|
// 1. Get the rendered width of the single list of partners
|
|
const currentWidth = track.scrollWidth;
|
|
|
|
// 2. Calculate how many times we need to repeat the list
|
|
// Target: We want the track to be at least 3x the screen width
|
|
// so it scrolls smoothly without running out of items before looping.
|
|
const targetWidth = window.innerWidth * 3;
|
|
|
|
const repeats = Math.max(2, Math.ceil(targetWidth / currentWidth));
|
|
|
|
// 3. Store the original HTML (the first run of the loop)
|
|
const originalContent = track.innerHTML;
|
|
|
|
// 4. Rebuild the track with the repeated content
|
|
track.innerHTML = '';
|
|
for (let i = 0; i < repeats; i++) {
|
|
track.innerHTML += originalContent;
|
|
}
|
|
|
|
// --- NEW: Calculate Constant Speed ---
|
|
// The animation moves -50% of the track width.
|
|
const totalWidth = track.scrollWidth;
|
|
const distanceToMove = totalWidth / 2;
|
|
|
|
// Desired speed in pixels per second (Adjust this number to make it faster/slower)
|
|
// 50 = Relaxed/Easy reading speed
|
|
// 100 = Faster
|
|
const pixelsPerSecond = 50;
|
|
|
|
// Duration = Distance / Speed
|
|
const durationInSeconds = distanceToMove / pixelsPerSecond;
|
|
|
|
// Apply the calculated duration
|
|
track.style.animationDuration = `${durationInSeconds}s`;
|
|
}
|
|
|
|
// Run on load
|
|
window.addEventListener('load', adjustMarqueeWidth);
|
|
|
|
// Run on resize (Debounced by 200ms to prevent flickering during scroll)
|
|
window.addEventListener('resize', debounce(adjustMarqueeWidth, 200)); |