| Server IP : 103.243.232.44 / Your IP : 216.73.216.237 Web Server : LiteSpeed System : Linux server17213-10344.hostycare.online 5.14.0-687.38.1.el9_8.x86_64 #1 SMP PREEMPT_DYNAMIC Wed Aug 12 17:19:12 EDT 2026 x86_64 User : iamakash ( 1400) PHP Version : 8.1.34 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /home/iamakash/public_html/ftnconsortium.com/js/ |
Upload File : |
// ============================================================
// FTN Consortium — main.js
// ============================================================
// ---- Mobile menu ----
const hamburger = document.getElementById('hamburger');
const mobileMenu = document.getElementById('mobileMenu');
if (hamburger && mobileMenu) {
hamburger.addEventListener('click', () => mobileMenu.classList.toggle('open'));
document.addEventListener('click', e => {
if (!hamburger.contains(e.target) && !mobileMenu.contains(e.target))
mobileMenu.classList.remove('open');
});
}
// ---- Navbar shadow on scroll ----
const navbar = document.getElementById('navbar');
window.addEventListener('scroll', () => {
if (navbar) navbar.style.boxShadow = window.scrollY > 20 ? '0 2px 20px rgba(0,0,0,0.3)' : 'none';
}, { passive: true });
// ---- Scroll-to-top button ----
const scrollTopBtn = document.getElementById('scrollTop');
if (scrollTopBtn) {
window.addEventListener('scroll', () => scrollTopBtn.classList.toggle('visible', window.scrollY > 400), { passive: true });
scrollTopBtn.addEventListener('click', () => window.scrollTo({ top: 0, behavior: 'smooth' }));
}
// ---- Fade-in on scroll ----
const fadeEls = document.querySelectorAll('.fade-in');
if (fadeEls.length) {
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry, i) => {
if (entry.isIntersecting) {
setTimeout(() => entry.target.classList.add('visible'), i * 90);
observer.unobserve(entry.target);
}
});
}, { threshold: 0.1 });
fadeEls.forEach(el => observer.observe(el));
}
// ---- Portfolio filter ----
const filterBtns = document.querySelectorAll('.filter-btn');
const portfolioCards = document.querySelectorAll('.portfolio-card[data-category]');
filterBtns.forEach(btn => {
btn.addEventListener('click', () => {
filterBtns.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
const f = btn.dataset.filter;
portfolioCards.forEach(card => {
card.style.display = (f === 'all' || card.dataset.category === f) ? 'block' : 'none';
});
});
});
// ---- Smooth scroll for anchor links ----
document.querySelectorAll('a[href^="#"]').forEach(link => {
link.addEventListener('click', e => {
const target = document.querySelector(link.getAttribute('href'));
if (target) {
e.preventDefault();
window.scrollTo({ top: target.offsetTop - 80, behavior: 'smooth' });
}
});
});
// ---- Animate stat numbers ----
function animateCount(el, target, duration) {
const isSpecial = isNaN(parseInt(target));
if (isSpecial) return;
const suffix = target.replace(/[0-9]/g, '');
const num = parseInt(target);
let startTime = null;
const step = (ts) => {
if (!startTime) startTime = ts;
const progress = Math.min((ts - startTime) / duration, 1);
const ease = 1 - Math.pow(1 - progress, 3);
el.textContent = Math.floor(ease * num) + suffix;
if (progress < 1) requestAnimationFrame(step);
else el.textContent = target;
};
requestAnimationFrame(step);
}
const statNums = document.querySelectorAll('.stat-box .number, .hero-stat .number');
const statObs = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
animateCount(entry.target, entry.target.textContent.trim(), 1400);
statObs.unobserve(entry.target);
}
});
}, { threshold: 0.6 });
statNums.forEach(el => statObs.observe(el));
// ---- Hero Slider ----
const heroSection = document.getElementById('heroSection');
const slides = document.querySelectorAll('.hslide');
const dots = document.querySelectorAll('.sdot');
const prevBtn = document.getElementById('sliderPrev');
const nextBtn = document.getElementById('sliderNext');
const progressFill = document.getElementById('sliderProgress');
const SLIDE_DURATION = 5500;
if (slides.length > 1) {
let current = 0;
let autoTimer = null;
let paused = false;
function startProgress() {
if (!progressFill) return;
progressFill.style.transition = 'none';
progressFill.style.width = '0%';
requestAnimationFrame(() => requestAnimationFrame(() => {
progressFill.style.transition = `width ${SLIDE_DURATION}ms linear`;
progressFill.style.width = '100%';
}));
}
function goTo(n) {
const prev = current;
current = ((n % slides.length) + slides.length) % slides.length;
if (prev === current) return;
slides[prev].classList.add('leaving');
slides[prev].classList.remove('active');
dots[prev].classList.remove('active');
setTimeout(() => slides[prev].classList.remove('leaving'), 900);
slides[current].classList.add('active');
dots[current].classList.add('active');
startProgress();
}
function startAuto() {
clearInterval(autoTimer);
autoTimer = setInterval(() => { if (!paused) goTo(current + 1); }, SLIDE_DURATION);
}
// Controls
if (prevBtn) prevBtn.addEventListener('click', () => { goTo(current - 1); startAuto(); });
if (nextBtn) nextBtn.addEventListener('click', () => { goTo(current + 1); startAuto(); });
dots.forEach((dot, i) => dot.addEventListener('click', () => { goTo(i); startAuto(); }));
// Pause on hover
if (heroSection) {
heroSection.addEventListener('mouseenter', () => { paused = true; });
heroSection.addEventListener('mouseleave', () => { paused = false; });
}
// Touch / swipe support
let touchStartX = 0;
if (heroSection) {
heroSection.addEventListener('touchstart', e => { touchStartX = e.touches[0].clientX; }, { passive: true });
heroSection.addEventListener('touchend', e => {
const diff = touchStartX - e.changedTouches[0].clientX;
if (Math.abs(diff) > 50) { goTo(diff > 0 ? current + 1 : current - 1); startAuto(); }
}, { passive: true });
}
startProgress();
startAuto();
}
// ---- Contact Form — AJAX + CAPTCHA ----
const contactForm = document.getElementById('contactForm');
if (contactForm) {
const submitBtn = document.getElementById('submitBtn');
const formAlert = document.getElementById('formAlert');
const captchaQ = document.getElementById('captchaQuestion');
const captchaInput = document.getElementById('captcha');
// Resolve absolute URL to contact.php regardless of current path
const contactUrl = (function () {
const a = document.createElement('a');
a.href = 'contact.php';
return a.href;
})();
function showAlert(msg, type) {
if (!formAlert) return;
formAlert.innerHTML = `<div class="alert alert-${type}" role="alert">${type === 'success' ? '✅' : '❌'} ${msg}</div>`;
formAlert.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
function clearAlert() {
if (formAlert) formAlert.innerHTML = '';
}
function refreshCaptcha(question) {
if (captchaQ && question) captchaQ.textContent = question;
if (captchaInput) captchaInput.value = '';
}
contactForm.addEventListener('submit', async (e) => {
e.preventDefault();
clearAlert();
// Client-side required field check
const fields = [
{ name: 'name', label: 'Full Name' },
{ name: 'email', label: 'Email Address' },
{ name: 'service', label: 'Service' },
{ name: 'message', label: 'Project Details' },
{ name: 'captcha', label: 'Security Answer' },
];
for (const f of fields) {
const el = contactForm.elements[f.name];
if (!el || !el.value.trim()) {
showAlert(`Please fill in: ${f.label}.`, 'error');
el?.focus();
return;
}
}
// Disable button while sending
submitBtn.disabled = true;
submitBtn.textContent = 'Sending…';
try {
const res = await fetch(contactUrl, {
method: 'POST',
headers: { 'X-Requested-With': 'XMLHttpRequest' },
body: new FormData(contactForm),
});
// Try to parse JSON; fall back gracefully if server returned HTML (PHP error)
let data;
const raw = await res.text();
try {
data = JSON.parse(raw);
} catch (_) {
throw new Error('Unexpected server response.');
}
showAlert(data.message, data.success ? 'success' : 'error');
if (data.success) {
contactForm.reset();
}
// Always refresh captcha after any submission attempt
if (data.new_captcha) {
refreshCaptcha(data.new_captcha);
} else {
fetch(contactUrl + '?refresh_captcha=1')
.then(r => r.json())
.then(d => { if (d.question) refreshCaptcha(d.question); })
.catch(() => {});
}
} catch (err) {
showAlert('Something went wrong. Please try again or email us at <a href="mailto:ftn-support@ftnconsortium.com">ftn-support@ftnconsortium.com</a>.', 'error');
} finally {
submitBtn.disabled = false;
submitBtn.textContent = 'Send Message →';
}
});
}