198 lines
6.8 KiB
PHP
198 lines
6.8 KiB
PHP
<?php
|
|
// Ce Footer sera inseré sur des pages grâce a la fonction require_once('./../elements/footer.php')
|
|
// Rappel des différentes pages : Acceuil, Projets, Contact
|
|
|
|
// Notifications "toast" : affichées selon le paramètre présent dans l'URL
|
|
// (?success=1 après un message de contact, ?login=1 après connexion, ?logout=1 après déconnexion, ?import=1 après un import)
|
|
$toast = null;
|
|
if (isset($_GET['success']) && $_GET['success'] === '1') {
|
|
$toast = 'Votre message a bien été envoyé !';
|
|
} elseif (isset($_GET['login']) && $_GET['login'] === '1') {
|
|
$toast = 'Connexion réussie' . (isset($_SESSION['username']) ? ', bonjour ' . htmlspecialchars($_SESSION['username']) . ' !' : ' !');
|
|
} elseif (isset($_GET['logout']) && $_GET['logout'] === '1') {
|
|
$toast = 'Vous avez bien été déconnecté.';
|
|
} elseif (isset($_GET['import']) && $_GET['import'] === '1') {
|
|
$toast = 'Les données ont bien été importées.';
|
|
}
|
|
?>
|
|
</main>
|
|
|
|
<?php if ($toast !== null): ?>
|
|
<!-- Notification toast : disparaît toute seule, clic pour fermer -->
|
|
<div class="toast" onclick="this.remove()" role="status">
|
|
<i class="fa-solid fa-circle-check"></i>
|
|
<span><?php echo $toast; ?></span>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<footer class="site-footer">
|
|
<div class="footer-content">
|
|
<p class="footer-copyright">
|
|
<!-- Logo droit d'auteur + année -->
|
|
© <?php echo date('Y'); ?> - Portfolio développé par Esteban MOUSSU
|
|
</p>
|
|
<div class="footer-links">
|
|
<a href="https://www.linkedin.com/in/estebanmoussu/" target="_blank" rel="noopener noreferrer">LinkedIn</a>
|
|
<a href="https://www.instagram.com/ours_one_" target="_blank" rel="noopener noreferrer">Instagram</a>
|
|
<a href="https://git.eldorianet.work/esteban" target="_blank" rel="noopener noreferrer">Git</a>
|
|
<span class="discord-handle" title="@ours1" onclick="alert('Discord : @ours1')">Discord</span>
|
|
<a href="../pages/cgu.php">CGU</a>
|
|
</div>
|
|
</div>
|
|
</footer>
|
|
|
|
<!-- Apparition des cartes au défilement (IntersectionObserver) -->
|
|
<script>
|
|
(function () {
|
|
// On respecte la préférence "réduire les animations" du système
|
|
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
|
|
if (!('IntersectionObserver' in window)) return;
|
|
|
|
// Éléments qui apparaissent en glissant vers le haut quand ils entrent dans l'écran
|
|
var cibles = document.querySelectorAll(
|
|
'.project-card, .quick-link-card, .skill-category, .contact-card, ' +
|
|
'.login-card, .project-form-card, .cgu-card, .logo-admin-item, ' +
|
|
'.page-header, .quick-links-header, .skills-header, .projects-title, .logo-banner-header, ' +
|
|
'.stat-item'
|
|
);
|
|
|
|
var observer = new IntersectionObserver(function (entries) {
|
|
entries.forEach(function (entry) {
|
|
if (entry.isIntersecting) {
|
|
entry.target.classList.add('visible');
|
|
observer.unobserve(entry.target);
|
|
}
|
|
});
|
|
}, { threshold: 0.1, rootMargin: '0px 0px -40px 0px' });
|
|
|
|
cibles.forEach(function (el, i) {
|
|
el.classList.add('reveal');
|
|
// Petit décalage en cascade pour les éléments d'une même grille
|
|
el.style.setProperty('--reveal-delay', ((i % 4) * 0.08) + 's');
|
|
observer.observe(el);
|
|
});
|
|
})();
|
|
</script>
|
|
|
|
<!-- Easter egg : code Konami (↑↑↓↓←→←→BA) => ouvre le mini-jeu "Presse-Citron" -->
|
|
<style>
|
|
#konami-overlay {
|
|
position: fixed;
|
|
inset: 0;
|
|
background: rgba(0, 0, 0, 0.72);
|
|
z-index: 9999;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 3vh 3vw;
|
|
animation: konami-fade-in 0.2s ease;
|
|
}
|
|
|
|
@keyframes konami-fade-in {
|
|
from { opacity: 0; }
|
|
to { opacity: 1; }
|
|
}
|
|
|
|
.konami-modal {
|
|
position: relative;
|
|
width: min(1100px, 100%);
|
|
height: min(760px, 94vh);
|
|
border-radius: 18px;
|
|
overflow: hidden;
|
|
box-shadow: 0 30px 80px rgba(0, 0, 0, 0.55);
|
|
}
|
|
|
|
.konami-modal iframe {
|
|
width: 100%;
|
|
height: 100%;
|
|
border: none;
|
|
display: block;
|
|
}
|
|
|
|
.konami-close {
|
|
position: absolute;
|
|
top: 10px;
|
|
right: 10px;
|
|
width: 34px;
|
|
height: 34px;
|
|
border-radius: 50%;
|
|
border: none;
|
|
background: rgba(0, 0, 0, 0.55);
|
|
color: #fff;
|
|
font-size: 15px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: pointer;
|
|
z-index: 2;
|
|
}
|
|
|
|
.konami-close:hover {
|
|
background: rgba(0, 0, 0, 0.8);
|
|
}
|
|
</style>
|
|
<script>
|
|
(function () {
|
|
// Séquence classique : Haut Haut Bas Bas Gauche Droite Gauche Droite B A
|
|
var sequence = ['ArrowUp', 'ArrowUp', 'ArrowDown', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'ArrowLeft', 'ArrowRight', 'b', 'a'];
|
|
var progres = 0;
|
|
|
|
document.addEventListener('keydown', function (e) {
|
|
// Ignore la saisie dans un champ de formulaire (évite de déclencher pendant qu'on tape)
|
|
var cible = e.target;
|
|
if (cible && (cible.tagName === 'INPUT' || cible.tagName === 'TEXTAREA' || cible.isContentEditable)) return;
|
|
|
|
var touche = e.key.length === 1 ? e.key.toLowerCase() : e.key;
|
|
var attendue = sequence[progres];
|
|
|
|
if (touche === attendue) {
|
|
progres++;
|
|
if (progres === sequence.length) {
|
|
progres = 0;
|
|
ouvrirPresseCitron();
|
|
}
|
|
} else {
|
|
// Permet de relancer la séquence même si elle recommence par la même touche
|
|
progres = (touche === sequence[0]) ? 1 : 0;
|
|
}
|
|
});
|
|
|
|
function ouvrirPresseCitron() {
|
|
if (document.getElementById('konami-overlay')) return;
|
|
|
|
var overlay = document.createElement('div');
|
|
overlay.id = 'konami-overlay';
|
|
overlay.innerHTML =
|
|
'<div class="konami-modal">' +
|
|
'<button class="konami-close" aria-label="Fermer"><i class="fa-solid fa-xmark"></i></button>' +
|
|
'<iframe src="./../pages/citron.php" title="Presse-Citron"></iframe>' +
|
|
'</div>';
|
|
document.body.appendChild(overlay);
|
|
|
|
overlay.querySelector('.konami-close').addEventListener('click', fermerPresseCitron);
|
|
overlay.addEventListener('click', function (e) {
|
|
if (e.target === overlay) fermerPresseCitron();
|
|
});
|
|
document.addEventListener('keydown', echapFerme);
|
|
}
|
|
|
|
function echapFerme(e) {
|
|
if (e.key === 'Escape') fermerPresseCitron();
|
|
}
|
|
|
|
function fermerPresseCitron() {
|
|
var overlay = document.getElementById('konami-overlay');
|
|
if (overlay) overlay.remove();
|
|
document.removeEventListener('keydown', echapFerme);
|
|
}
|
|
|
|
// Le jeu (dans l'iframe) peut aussi demander la fermeture (touche Échap captée à
|
|
// l'intérieur de l'iframe, qui ne reçoit pas les événements clavier de la page parente)
|
|
window.addEventListener('message', function (e) {
|
|
if (e.data === 'citron-close') fermerPresseCitron();
|
|
});
|
|
})();
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|