/* Corvella — behaviours, one at a time.

   1. Menu: restart the opening animation, close on Escape and on a link
   2. Reveals: show blocks as they come up the page
   3. Parallax: write a 0-to-1 progress into a CSS variable
   4. The window: the lens follows the pointer
   5. Carousel: arrows and the spoken position
   6. Booking form: no back end, so say so instead of navigating

   Nothing here hides content. Every hidden state is a CSS rule behind the
   "js" class set by enhance.js, so a failure in this file leaves the page
   readable rather than blank. */

(function () {
  'use strict';

  var doux = window.matchMedia('(prefers-reduced-motion: reduce)');

  /* --- 1. Menu ---------------------------------------------------------- */

  var menu = document.querySelector('[data-menu]');
  if (menu) {
    var panneau = menu.querySelector('.menu__panel');

    // Closing a <details> does not remove its content from the document, the
    // browser hides it with content-visibility — so the keyframes never run a
    // second time. Taking the animation off and putting it back does.
    menu.addEventListener('toggle', function () {
      if (!menu.open || !panneau) return;
      panneau.style.animation = 'none';
      void panneau.offsetWidth;          // force a reflow between the two
      panneau.style.animation = '';
    });

    menu.addEventListener('click', function (evenement) {
      if (evenement.target.closest('.menu__link')) menu.open = false;
    });

    document.addEventListener('keydown', function (evenement) {
      if (evenement.key === 'Escape' && menu.open) {
        menu.open = false;
        menu.querySelector('summary').focus();
      }
    });
  }

  /* --- 2. Reveals ------------------------------------------------------- */

  var aReveler = document.querySelectorAll('[data-reveal]');

  function toutReveler() {
    aReveler.forEach(function (element) { element.classList.add('is-in'); });
  }

  if (!('IntersectionObserver' in window) || doux.matches) {
    toutReveler();
  } else {
    // The trigger is measured on the SCREEN, not on the element: a threshold
    // expressed as a share of the element would never fire for a block taller
    // than the viewport.
    var guetteur = new IntersectionObserver(function (entrees) {
      entrees.forEach(function (entree) {
        if (!entree.isIntersecting) return;
        entree.target.classList.add('is-in');
        guetteur.unobserve(entree.target);
      });
    }, { rootMargin: '0px 0px -15% 0px' });

    aReveler.forEach(function (element) { guetteur.observe(element); });

    // Safety net: whatever is still hidden when the page bottom is reached
    // gets shown, so nothing can stay invisible for good.
    window.addEventListener('scroll', function () {
      var reste = window.innerHeight + window.scrollY;
      if (reste >= document.body.scrollHeight - 4) toutReveler();
    }, { passive: true });
  }

  /* --- 3. Parallax ------------------------------------------------------ */

  var paralleles = [].slice.call(document.querySelectorAll('[data-parallax]'));

  function progression(boite) {
    var hauteur = window.innerHeight;
    var p = (hauteur - boite.top) / (hauteur + boite.height);
    return Math.min(1, Math.max(0, p));
  }

  var enAttente = false;
  function placerParallaxes() {
    enAttente = false;
    paralleles.forEach(function (element) {
      var cadre = element.parentElement || element;
      element.style.setProperty('--p', progression(cadre.getBoundingClientRect()).toFixed(4));
    });
  }

  if (paralleles.length && !doux.matches) {
    window.addEventListener('scroll', function () {
      if (enAttente) return;
      enAttente = true;
      requestAnimationFrame(placerParallaxes);
    }, { passive: true });
    window.addEventListener('resize', placerParallaxes);
    placerParallaxes();
  }

  /* --- 4. The window ---------------------------------------------------- */

  var fenetre = document.querySelector('[data-window]');
  if (fenetre && !doux.matches) {
    var lentille = fenetre.querySelector('.window__lens');

    fenetre.addEventListener('pointermove', function (evenement) {
      if (evenement.pointerType === 'touch') return;
      var boite = fenetre.getBoundingClientRect();
      var taille = lentille.offsetWidth;
      // keep the lens whole inside the frame
      var limiteX = (boite.width - taille) / 2;
      var limiteY = (boite.height - taille) / 2;
      var x = evenement.clientX - boite.left - boite.width / 2;
      var y = evenement.clientY - boite.top - boite.height / 2;
      fenetre.style.setProperty('--x', Math.max(-limiteX, Math.min(limiteX, x)) + 'px');
      fenetre.style.setProperty('--y', Math.max(-limiteY, Math.min(limiteY, y)) + 'px');
    });
  }

  /* --- 5. Carousel ------------------------------------------------------ */

  var carrousel = document.querySelector('[data-carousel]');
  if (carrousel) {
    var piste = carrousel.querySelector('.carousel__track');
    var vues = carrousel.querySelectorAll('.carousel__slide');
    var etat = carrousel.querySelector('[data-carousel-status]');

    function glisser(sens) {
      piste.scrollBy({ left: sens * piste.clientWidth, behavior: doux.matches ? 'auto' : 'smooth' });
    }

    carrousel.querySelector('[data-carousel-prev]').addEventListener('click', function () { glisser(-1); });
    carrousel.querySelector('[data-carousel-next]').addEventListener('click', function () { glisser(1); });

    var annonce = null;
    piste.addEventListener('scroll', function () {
      clearTimeout(annonce);
      annonce = setTimeout(function () {
        var rang = Math.round(piste.scrollLeft / piste.clientWidth) + 1;
        if (etat) etat.textContent = 'Photograph ' + rang + ' of ' + vues.length;
      }, 120);
    }, { passive: true });
  }

  /* --- 6. Booking form -------------------------------------------------- */

  var formulaire = document.querySelector('[data-booking]');
  if (formulaire) {
    var confirmation = formulaire.querySelector('[data-booking-done]');
    formulaire.addEventListener('submit', function (evenement) {
      // This template ships without a server. Point the action at your own
      // endpoint and delete these four lines to send the booking for real.
      evenement.preventDefault();
      if (!confirmation) return;
      confirmation.hidden = false;
      confirmation.setAttribute('role', 'status');
    });
  }
}());
