← 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;

})();