← Kapat
main.js
(function () {
"use strict";
/**
* ============================================================================
* CORE UTILITIES
* ============================================================================
*/
/**
* Smooth scroll with cubic easing (premium animation)
*/
function smoothScrollTo(targetY, duration = 800) {
const startY = window.scrollY;
const difference = targetY - startY;
const startTime = performance.now();
function easeOutCubic(t) {
return 1 - Math.pow(1 - t, 3);
}
function step(currentTime) {
const elapsed = currentTime - startTime;
let progress = elapsed / duration;
if (progress > 1) progress = 1;
const val = easeOutCubic(progress);
const nextY = startY + (difference * val);
window.scrollTo(0, nextY);
if (progress < 1) {
requestAnimationFrame(step);
}
}
requestAnimationFrame(step);
}
/**
* Calculate element top position with header offset
*/
function getElementTop(element) {
const header = document.querySelector('#header');
const offset = header ? header.offsetHeight : 0;
return element.getBoundingClientRect().top + window.scrollY - offset;
}
/**
* ============================================================================
* NAVIGATION SYSTEM (Clean URLs & SPA Behavior)
* ============================================================================
*/
const Navigation = {
sections: ['hakkimizda', 'hizmetler', 'projeler', 'iletisim', 'referanslar',
'ozellikler', 'istatistikler', 'musteri-gorusleri', 'proje-sureci', 'teklif-al'],
init() {
this.setupClickHandlers();
this.handleInitialLoad();
this.setupScrollSpy();
},
setupClickHandlers() {
document.querySelectorAll('#navmenu a').forEach(link => {
link.addEventListener('click', (e) => this.handleLinkClick(e, link));
});
},
handleLinkClick(e, link) {
const href = link.getAttribute('href');
// Handle homepage link
if (href === './' || href === '/' || href === 'index.php') {
const isOnHome = window.location.pathname.endsWith('/') ||
window.location.pathname.endsWith('index.php') ||
window.location.pathname.split('/').pop() === '';
if (isOnHome) {
e.preventDefault();
this.closeMobileMenu();
smoothScrollTo(0, 800);
this.updateURL('./');
}
return;
}
// Handle section links
const cleanId = href.replace(/^(\.\/|\/|#)/, '');
const section = document.getElementById(cleanId);
if (section) {
e.preventDefault();
this.closeMobileMenu();
const targetTop = getElementTop(section);
smoothScrollTo(targetTop, 800);
this.updateURL(cleanId);
}
},
handleInitialLoad() {
const pathSegments = window.location.pathname.split('/').filter(Boolean);
const lastSegment = pathSegments[pathSegments.length - 1] || '';
let targetId = window.location.hash.replace('#', '');
if (!targetId && this.sections.includes(lastSegment)) {
targetId = lastSegment;
}
const section = document.getElementById(targetId);
if (section) {
setTimeout(() => {
const targetTop = getElementTop(section);
window.scrollTo({ top: targetTop, behavior: 'smooth' });
}, 100);
}
},
setupScrollSpy() {
const navLinks = document.querySelectorAll('.navmenu a');
const updateActiveLink = () => {
const position = window.scrollY + 200;
navLinks.forEach(link => {
const cleanId = link.getAttribute('href').replace(/^(\.\/|\/|#)/, '');
const section = document.getElementById(cleanId);
if (section && position >= section.offsetTop &&
position <= (section.offsetTop + section.offsetHeight)) {
navLinks.forEach(l => l.classList.remove('active'));
link.classList.add('active');
}
});
};
window.addEventListener('load', updateActiveLink);
document.addEventListener('scroll', updateActiveLink);
},
updateURL(path) {
if (history.pushState) {
const url = path === './' ? './' : path;
history.pushState(null, null, url);
}
},
closeMobileMenu() {
if (document.body.classList.contains('mobile-nav-active')) {
document.body.classList.remove('mobile-nav-active');
const toggleBtn = document.querySelector('.mobile-nav-toggle');
if (toggleBtn) {
toggleBtn.classList.remove('bi-x');
toggleBtn.classList.add('bi-list');
}
}
}
};
/**
* ============================================================================
* UI COMPONENTS
* ============================================================================
*/
// Header scroll effect
function initHeaderScroll() {
const body = document.querySelector('body');
const header = document.querySelector('#header');
if (!header || (!header.classList.contains('scroll-up-sticky') &&
!header.classList.contains('sticky-top') &&
!header.classList.contains('fixed-top'))) return;
const toggleScrolled = () => {
window.scrollY > 100 ? body.classList.add('scrolled') : body.classList.remove('scrolled');
};
document.addEventListener('scroll', toggleScrolled);
window.addEventListener('load', toggleScrolled);
}
// Mobile navigation toggle
function initMobileNav() {
const toggleBtn = document.querySelector('.mobile-nav-toggle');
if (!toggleBtn) return;
toggleBtn.addEventListener('click', () => {
document.body.classList.toggle('mobile-nav-active');
toggleBtn.classList.toggle('bi-list');
toggleBtn.classList.toggle('bi-x');
});
// Dropdown toggles
document.querySelectorAll('.navmenu .toggle-dropdown').forEach(toggle => {
toggle.addEventListener('click', function (e) {
e.preventDefault();
this.parentNode.classList.toggle('active');
this.parentNode.nextElementSibling.classList.toggle('dropdown-active');
e.stopImmediatePropagation();
});
});
}
// Scroll to top button
function initScrollTop() {
const scrollTop = document.querySelector('.scroll-top');
if (!scrollTop) return;
const toggleVisibility = () => {
window.scrollY > 100 ? scrollTop.classList.add('active') : scrollTop.classList.remove('active');
};
scrollTop.addEventListener('click', (e) => {
e.preventDefault();
smoothScrollTo(0, 800);
});
window.addEventListener('load', toggleVisibility);
document.addEventListener('scroll', toggleVisibility);
}
// Preloader - Initialize immediately, not on window load
function initPreloader() {
const preloader = document.querySelector('#preloader');
if (preloader) {
window.addEventListener('load', () => {
preloader.remove();
});
}
}
// Call preloader init immediately
initPreloader();
// AOS Animation
function initAOS() {
if (typeof AOS !== 'undefined') {
AOS.init({
duration: 600,
easing: 'ease-in-out',
once: true,
mirror: false
});
}
}
// Swiper sliders
function initSwiper() {
if (typeof Swiper === 'undefined') return;
document.querySelectorAll(".init-swiper").forEach(swiperElement => {
const config = JSON.parse(swiperElement.querySelector(".swiper-config").innerHTML.trim());
new Swiper(swiperElement, config);
});
}
// GLightbox
function initGLightbox() {
if (typeof GLightbox !== 'undefined') {
GLightbox({ selector: '.glightbox' });
}
}
// Isotope layout
function initIsotope() {
if (typeof Isotope === 'undefined' || typeof imagesLoaded === 'undefined') return;
document.querySelectorAll('.isotope-layout').forEach(isotopeItem => {
const layout = isotopeItem.getAttribute('data-layout') ?? 'masonry';
const filter = isotopeItem.getAttribute('data-default-filter') ?? '*';
const sort = isotopeItem.getAttribute('data-sort') ?? 'original-order';
let initIsotope;
imagesLoaded(isotopeItem.querySelector('.isotope-container'), () => {
initIsotope = new Isotope(isotopeItem.querySelector('.isotope-container'), {
itemSelector: '.isotope-item',
layoutMode: layout,
filter: filter,
sortBy: sort
});
});
isotopeItem.querySelectorAll('.isotope-filters li').forEach(filterBtn => {
filterBtn.addEventListener('click', function () {
isotopeItem.querySelector('.isotope-filters .filter-active').classList.remove('filter-active');
this.classList.add('filter-active');
initIsotope.arrange({ filter: this.getAttribute('data-filter') });
if (typeof AOS !== 'undefined') AOS.refresh();
});
});
});
}
// PureCounter
function initPureCounter() {
if (typeof PureCounter !== 'undefined') {
new PureCounter();
}
}
// Icon box smooth scroll
function initIconBoxScroll() {
document.querySelectorAll('.icon-box[href="hizmetler"], .icon-box[href="#hizmetler"]').forEach(iconBox => {
iconBox.addEventListener('click', (e) => {
e.preventDefault();
const target = document.getElementById('hizmetler');
if (target) {
const top = getElementTop(target);
smoothScrollTo(top, 800);
}
});
});
}
/**
* ============================================================================
* CONTACT WIDGET
* ============================================================================
*/
function initContactWidget() {
const widget = document.getElementById('contactWidget');
if (!widget) return;
const widgetButton = widget.querySelector('.contact-widget-button');
const widgetMenu = widget.querySelector('.contact-widget-menu');
const widgetPrompt = widget.querySelector('.contact-widget-prompt');
const promptClose = widget.querySelector('.prompt-close');
const callbackTrigger = widget.querySelector('.callback-trigger');
const campaignTrigger = widget.querySelector('.campaign-trigger');
let promptDismissed = false;
let promptHideTimer;
const hidePrompt = (options = {}) => {
const { removeNotification = true, dismiss = false } = options;
if (!widgetPrompt) return;
widgetPrompt.classList.remove('show');
if (promptHideTimer) {
clearTimeout(promptHideTimer);
promptHideTimer = null;
}
if (dismiss) promptDismissed = true;
if (removeNotification) widget.classList.remove('has-notification');
};
const showPrompt = () => {
if (promptDismissed || !widgetPrompt) return;
widget.classList.remove('has-notification');
widgetPrompt.classList.add('show');
promptHideTimer = setTimeout(() => {
widget.classList.add('has-notification');
hidePrompt({ removeNotification: false, dismiss: true });
}, 2000);
};
widgetButton.addEventListener('click', () => {
widget.classList.toggle('active');
if (widget.classList.contains('active')) {
hidePrompt({ removeNotification: true, dismiss: true });
widgetMenu.classList.remove('show-campaign');
}
});
document.addEventListener('click', (e) => {
if (!widget.contains(e.target) && widget.classList.contains('active')) {
widget.classList.remove('active');
widgetMenu.classList.remove('show-campaign');
}
});
setTimeout(showPrompt, 1000);
if (promptClose) {
promptClose.addEventListener('click', (e) => {
e.stopPropagation();
hidePrompt({ removeNotification: true, dismiss: true });
});
}
widgetPrompt.addEventListener('click', (e) => {
if (e.target !== promptClose && !promptClose.contains(e.target)) {
widget.classList.add('active');
hidePrompt({ removeNotification: true, dismiss: true });
}
});
if (callbackTrigger) {
callbackTrigger.addEventListener('click', (e) => {
e.preventDefault();
widget.classList.remove('active');
widgetMenu.classList.remove('show-campaign');
const contactSection = document.querySelector('#iletisim');
if (contactSection) {
const top = getElementTop(contactSection);
smoothScrollTo(top, 800);
}
});
}
if (campaignTrigger) {
campaignTrigger.addEventListener('click', (e) => {
e.preventDefault();
widgetMenu.classList.add('show-campaign');
});
}
widget.querySelectorAll('.campaign-btn').forEach(btn => {
btn.addEventListener('click', () => {
widget.classList.remove('active');
widgetMenu.classList.remove('show-campaign');
});
});
widget.querySelectorAll('.contact-item a:not(.callback-trigger):not(.campaign-trigger)').forEach(link => {
link.addEventListener('click', () => {
widget.classList.remove('active');
widgetMenu.classList.remove('show-campaign');
});
});
}
/**
* ============================================================================
* CALL REQUEST POPUP
* ============================================================================
*/
function initCallRequestPopup() {
const popup = document.getElementById('callRequestPopup');
const form = document.getElementById('callRequestForm');
if (!popup || !form) return;
const loadingMsg = popup.querySelector('.call-popup-loading');
const successMsg = popup.querySelector('.call-popup-success');
const errorMsg = popup.querySelector('.call-popup-error');
const closeElements = popup.querySelectorAll('[data-call-popup-close]');
const triggers = document.querySelectorAll('[data-call-popup="true"]');
const resetFeedback = () => {
[loadingMsg, successMsg, errorMsg].forEach(el => {
if (el) el.style.display = 'none';
});
};
const setFeedback = (state) => {
resetFeedback();
if (state === 'loading' && loadingMsg) loadingMsg.style.display = 'inline-flex';
if (state === 'success' && successMsg) successMsg.style.display = 'inline-flex';
if (state === 'error' && errorMsg) errorMsg.style.display = 'inline-flex';
};
const closePopup = () => {
popup.classList.remove('is-visible');
document.body.classList.remove('call-popup-open');
document.removeEventListener('keydown', handleEscape);
resetFeedback();
};
const handleEscape = (event) => {
if (event.key === 'Escape') closePopup();
};
const openPopup = (prefill = {}) => {
form.reset();
form.classList.remove('is-submitting');
resetFeedback();
if (prefill.service) {
const serviceField = form.querySelector('#call-service');
if (serviceField) serviceField.value = prefill.service;
}
popup.classList.add('is-visible');
document.body.classList.add('call-popup-open');
document.addEventListener('keydown', handleEscape);
};
closeElements.forEach(el => {
el.addEventListener('click', () => {
if (!form.classList.contains('is-submitting')) closePopup();
});
});
triggers.forEach(trigger => {
trigger.addEventListener('click', (event) => {
event.preventDefault();
openPopup({ service: trigger.dataset.callPopupService || '' });
});
});
form.addEventListener('submit', (event) => {
event.preventDefault();
if (!form.checkValidity()) {
form.reportValidity();
return;
}
form.classList.add('is-submitting');
setFeedback('loading');
fetch('forms/call-request.php', {
method: 'POST',
body: new FormData(form)
})
.then(response => {
if (!response.ok) throw new Error('Error');
return response.text();
})
.then(() => {
setFeedback('success');
form.reset();
setTimeout(closePopup, 2000);
})
.catch(() => setFeedback('error'))
.finally(() => form.classList.remove('is-submitting'));
});
}
/**
* ============================================================================
* PORTFOLIO MODAL
* ============================================================================
*/
function initPortfolioModal() {
const modal = document.getElementById("portfolioModal");
const modalBody = document.getElementById("portfolioModalBody");
const closeBtn = document.querySelector(".close-modal");
const detailsLinks = document.querySelectorAll(".details-link");
if (!modal) return;
const closeModal = () => {
modal.style.display = "none";
document.body.style.overflow = "";
};
if (closeBtn) closeBtn.onclick = closeModal;
window.onclick = (event) => {
if (event.target === modal) closeModal();
};
document.addEventListener('keydown', (event) => {
if (event.key === "Escape" && modal.style.display === "block") closeModal();
});
detailsLinks.forEach(link => {
link.addEventListener("click", function (e) {
e.preventDefault();
const slug = this.getAttribute("data-slug");
const project = window.portfolioData?.find(p => p.slug === slug);
if (!project) return;
let swiperHtml = '';
if (project.images?.length > 0) {
swiperHtml = `
<div class="portfolio-details-slider swiper init-swiper">
<script type="application/json" class="swiper-config">
{
"loop": true,
"speed": 600,
"autoplay": { "delay": 5000 },
"slidesPerView": "auto",
"pagination": { "el": ".swiper-pagination", "type": "bullets", "clickable": true }
}
</script>
<div class="swiper-wrapper align-items-center">
${project.images.map(img => `<div class="swiper-slide"><img src="${img}" alt=""></div>`).join('')}
</div>
<div class="swiper-pagination"></div>
</div>
`;
} else if (project.image) {
swiperHtml = `<img src="${project.image}" alt="" class="img-fluid rounded border mb-4">`;
}
modalBody.innerHTML = `
<div class="modal-project-details">
<h2>${project.title}</h2>
<div class="row">
<div class="col-lg-8">${swiperHtml}</div>
<div class="col-lg-4">
<div class="modal-project-info">
<h3>Proje Bilgileri</h3>
<ul>
<li><strong>Kategori</strong>: ${project.category || 'Belirtilmemiş'}</li>
<li><strong>Müşteri</strong>: ${project.client || 'Belirtilmemiş'}</li>
<li><strong>Tarih</strong>: ${project.date || 'Belirtilmemiş'}</li>
${project.url ? `<li><strong>Web URL</strong>: <a href="${project.url.startsWith('http') ? project.url : 'http://' + project.url}" target="_blank">${project.url}</a></li>` : ''}
</ul>
</div>
<div class="modal-project-description">
<h3>Detaylar</h3>
<p>${project.description || project.text || 'Detay bulunmuyor.'}</p>
</div>
</div>
</div>
</div>
`;
modal.style.display = "block";
document.body.style.overflow = "hidden";
initSwiper();
});
});
}
/**
* ============================================================================
* INITIALIZATION
* ============================================================================
*/
window.addEventListener('load', () => {
// Core UI
initHeaderScroll();
initMobileNav();
initScrollTop();
initAOS();
// Libraries
initSwiper();
initGLightbox();
initIsotope();
initPureCounter();
// Custom components
initIconBoxScroll();
initContactWidget();
initCallRequestPopup();
initPortfolioModal();
// Navigation system
Navigation.init();
});
// Make initSwiper globally available for modal re-initialization
window.initSwiper = initSwiper;
})();