initial commit
8
.idea/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
8
.idea/modules.xml
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/mon-portfolio.iml" filepath="$PROJECT_DIR$/.idea/mon-portfolio.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
12
.idea/mon-portfolio.iml
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="WEB_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
119
404.html
Normal file
|
|
@ -0,0 +1,119 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="fr">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta content="width=device-width, initial-scale=1.0" name="viewport">
|
||||||
|
<title>Page non trouvée - Ewen GADONNAUD</title>
|
||||||
|
<meta name="description" content="Page non trouvée">
|
||||||
|
<meta name="keywords" content="404, erreur, page non trouvée">
|
||||||
|
|
||||||
|
<!-- Favicons -->
|
||||||
|
<link rel="apple-touch-icon" sizes="57x57" href="assets/img/favicons.ico/">
|
||||||
|
<link rel="apple-touch-icon" sizes="60x60" href="assets/img/favicons.ico/apple-icon-60x60.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="72x72" href="assets/img/favicons.ico/apple-icon-72x72.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="76x76" href="assets/img/favicons.ico/apple-icon-76x76.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="114x114" href="assets/img/favicons.ico/apple-icon-114x114.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="120x120" href="assets/img/favicons.ico/apple-icon-120x120.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="144x144" href="assets/img/favicons.ico/apple-icon-144x144.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="152x152" href="assets/img/favicons.ico/apple-icon-152x152.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="assets/img/favicons.ico/apple-icon-180x180.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="192x192" href="assets/img/favicons.ico/android-icon-192x192.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="assets/img/favicons.ico/favicon-32x32.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="96x96" href="assets/img/favicons.ico/favicon-96x96.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="assets/img/favicons.ico/favicon-16x16.png">
|
||||||
|
<link rel="manifest" href="assets/img/favicons.ico/manifest.json">
|
||||||
|
<meta name="msapplication-TileColor" content="#ffffff">
|
||||||
|
<meta name="msapplication-TileImage" content="assets/img/favicons.ico/ms-icon-144x144.png">
|
||||||
|
<meta name="theme-color" content="#ffffff">
|
||||||
|
|
||||||
|
<!-- Fonts -->
|
||||||
|
<link href="https://fonts.googleapis.com" rel="preconnect">
|
||||||
|
<link href="https://fonts.gstatic.com" rel="preconnect" crossorigin>
|
||||||
|
<link
|
||||||
|
href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Raleway:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap"
|
||||||
|
rel="stylesheet">
|
||||||
|
|
||||||
|
<!-- Vendor CSS Files -->
|
||||||
|
<link href="assets/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link href="assets/vendor/bootstrap-icons/bootstrap-icons.css" rel="stylesheet">
|
||||||
|
<link href="assets/vendor/aos/aos.css" rel="stylesheet">
|
||||||
|
<link href="assets/vendor/swiper/swiper-bundle.min.css" rel="stylesheet">
|
||||||
|
<link href="assets/vendor/glightbox/css/glightbox.min.css" rel="stylesheet">
|
||||||
|
|
||||||
|
<!-- Main CSS File -->
|
||||||
|
<link href="assets/css/main.css" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="index-page">
|
||||||
|
|
||||||
|
<header id="header" class="header d-flex align-items-center light-background sticky-top">
|
||||||
|
<canvas class="matrix-bg-header" aria-hidden="true"></canvas>
|
||||||
|
<div class="container position-relative d-flex align-items-center justify-content-between">
|
||||||
|
<nav id="navmenu" class="navmenu">
|
||||||
|
<ul>
|
||||||
|
<li><a href="index.html">Accueil</a></li>
|
||||||
|
<li><a href="about.html">À propos de moi</a></li>
|
||||||
|
<li><a href="portfolio.html">Projets</a></li>
|
||||||
|
<li><a href="formation.html">Formation</a></li>
|
||||||
|
<li><a href="resume.html">Mon CV</a></li>
|
||||||
|
<li><a href="services.html">Mes services</a></li>
|
||||||
|
<li><a href="contact.html">Contact</a></li>
|
||||||
|
</ul>
|
||||||
|
<i class="mobile-nav-toggle d-xl-none bi bi-list"></i>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="main">
|
||||||
|
<section class="section d-flex align-items-center justify-content-center" style="min-height: 80vh;">
|
||||||
|
<div class="container text-center" data-aos="fade-up">
|
||||||
|
<h1 class="display-1 fw-bold text-primary">404</h1>
|
||||||
|
<h2 class="mb-4">Oups ! Page non trouvée.</h2>
|
||||||
|
<p class="mb-4">La page que vous recherchez semble avoir disparu ou n'existe pas.</p>
|
||||||
|
<a href="index.html" class="btn btn-primary btn-lg rounded-pill px-5">Retour à l'accueil</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer id="footer" class="footer">
|
||||||
|
<div class="container">
|
||||||
|
<div class="copyright text-center ">
|
||||||
|
<p>© <span>Copyright</span> <strong class="px-1 sitename">GADONNAUD Ewen</strong> <span>Tout droits
|
||||||
|
réservés<br></span></p>
|
||||||
|
</div>
|
||||||
|
<div class="social-links d-flex justify-content-center">
|
||||||
|
<a href="https://www.linkedin.com/in/ewen-gadonnaud-b17491283" aria-label="LinkedIn"><i
|
||||||
|
class="bi bi-linkedin"></i></a>
|
||||||
|
<a href="https://github.com/Azuneer" aria-label="GitHub"><i class="bi bi-github"></i></a>
|
||||||
|
</div>
|
||||||
|
<div class="credits">
|
||||||
|
Designé avec <a href="https://bootstrapmade.com/">BootstrapMade</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<!-- Scroll Top -->
|
||||||
|
<a href="#" id="scroll-top" class="scroll-top d-flex align-items-center justify-content-center"><i
|
||||||
|
class="bi bi-arrow-up-short"></i></a>
|
||||||
|
|
||||||
|
<!-- Preloader -->
|
||||||
|
<div id="preloader"></div>
|
||||||
|
|
||||||
|
<!-- Vendor JS Files -->
|
||||||
|
<script src="assets/vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<script src="assets/vendor/aos/aos.js"></script>
|
||||||
|
<script src="assets/vendor/typed.js/typed.umd.js"></script>
|
||||||
|
<script src="assets/vendor/waypoints/noframework.waypoints.js"></script>
|
||||||
|
<script src="assets/vendor/purecounter/purecounter_vanilla.js"></script>
|
||||||
|
<script src="assets/vendor/swiper/swiper-bundle.min.js"></script>
|
||||||
|
<script src="assets/vendor/imagesloaded/imagesloaded.pkgd.min.js"></script>
|
||||||
|
<script src="assets/vendor/isotope-layout/isotope.pkgd.min.js"></script>
|
||||||
|
<script src="assets/vendor/glightbox/js/glightbox.min.js"></script>
|
||||||
|
|
||||||
|
<!-- Main JS File -->
|
||||||
|
<script src="assets/js/main.js"></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
493
about.html
Normal file
|
|
@ -0,0 +1,493 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="fr">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta content="width=device-width, initial-scale=1.0" name="viewport">
|
||||||
|
<title>À Propos</title>
|
||||||
|
<meta name="description"
|
||||||
|
content="En savoir plus sur Ewen GADONNAUD : mon parcours, mes compétences en informatique (Linux, Cisco, AWS), mes certifications et mes ambitions professionnelles.">
|
||||||
|
<meta name="keywords"
|
||||||
|
content="portfolio, administrateur système, gestion de parc informatique, expérience, étudiant, stages, entreprise, cisco, linux, windows, devops">
|
||||||
|
|
||||||
|
<!-- Favicons -->
|
||||||
|
<link rel="apple-touch-icon" sizes="57x57" href="assets/img/favicons.ico/">
|
||||||
|
<link rel="apple-touch-icon" sizes="60x60" href="assets/img/favicons.ico/apple-icon-60x60.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="72x72" href="assets/img/favicons.ico/apple-icon-72x72.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="76x76" href="assets/img/favicons.ico/apple-icon-76x76.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="114x114" href="assets/img/favicons.ico/apple-icon-114x114.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="120x120" href="assets/img/favicons.ico/apple-icon-120x120.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="144x144" href="assets/img/favicons.ico/apple-icon-144x144.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="152x152" href="assets/img/favicons.ico/apple-icon-152x152.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="assets/img/favicons.ico/apple-icon-180x180.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="192x192" href="assets/img/favicons.ico/android-icon-192x192.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="assets/img/favicons.ico/favicon-32x32.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="96x96" href="assets/img/favicons.ico/favicon-96x96.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="assets/img/favicons.ico/favicon-16x16.png">
|
||||||
|
<link rel="manifest" href="assets/img/favicons.ico/manifest.json">
|
||||||
|
<meta name="msapplication-TileColor" content="#ffffff">
|
||||||
|
<meta name="msapplication-TileImage" content="assets/img/favicons.ico/ms-icon-144x144.png">
|
||||||
|
<meta name="theme-color" content="#ffffff">
|
||||||
|
|
||||||
|
<!-- Fonts -->
|
||||||
|
<link href="https://fonts.googleapis.com" rel="preconnect">
|
||||||
|
<link href="https://fonts.gstatic.com" rel="preconnect" crossorigin>
|
||||||
|
<link
|
||||||
|
href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Raleway:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap"
|
||||||
|
rel="stylesheet">
|
||||||
|
|
||||||
|
<!-- Vendor CSS Files -->
|
||||||
|
<link href="assets/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link href="assets/vendor/bootstrap-icons/bootstrap-icons.css" rel="stylesheet">
|
||||||
|
<link href="assets/vendor/aos/aos.css" rel="stylesheet">
|
||||||
|
<link href="assets/vendor/swiper/swiper-bundle.min.css" rel="stylesheet">
|
||||||
|
<link href="assets/vendor/glightbox/css/glightbox.min.css" rel="stylesheet">
|
||||||
|
|
||||||
|
<!-- Main CSS File -->
|
||||||
|
<link href="assets/css/main.css" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="about-page">
|
||||||
|
|
||||||
|
<header id="header" class="header d-flex align-items-center light-background sticky-top">
|
||||||
|
<canvas class="matrix-bg-header" aria-hidden="true"></canvas>
|
||||||
|
<div class="container position-relative d-flex align-items-center justify-content-between">
|
||||||
|
|
||||||
|
<nav id="navmenu" class="navmenu">
|
||||||
|
<ul>
|
||||||
|
<li><a href="index.html">Accueil</a></li>
|
||||||
|
<li><a href="about.html" class="active">À propos de moi</a></li>
|
||||||
|
<li><a href="portfolio.html">Projets</a></li>
|
||||||
|
<li><a href="formation.html">Formation</a></li>
|
||||||
|
<li><a href="resume.html">Mon CV</a></li>
|
||||||
|
<li><a href="services.html">Mes services</a></li>
|
||||||
|
<li><a href="contact.html">Contact</a></li>
|
||||||
|
</ul>
|
||||||
|
<i class="mobile-nav-toggle d-xl-none bi bi-list"></i>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="header-social-links">
|
||||||
|
<a href="https://www.linkedin.com/in/ewen-gadonnaud-b17491283" class="linkedin" target="_blank"
|
||||||
|
rel="noopener noreferrer" aria-label="LinkedIn"><i class="bi bi-linkedin"></i></a>
|
||||||
|
<a href="https://github.com/Azuneer" class="github" target="_blank" rel="noopener noreferrer"
|
||||||
|
aria-label="GitHub"><i class="bi bi-github"></i></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="main">
|
||||||
|
|
||||||
|
<!-- About Section -->
|
||||||
|
<section id="about" class="about section">
|
||||||
|
|
||||||
|
<!-- Section Title -->
|
||||||
|
<div class="container section-title" data-aos="fade-up">
|
||||||
|
<h2>À Propos</h2>
|
||||||
|
<p>Depuis mon plus jeune âge, je suis passionné par les nouvelles technologies, surtout par internet et
|
||||||
|
toutes les micro-sphères qui en découlent. Il m'est paru alors naturel d'orienter ma carrière
|
||||||
|
académique et professionnelle vers un sujet que j'aime tant, le processus de mise en relation de
|
||||||
|
tout le monde sur terre grâce à de nouveaux moyens de communication qui florissent chaque jour.</p>
|
||||||
|
</div><!-- End Section Title -->
|
||||||
|
|
||||||
|
<div class="container" data-aos="fade-up" data-aos-delay="100">
|
||||||
|
|
||||||
|
<!-- Intro + Photo -->
|
||||||
|
<div class="row align-items-center justify-content-between gy-5 mb-5">
|
||||||
|
<div class="col-lg-7" data-aos="fade-right" data-aos-delay="150">
|
||||||
|
<div class="intro-content">
|
||||||
|
<span class="eyebrow">Bonjour</span>
|
||||||
|
<h2 class="headline">Je suis Ewen - étudiant en BTS SIO passionné par le DevOps, les réseaux
|
||||||
|
et le Cloud</h2>
|
||||||
|
<p class="lead">
|
||||||
|
Dans la vie de tous les jours, j'aime mettre en avant mon côté indépendant et ambitieux.
|
||||||
|
Je considère qu'il est primordial de ne jamais baisser les bras, quelle que soit la
|
||||||
|
situation.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Ce déterminisme et ce sens de l'autonomie sont à la hauteur de mes ambitions (qu'elles
|
||||||
|
soient professionnelles comme personnelles), et je n'accepterais pas de me voir baisser
|
||||||
|
les bras, car la vie est un combat de tous les jours.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Je pense également qu'il faut savoir être tolérant, tolérant envers les autres, mais
|
||||||
|
surtout envers soi-même. C'est pour ça qu'il ne faut pas avoir peur de tomber et
|
||||||
|
d'échouer pour repartir encore plus fort.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
J'aime être souriant dans la vie de tous les jours, même quand la vie me met à
|
||||||
|
l'épreuve. Je sais qu'avant tout, le positif attire le positif, et les gens redonnent
|
||||||
|
généralement la pareille.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="cta-group">
|
||||||
|
<a href="portfolio.html" class="btn-ghost">
|
||||||
|
Voir mes projets <i class="bi bi-arrow-up-right"></i>
|
||||||
|
</a>
|
||||||
|
<a href="assets/img/profile/CV_GADONNAUD_EWEN_2025.pdf" class="link-underline">
|
||||||
|
Télécharger mon CV <i class="bi bi-download"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-5" data-aos="zoom-in" data-aos-delay="250">
|
||||||
|
<figure class="profile-figure text-center text-lg-end">
|
||||||
|
<img src="assets/img/profile/me.webp" alt="Portrait d'Ewen" class="img-fluid profile-photo">
|
||||||
|
</figure>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- End Intro + Photo -->
|
||||||
|
|
||||||
|
<!-- Skills Grid -->
|
||||||
|
<div class="mb-5">
|
||||||
|
<div class="row g-4">
|
||||||
|
<div class="col-6 col-md-4 col-lg-3" data-aos="fade-up" data-aos-delay="120">
|
||||||
|
<div class="skill-item">
|
||||||
|
<i class="bi bi-hdd-network"></i>
|
||||||
|
<h3>Réseaux</h3>
|
||||||
|
<p>Configuration et gestion de réseaux Cisco.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-6 col-md-4 col-lg-3" data-aos="fade-up" data-aos-delay="180">
|
||||||
|
<div class="skill-item">
|
||||||
|
<i class="bi bi-terminal"></i>
|
||||||
|
<h3>Linux</h3>
|
||||||
|
<p>Administration système et lignes de commande.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-6 col-md-4 col-lg-3" data-aos="fade-up" data-aos-delay="240">
|
||||||
|
<div class="skill-item">
|
||||||
|
<i class="bi bi-pc-display"></i>
|
||||||
|
<h3>Virtualisation</h3>
|
||||||
|
<p>VirtualBox, VMware, Docker, bientôt Proxmox.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-6 col-md-4 col-lg-3" data-aos="fade-up" data-aos-delay="300">
|
||||||
|
<div class="skill-item">
|
||||||
|
<i class="bi bi-cloud"></i>
|
||||||
|
<h3>Cloud AWS</h3>
|
||||||
|
<p>EC2, S3, VPC, RDS - Certification AWS CPE.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- End Skills Grid -->
|
||||||
|
|
||||||
|
<!-- Journey Timeline -->
|
||||||
|
<div class="mb-5">
|
||||||
|
<div class="row g-4">
|
||||||
|
<div class="col-md-6 col-lg-3" data-aos="fade-up" data-aos-delay="120">
|
||||||
|
<article class="timeline-item">
|
||||||
|
<span class="dot"></span>
|
||||||
|
<time>2025</time>
|
||||||
|
<h4>BTS SIO - 1ère année</h4>
|
||||||
|
<p>Lycée Paul-Louis Courier, Tours</p>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 col-lg-3" data-aos="fade-up" data-aos-delay="140">
|
||||||
|
<article class="timeline-item">
|
||||||
|
<span class="dot"></span>
|
||||||
|
<time>2025</time>
|
||||||
|
<h4>Certification Cisco</h4>
|
||||||
|
<p>Notions de bases des réseaux informatiques</p>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 col-lg-3" data-aos="fade-up" data-aos-delay="180">
|
||||||
|
<article class="timeline-item">
|
||||||
|
<span class="dot"></span>
|
||||||
|
<time>2025</time>
|
||||||
|
<h4>Certification AWS CPE</h4>
|
||||||
|
<p>Cloud Practitioner Essentials - Fondamentaux AWS</p>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 col-lg-3" data-aos="fade-up" data-aos-delay="240">
|
||||||
|
<article class="timeline-item">
|
||||||
|
<span class="dot"></span>
|
||||||
|
<time>2023</time>
|
||||||
|
<h4>Certification ANSSI</h4>
|
||||||
|
<p>MOOC Cybersécurité - Fondamentaux de la sécurité</p>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- End Journey Timeline -->
|
||||||
|
|
||||||
|
<!-- Quote -->
|
||||||
|
<blockquote class="personal-quote text-center mb-5" data-aos="fade-down" data-aos-delay="200">
|
||||||
|
<p>"Soyez vous-même, les autres sont déjà pris" - Oscar Wilde</p>
|
||||||
|
</blockquote>
|
||||||
|
<!-- End Quote -->
|
||||||
|
|
||||||
|
<!-- Fun Facts -->
|
||||||
|
<div class="row g-3 justify-content-center">
|
||||||
|
<div class="col-6 col-md-3 col-lg-2" data-aos="zoom-in" data-aos-delay="120">
|
||||||
|
<div class="fact-pill">
|
||||||
|
<i class="bi bi-laptop"></i>
|
||||||
|
<span>Technologies</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-6 col-md-3 col-lg-2" data-aos="zoom-in" data-aos-delay="160">
|
||||||
|
<div class="fact-pill">
|
||||||
|
<i class="bi bi-lightbulb"></i>
|
||||||
|
<span>Innovation</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-6 col-md-3 col-lg-2" data-aos="zoom-in" data-aos-delay="200">
|
||||||
|
<div class="fact-pill">
|
||||||
|
<i class="bi bi-trophy"></i>
|
||||||
|
<span>Ambition</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-6 col-md-3 col-lg-2" data-aos="zoom-in" data-aos-delay="240">
|
||||||
|
<div class="fact-pill">
|
||||||
|
<i class="bi bi-emoji-smile"></i>
|
||||||
|
<span>Positivité</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- End Fun Facts -->
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section><!-- /About Section -->
|
||||||
|
|
||||||
|
<!-- Skills Section -->
|
||||||
|
<section id="skills" class="skills section">
|
||||||
|
|
||||||
|
<!-- Section Title -->
|
||||||
|
<div class="container section-title" data-aos="fade-up">
|
||||||
|
<h2>Compétences Techniques</h2>
|
||||||
|
<p>Mes compétences acquises au cours de ma formation en BTS Services Informatiques aux Organisations et
|
||||||
|
de mes projets personnels</p>
|
||||||
|
</div><!-- End Section Title -->
|
||||||
|
|
||||||
|
<div class="container" data-aos="fade-up" data-aos-delay="100">
|
||||||
|
|
||||||
|
<div class="row g-4 skills-animation">
|
||||||
|
|
||||||
|
<div class="col-md-6 col-lg-3" data-aos="fade-up" data-aos-delay="100">
|
||||||
|
<div class="skill-box">
|
||||||
|
<h3>Linux</h3>
|
||||||
|
<p>Administration système, lignes de commande et scripting</p>
|
||||||
|
<span class="text-end d-block">85%</span>
|
||||||
|
<div class="progress">
|
||||||
|
<div class="progress-bar" role="progressbar" aria-valuenow="85" aria-valuemin="0"
|
||||||
|
aria-valuemax="100" aria-label="Linux: 85%"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6 col-lg-3" data-aos="fade-up" data-aos-delay="200">
|
||||||
|
<div class="skill-box">
|
||||||
|
<h3>Réseaux Cisco</h3>
|
||||||
|
<p>Configuration routeurs, switchs et protocoles réseau</p>
|
||||||
|
<span class="text-end d-block">80%</span>
|
||||||
|
<div class="progress">
|
||||||
|
<div class="progress-bar" role="progressbar" aria-valuenow="80" aria-valuemin="0"
|
||||||
|
aria-valuemax="100" aria-label="Réseaux Cisco: 80%"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6 col-lg-3" data-aos="fade-up" data-aos-delay="300">
|
||||||
|
<div class="skill-box">
|
||||||
|
<h3>Docker / Containers</h3>
|
||||||
|
<p>Conteneurisation et orchestration d'applications</p>
|
||||||
|
<span class="text-end d-block">70%</span>
|
||||||
|
<div class="progress">
|
||||||
|
<div class="progress-bar" role="progressbar" aria-valuenow="70" aria-valuemin="0"
|
||||||
|
aria-valuemax="100" aria-label="Docker / Containers: 70%"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6 col-lg-3" data-aos="fade-up" data-aos-delay="400">
|
||||||
|
<div class="skill-box">
|
||||||
|
<h3>Cloud AWS</h3>
|
||||||
|
<p>EC2, S3, VPC, RDS - Infrastructure cloud</p>
|
||||||
|
<span class="text-end d-block">55%</span>
|
||||||
|
<div class="progress">
|
||||||
|
<div class="progress-bar" role="progressbar" aria-valuenow="55" aria-valuemin="0"
|
||||||
|
aria-valuemax="100" aria-label="Cloud AWS: 55%">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section><!-- /Skills Section -->
|
||||||
|
|
||||||
|
<!-- Stats Section -->
|
||||||
|
<section id="stats" class="stats section dark-background">
|
||||||
|
|
||||||
|
<div class="container" data-aos="fade-up" data-aos-delay="100">
|
||||||
|
|
||||||
|
<div class="row align-items-center">
|
||||||
|
|
||||||
|
|
||||||
|
<div class="col-lg-8">
|
||||||
|
<div class="row counters">
|
||||||
|
<div class="col-md-4" data-aos="fade-up" data-aos-delay="300">
|
||||||
|
<h2><span data-purecounter-start="0" data-purecounter-end="8"
|
||||||
|
data-purecounter-duration="1" class="purecounter"></span>+</h2>
|
||||||
|
<p>Projets réalisés</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-4" data-aos="fade-up" data-aos-delay="400">
|
||||||
|
<h2><span data-purecounter-start="0" data-purecounter-end="5"
|
||||||
|
data-purecounter-duration="1" class="purecounter"></span></h2>
|
||||||
|
<p>Certifications</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-4" data-aos="fade-up" data-aos-delay="500">
|
||||||
|
<h2><span data-purecounter-start="0" data-purecounter-end="100"
|
||||||
|
data-purecounter-duration="1" class="purecounter"></span>%</h2>
|
||||||
|
<p>Motivation</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section><!-- /Stats Section -->
|
||||||
|
|
||||||
|
<!-- <!– Testimonials Section –>-->
|
||||||
|
<!-- <section id="testimonials" class="testimonials section">-->
|
||||||
|
|
||||||
|
<!-- <!– Section Title –>-->
|
||||||
|
<!-- <div class="container section-title" data-aos="fade-up">-->
|
||||||
|
<!-- <h2>Témoignages</h2>-->
|
||||||
|
<!-- <p>Les retours de mes collaborateurs et formateurs</p>-->
|
||||||
|
<!-- </div><!– End Section Title –>-->
|
||||||
|
|
||||||
|
<!-- <div class="container" data-aos="fade-up" data-aos-delay="100">-->
|
||||||
|
|
||||||
|
<!-- <div class="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">-->
|
||||||
|
|
||||||
|
<!-- <div class="swiper-slide">-->
|
||||||
|
<!-- <div class="testimonial-item">-->
|
||||||
|
<!-- <div class="row gy-4 justify-content-center">-->
|
||||||
|
<!-- <div class="col-lg-6">-->
|
||||||
|
<!-- <div class="testimonial-content">-->
|
||||||
|
<!-- <p>-->
|
||||||
|
<!-- <i class="bi bi-quote quote-icon-left"></i>-->
|
||||||
|
<!-- <span>Ewen fait preuve d'une grande autonomie et d'un excellent esprit d'équipe. Son sérieux et sa motivation sont remarquables.</span>-->
|
||||||
|
<!-- <i class="bi bi-quote quote-icon-right"></i>-->
|
||||||
|
<!-- </p>-->
|
||||||
|
<!-- <h3>Jean Dupont</h3>-->
|
||||||
|
<!-- <h4>Formateur BTS SIO</h4>-->
|
||||||
|
<!-- <div class="stars">-->
|
||||||
|
<!-- <i class="bi bi-star-fill"></i><i class="bi bi-star-fill"></i><i class="bi bi-star-fill"></i><i class="bi bi-star-fill"></i><i class="bi bi-star-fill"></i>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- <div class="col-lg-2 text-center">-->
|
||||||
|
<!-- <img src="assets/img/person/person-m-9.webp" class="img-fluid testimonial-img" alt="">-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </div><!– End testimonial item –>-->
|
||||||
|
|
||||||
|
<!-- <div class="swiper-slide">-->
|
||||||
|
<!-- <div class="testimonial-item">-->
|
||||||
|
<!-- <div class="row gy-4 justify-content-center">-->
|
||||||
|
<!-- <div class="col-lg-6">-->
|
||||||
|
<!-- <div class="testimonial-content">-->
|
||||||
|
<!-- <p>-->
|
||||||
|
<!-- <i class="bi bi-quote quote-icon-left"></i>-->
|
||||||
|
<!-- <span>Un étudiant passionné qui n'hésite pas à approfondir ses connaissances par lui-même. Son intérêt pour les technologies est authentique.</span>-->
|
||||||
|
<!-- <i class="bi bi-quote quote-icon-right"></i>-->
|
||||||
|
<!-- </p>-->
|
||||||
|
<!-- <h3>Marie Lambert</h3>-->
|
||||||
|
<!-- <h4>Responsable Pédagogique</h4>-->
|
||||||
|
<!-- <div class="stars">-->
|
||||||
|
<!-- <i class="bi bi-star-fill"></i><i class="bi bi-star-fill"></i><i class="bi bi-star-fill"></i><i class="bi bi-star-fill"></i><i class="bi bi-star-fill"></i>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- <div class="col-lg-2 text-center">-->
|
||||||
|
<!-- <img src="assets/img/person/person-f-5.webp" class="img-fluid testimonial-img" alt="">-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </div><!– End testimonial item –>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- <div class="swiper-pagination"></div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
|
||||||
|
<!-- </div>-->
|
||||||
|
|
||||||
|
<!-- </section><!– /Testimonials Section –>-->
|
||||||
|
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer id="footer" class="footer">
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="copyright text-center ">
|
||||||
|
<p>© <span>Copyright</span> <strong class="px-1 sitename">GADONNAUD Ewen</strong> <span>Tout droits
|
||||||
|
réservés<br></span></p>
|
||||||
|
</div>
|
||||||
|
<div class="social-links d-flex justify-content-center">
|
||||||
|
<a href="https://www.linkedin.com/in/ewen-gadonnaud-b17491283" aria-label="LinkedIn"><i
|
||||||
|
class="bi bi-linkedin"></i></a>
|
||||||
|
<a href="https://github.com/Azuneer" aria-label="GitHub"><i class="bi bi-github"></i></a>
|
||||||
|
</div>
|
||||||
|
<div class="credits">
|
||||||
|
<!-- All the links in the footer should remain intact. -->
|
||||||
|
<!-- You can delete the links only if you've purchased the pro version. -->
|
||||||
|
<!-- Licensing information: https://bootstrapmade.com/license/ -->
|
||||||
|
<!-- Purchase the pro version with working PHP/AJAX contact form: [buy-url] -->
|
||||||
|
Designé avec <a href="https://bootstrapmade.com/">BootstrapMade</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<!-- Scroll Top -->
|
||||||
|
<a href="#" id="scroll-top" class="scroll-top d-flex align-items-center justify-content-center"><i
|
||||||
|
class="bi bi-arrow-up-short"></i></a>
|
||||||
|
|
||||||
|
<!-- Preloader -->
|
||||||
|
<div id="preloader"></div>
|
||||||
|
|
||||||
|
<!-- Vendor JS Files -->
|
||||||
|
<script src="assets/vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<script src="assets/vendor/php-email-form/validate.js"></script>
|
||||||
|
<script src="assets/vendor/aos/aos.js"></script>
|
||||||
|
<script src="assets/vendor/typed.js/typed.umd.js"></script>
|
||||||
|
<script src="assets/vendor/waypoints/noframework.waypoints.js"></script>
|
||||||
|
<script src="assets/vendor/purecounter/purecounter_vanilla.js"></script>
|
||||||
|
<script src="assets/vendor/swiper/swiper-bundle.min.js"></script>
|
||||||
|
<script src="assets/vendor/imagesloaded/imagesloaded.pkgd.min.js"></script>
|
||||||
|
<script src="assets/vendor/isotope-layout/isotope.pkgd.min.js"></script>
|
||||||
|
<script src="assets/vendor/glightbox/js/glightbox.min.js"></script>
|
||||||
|
|
||||||
|
<!-- Main JS File -->
|
||||||
|
<script src="assets/js/main.js"></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
BIN
assets/.DS_Store
vendored
Normal file
3850
assets/css/main.css
Normal file
BIN
assets/img/.DS_Store
vendored
Normal file
BIN
assets/img/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
BIN
assets/img/favicon.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/img/favicons.ico/android-icon-144x144.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
assets/img/favicons.ico/android-icon-192x192.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
assets/img/favicons.ico/android-icon-36x36.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
assets/img/favicons.ico/android-icon-48x48.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
assets/img/favicons.ico/android-icon-72x72.png
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
assets/img/favicons.ico/android-icon-96x96.png
Normal file
|
After Width: | Height: | Size: 8.5 KiB |
BIN
assets/img/favicons.ico/apple-icon-114x114.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
assets/img/favicons.ico/apple-icon-120x120.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
assets/img/favicons.ico/apple-icon-144x144.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
assets/img/favicons.ico/apple-icon-152x152.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
assets/img/favicons.ico/apple-icon-180x180.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
assets/img/favicons.ico/apple-icon-57x57.png
Normal file
|
After Width: | Height: | Size: 4 KiB |
BIN
assets/img/favicons.ico/apple-icon-60x60.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
assets/img/favicons.ico/apple-icon-72x72.png
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
assets/img/favicons.ico/apple-icon-76x76.png
Normal file
|
After Width: | Height: | Size: 6 KiB |
BIN
assets/img/favicons.ico/apple-icon-precomposed.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
assets/img/favicons.ico/apple-icon.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
2
assets/img/favicons.ico/browserconfig.xml
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<browserconfig><msapplication><tile><square70x70logo src="/ms-icon-70x70.png"/><square150x150logo src="/ms-icon-150x150.png"/><square310x310logo src="/ms-icon-310x310.png"/><TileColor>#ffffff</TileColor></tile></msapplication></browserconfig>
|
||||||
BIN
assets/img/favicons.ico/favicon-16x16.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
assets/img/favicons.ico/favicon-32x32.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
assets/img/favicons.ico/favicon-96x96.png
Normal file
|
After Width: | Height: | Size: 8.5 KiB |
BIN
assets/img/favicons.ico/favicon.ico
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
41
assets/img/favicons.ico/manifest.json
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
{
|
||||||
|
"name": "App",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "\/android-icon-36x36.png",
|
||||||
|
"sizes": "36x36",
|
||||||
|
"type": "image\/png",
|
||||||
|
"density": "0.75"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "\/android-icon-48x48.png",
|
||||||
|
"sizes": "48x48",
|
||||||
|
"type": "image\/png",
|
||||||
|
"density": "1.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "\/android-icon-72x72.png",
|
||||||
|
"sizes": "72x72",
|
||||||
|
"type": "image\/png",
|
||||||
|
"density": "1.5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "\/android-icon-96x96.png",
|
||||||
|
"sizes": "96x96",
|
||||||
|
"type": "image\/png",
|
||||||
|
"density": "2.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "\/android-icon-144x144.png",
|
||||||
|
"sizes": "144x144",
|
||||||
|
"type": "image\/png",
|
||||||
|
"density": "3.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "\/android-icon-192x192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image\/png",
|
||||||
|
"density": "4.0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
assets/img/favicons.ico/ms-icon-144x144.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
assets/img/favicons.ico/ms-icon-150x150.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
assets/img/favicons.ico/ms-icon-310x310.png
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
assets/img/favicons.ico/ms-icon-70x70.png
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
BIN
assets/img/logo.webp
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
assets/img/person/person-f-12.webp
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
assets/img/person/person-f-5.webp
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
assets/img/person/person-m-12.webp
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
assets/img/person/person-m-2.webp
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
assets/img/person/person-m-3.webp
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
assets/img/person/person-m-5.webp
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
assets/img/person/person-m-8.webp
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
assets/img/person/person-m-9.webp
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
assets/img/portfolio/AWSCPE.pdf
Normal file
BIN
assets/img/portfolio/AttestationEBIOS.pdf
Normal file
BIN
assets/img/portfolio/AttestationMOOC.pdf
Normal file
BIN
assets/img/portfolio/CertificatPIX.pdf
Normal file
BIN
assets/img/portfolio/VignesDuVal.png
Normal file
|
After Width: | Height: | Size: 79 KiB |
BIN
assets/img/portfolio/graph.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
assets/img/portfolio/laboAWS.pdf
Normal file
BIN
assets/img/portfolio/lycee.jpg
Normal file
|
After Width: | Height: | Size: 849 KiB |
BIN
assets/img/portfolio/portfolio-1.webp
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
assets/img/portfolio/portfolio-11.webp
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
assets/img/portfolio/portfolio-12.webp
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
assets/img/portfolio/portfolio-2.webp
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
assets/img/portfolio/portfolio-3.webp
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
assets/img/portfolio/portfolio-4.webp
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
assets/img/portfolio/portfolio-5.webp
Normal file
|
After Width: | Height: | Size: 9.1 KiB |
BIN
assets/img/portfolio/portfolio-6.webp
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
assets/img/portfolio/portfolio-7.webp
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
assets/img/portfolio/portfolio-8.webp
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
assets/img/portfolio/portfolio-blastpass.webp
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
assets/img/portfolio/portfolio-cinema.webp
Normal file
|
After Width: | Height: | Size: 175 KiB |
BIN
assets/img/portfolio/portfolio-concours-photo.webp
Normal file
|
After Width: | Height: | Size: 77 KiB |
BIN
assets/img/portfolio/portfolio-ewengadonnaud.webp
Normal file
|
After Width: | Height: | Size: 77 KiB |
BIN
assets/img/portfolio/portfolio-proc-linux.webp
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
assets/img/portfolio/portfolio-reseau-gns3.webp
Normal file
|
After Width: | Height: | Size: 108 KiB |
BIN
assets/img/portfolio/portfolio-serveur.webp
Normal file
|
After Width: | Height: | Size: 327 KiB |
BIN
assets/img/portfolio/serveur.png
Normal file
|
After Width: | Height: | Size: 610 KiB |
BIN
assets/img/profile/.DS_Store
vendored
Normal file
BIN
assets/img/profile/CV_GADONNAUD_EWEN_2025.pdf
Normal file
BIN
assets/img/profile/me.jpg
Normal file
|
After Width: | Height: | Size: 2.8 MiB |
BIN
assets/img/profile/me.webp
Normal file
|
After Width: | Height: | Size: 240 KiB |
BIN
assets/img/profile/profile-square-11.webp
Normal file
|
After Width: | Height: | Size: 376 KiB |
BIN
assets/img/services/services-7.webp
Normal file
|
After Width: | Height: | Size: 28 KiB |
785
assets/js/main.js
Normal file
|
|
@ -0,0 +1,785 @@
|
||||||
|
/**
|
||||||
|
* Template Name: FolioOne
|
||||||
|
* Template URL: https://bootstrapmade.com/folioone-bootstrap-portfolio-website-template/
|
||||||
|
* Updated: Aug 23 2025 with Bootstrap v5.3.7
|
||||||
|
* Author: BootstrapMade.com
|
||||||
|
* License: https://bootstrapmade.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply .scrolled class to the body as the page is scrolled down
|
||||||
|
*/
|
||||||
|
function toggleScrolled() {
|
||||||
|
const selectBody = document.querySelector('body');
|
||||||
|
const selectHeader = document.querySelector('#header');
|
||||||
|
if (!selectHeader.classList.contains('scroll-up-sticky') && !selectHeader.classList.contains('sticky-top') && !selectHeader.classList.contains('fixed-top')) return;
|
||||||
|
window.scrollY > 100 ? selectBody.classList.add('scrolled') : selectBody.classList.remove('scrolled');
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('scroll', toggleScrolled);
|
||||||
|
window.addEventListener('load', toggleScrolled);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mobile nav toggle
|
||||||
|
*/
|
||||||
|
const mobileNavToggleBtn = document.querySelector('.mobile-nav-toggle');
|
||||||
|
|
||||||
|
function mobileNavToogle() {
|
||||||
|
document.querySelector('body').classList.toggle('mobile-nav-active');
|
||||||
|
mobileNavToggleBtn.classList.toggle('bi-list');
|
||||||
|
mobileNavToggleBtn.classList.toggle('bi-x');
|
||||||
|
}
|
||||||
|
if (mobileNavToggleBtn) {
|
||||||
|
mobileNavToggleBtn.addEventListener('click', mobileNavToogle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide mobile nav on same-page/hash links
|
||||||
|
*/
|
||||||
|
document.querySelectorAll('#navmenu a').forEach(navmenu => {
|
||||||
|
navmenu.addEventListener('click', () => {
|
||||||
|
if (document.querySelector('.mobile-nav-active')) {
|
||||||
|
mobileNavToogle();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle mobile nav dropdowns
|
||||||
|
*/
|
||||||
|
document.querySelectorAll('.navmenu .toggle-dropdown').forEach(navmenu => {
|
||||||
|
navmenu.addEventListener('click', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.parentNode.classList.toggle('active');
|
||||||
|
this.parentNode.nextElementSibling.classList.toggle('dropdown-active');
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preloader
|
||||||
|
*/
|
||||||
|
const preloader = document.querySelector('#preloader');
|
||||||
|
if (preloader) {
|
||||||
|
window.addEventListener('load', () => {
|
||||||
|
preloader.remove();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scroll top button
|
||||||
|
*/
|
||||||
|
let scrollTop = document.querySelector('.scroll-top');
|
||||||
|
|
||||||
|
function toggleScrollTop() {
|
||||||
|
if (scrollTop) {
|
||||||
|
window.scrollY > 100 ? scrollTop.classList.add('active') : scrollTop.classList.remove('active');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scrollTop.addEventListener('click', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
window.scrollTo({
|
||||||
|
top: 0,
|
||||||
|
behavior: 'smooth'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener('load', toggleScrollTop);
|
||||||
|
document.addEventListener('scroll', toggleScrollTop);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init typed.js
|
||||||
|
*/
|
||||||
|
const selectTyped = document.querySelector('.typed');
|
||||||
|
if (selectTyped && typeof Typed !== 'undefined') {
|
||||||
|
let typed_strings = selectTyped.getAttribute('data-typed-items');
|
||||||
|
typed_strings = typed_strings.split(',');
|
||||||
|
new Typed('.typed', {
|
||||||
|
strings: typed_strings,
|
||||||
|
loop: true,
|
||||||
|
typeSpeed: 100,
|
||||||
|
backSpeed: 50,
|
||||||
|
backDelay: 2000
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Animate the skills items on reveal
|
||||||
|
*/
|
||||||
|
let skillsAnimation = document.querySelectorAll('.skills-animation');
|
||||||
|
if (skillsAnimation.length > 0 && typeof Waypoint !== 'undefined') {
|
||||||
|
skillsAnimation.forEach((item) => {
|
||||||
|
new Waypoint({
|
||||||
|
element: item,
|
||||||
|
offset: '80%',
|
||||||
|
handler: function (direction) {
|
||||||
|
let progress = item.querySelectorAll('.progress .progress-bar');
|
||||||
|
progress.forEach(el => {
|
||||||
|
el.style.width = el.getAttribute('aria-valuenow') + '%';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiate Pure Counter
|
||||||
|
*/
|
||||||
|
if (typeof PureCounter !== 'undefined') {
|
||||||
|
new PureCounter();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init swiper sliders
|
||||||
|
*/
|
||||||
|
function initSwiper() {
|
||||||
|
if (typeof Swiper !== 'undefined') {
|
||||||
|
document.querySelectorAll(".init-swiper").forEach(function (swiperElement) {
|
||||||
|
let config = JSON.parse(
|
||||||
|
swiperElement.querySelector(".swiper-config").innerHTML.trim()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (swiperElement.classList.contains("swiper-tab")) {
|
||||||
|
initSwiperWithCustomPagination(swiperElement, config);
|
||||||
|
} else {
|
||||||
|
new Swiper(swiperElement, config);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("load", initSwiper);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init isotope layout and filters
|
||||||
|
*/
|
||||||
|
if (typeof Isotope !== 'undefined' && typeof imagesLoaded !== 'undefined') {
|
||||||
|
document.querySelectorAll('.isotope-layout').forEach(function (isotopeItem) {
|
||||||
|
let layout = isotopeItem.getAttribute('data-layout') ?? 'masonry';
|
||||||
|
let filter = isotopeItem.getAttribute('data-default-filter') ?? '*';
|
||||||
|
let sort = isotopeItem.getAttribute('data-sort') ?? 'original-order';
|
||||||
|
|
||||||
|
let initIsotope;
|
||||||
|
imagesLoaded(isotopeItem.querySelector('.isotope-container'), function () {
|
||||||
|
initIsotope = new Isotope(isotopeItem.querySelector('.isotope-container'), {
|
||||||
|
itemSelector: '.isotope-item',
|
||||||
|
layoutMode: layout,
|
||||||
|
filter: filter,
|
||||||
|
sortBy: sort
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
isotopeItem.querySelectorAll('.isotope-filters li').forEach(function (filters) {
|
||||||
|
filters.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 aosInit === 'function') {
|
||||||
|
aosInit();
|
||||||
|
}
|
||||||
|
}, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiate glightbox
|
||||||
|
*/
|
||||||
|
if (typeof GLightbox !== 'undefined') {
|
||||||
|
const glightbox = GLightbox({
|
||||||
|
selector: '.glightbox'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UX Enhancements - Custom Cursor (Optimized)
|
||||||
|
*/
|
||||||
|
(function () {
|
||||||
|
// Only enable on non-touch devices and desktops (not mobile)
|
||||||
|
if (window.matchMedia("(hover: hover) and (pointer: fine)").matches && window.innerWidth >= 1024) {
|
||||||
|
window.addEventListener('load', function () {
|
||||||
|
const cursor = document.createElement('div');
|
||||||
|
cursor.className = 'custom-cursor';
|
||||||
|
const cursorFollower = document.createElement('div');
|
||||||
|
cursorFollower.className = 'custom-cursor-follower';
|
||||||
|
|
||||||
|
document.body.appendChild(cursor);
|
||||||
|
document.body.appendChild(cursorFollower);
|
||||||
|
|
||||||
|
let mouseX = 0, mouseY = 0;
|
||||||
|
let followerX = 0, followerY = 0;
|
||||||
|
|
||||||
|
// Use transform for better performance
|
||||||
|
document.addEventListener('mousemove', (e) => {
|
||||||
|
mouseX = e.clientX;
|
||||||
|
mouseY = e.clientY;
|
||||||
|
cursor.style.transform = `translate(${mouseX}px, ${mouseY}px)`;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Smooth follower animation with requestAnimationFrame
|
||||||
|
function animateFollower() {
|
||||||
|
const distX = mouseX - followerX;
|
||||||
|
const distY = mouseY - followerY;
|
||||||
|
|
||||||
|
followerX += distX * 0.15; // Augmenté de 0.1 à 0.15 pour plus de réactivité
|
||||||
|
followerY += distY * 0.15;
|
||||||
|
|
||||||
|
cursorFollower.style.transform = `translate(${followerX}px, ${followerY}px)`;
|
||||||
|
|
||||||
|
requestAnimationFrame(animateFollower);
|
||||||
|
}
|
||||||
|
animateFollower();
|
||||||
|
|
||||||
|
// Add hover effects for interactive elements (avec délégation d'événements)
|
||||||
|
document.addEventListener('mouseenter', (e) => {
|
||||||
|
if (e.target.matches('a, button, .btn, input, textarea, select')) {
|
||||||
|
cursor.classList.add('hover');
|
||||||
|
cursorFollower.classList.add('hover');
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
document.addEventListener('mouseleave', (e) => {
|
||||||
|
if (e.target.matches('a, button, .btn, input, textarea, select')) {
|
||||||
|
cursor.classList.remove('hover');
|
||||||
|
cursorFollower.classList.remove('hover');
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UX Enhancements - Scroll Progress Indicator (Optimized with RAF)
|
||||||
|
*/
|
||||||
|
(function () {
|
||||||
|
const progressBar = document.createElement('div');
|
||||||
|
progressBar.className = 'scroll-progress';
|
||||||
|
document.body.appendChild(progressBar);
|
||||||
|
|
||||||
|
let ticking = false;
|
||||||
|
|
||||||
|
function updateScrollProgress() {
|
||||||
|
const windowHeight = window.innerHeight;
|
||||||
|
const documentHeight = document.documentElement.scrollHeight;
|
||||||
|
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
||||||
|
const scrollPercent = Math.min((scrollTop / (documentHeight - windowHeight)) * 100, 100);
|
||||||
|
|
||||||
|
progressBar.style.width = scrollPercent + '%';
|
||||||
|
ticking = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('scroll', function () {
|
||||||
|
if (!ticking) {
|
||||||
|
window.requestAnimationFrame(updateScrollProgress);
|
||||||
|
ticking = true;
|
||||||
|
}
|
||||||
|
}, { passive: true });
|
||||||
|
|
||||||
|
window.addEventListener('load', updateScrollProgress);
|
||||||
|
})();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UX Enhancements - Text Reveal on Scroll (Optimized with RAF)
|
||||||
|
*/
|
||||||
|
(function () {
|
||||||
|
window.addEventListener('load', function () {
|
||||||
|
const revealElements = document.querySelectorAll('.reveal-text');
|
||||||
|
|
||||||
|
if (revealElements.length === 0) return;
|
||||||
|
|
||||||
|
let ticking = false;
|
||||||
|
|
||||||
|
function checkReveal() {
|
||||||
|
revealElements.forEach(el => {
|
||||||
|
if (!el.classList.contains('revealed')) {
|
||||||
|
const elementTop = el.getBoundingClientRect().top;
|
||||||
|
const elementVisible = 150;
|
||||||
|
|
||||||
|
if (elementTop < window.innerHeight - elementVisible) {
|
||||||
|
el.classList.add('revealed');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ticking = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('scroll', function () {
|
||||||
|
if (!ticking) {
|
||||||
|
window.requestAnimationFrame(checkReveal);
|
||||||
|
ticking = true;
|
||||||
|
}
|
||||||
|
}, { passive: true });
|
||||||
|
|
||||||
|
checkReveal(); // Initial check
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UX Enhancements - Smooth Parallax Effect (Optimized with RAF)
|
||||||
|
*/
|
||||||
|
(function () {
|
||||||
|
window.addEventListener('load', function () {
|
||||||
|
const parallaxElements = document.querySelectorAll('.parallax-bg');
|
||||||
|
|
||||||
|
if (parallaxElements.length === 0) return;
|
||||||
|
|
||||||
|
let ticking = false;
|
||||||
|
|
||||||
|
function updateParallax() {
|
||||||
|
const scrolled = window.pageYOffset;
|
||||||
|
parallaxElements.forEach(el => {
|
||||||
|
const rate = scrolled * 0.3;
|
||||||
|
el.style.transform = `translate3d(0, ${rate}px, 0)`;
|
||||||
|
});
|
||||||
|
ticking = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('scroll', function () {
|
||||||
|
if (!ticking) {
|
||||||
|
window.requestAnimationFrame(updateParallax);
|
||||||
|
ticking = true;
|
||||||
|
}
|
||||||
|
}, { passive: true });
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UX Enhancements - Enhanced Card Tilt Effect (Subtle 3D) - Optimized
|
||||||
|
*/
|
||||||
|
(function () {
|
||||||
|
window.addEventListener('load', function () {
|
||||||
|
const tiltElements = document.querySelectorAll('.service-item, .portfolio-item');
|
||||||
|
|
||||||
|
if (tiltElements.length === 0) return; // Exit if no elements
|
||||||
|
|
||||||
|
tiltElements.forEach(el => {
|
||||||
|
let ticking = false;
|
||||||
|
|
||||||
|
el.addEventListener('mousemove', (e) => {
|
||||||
|
if (!ticking) {
|
||||||
|
window.requestAnimationFrame(() => {
|
||||||
|
const rect = el.getBoundingClientRect();
|
||||||
|
const x = e.clientX - rect.left;
|
||||||
|
const y = e.clientY - rect.top;
|
||||||
|
|
||||||
|
const centerX = rect.width / 2;
|
||||||
|
const centerY = rect.height / 2;
|
||||||
|
|
||||||
|
const rotateX = ((y - centerY) / centerY) * 2; // Réduit à 2 pour plus de subtilité
|
||||||
|
const rotateY = ((centerX - x) / centerX) * 2;
|
||||||
|
|
||||||
|
// Préserve le translateY existant et ajoute la rotation 3D
|
||||||
|
const baseTranslateY = el.classList.contains('service-item') ? -5 : -10;
|
||||||
|
el.style.transform = `perspective(1000px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) translateY(${baseTranslateY}px)`;
|
||||||
|
|
||||||
|
ticking = false;
|
||||||
|
});
|
||||||
|
ticking = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
el.addEventListener('mouseleave', () => {
|
||||||
|
// Remet la transformation de base au survol (définie dans le CSS)
|
||||||
|
el.style.transform = '';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UX Enhancements - Improved AOS Configuration (Optimized for Performance)
|
||||||
|
*/
|
||||||
|
function aosInit() {
|
||||||
|
if (typeof AOS !== 'undefined') {
|
||||||
|
AOS.init({
|
||||||
|
duration: 400, // Encore plus rapide pour éviter le "lag" visuel
|
||||||
|
easing: 'ease-out-cubic', // Easing plus dynamique
|
||||||
|
once: true,
|
||||||
|
mirror: false,
|
||||||
|
offset: 50, // Déclenchement très tôt
|
||||||
|
delay: 0,
|
||||||
|
anchorPlacement: 'top-bottom',
|
||||||
|
disable: function () {
|
||||||
|
// Désactiver sur mobile pour meilleures performances
|
||||||
|
return window.innerWidth < 768;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.addEventListener('load', aosInit);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UX Enhancements - Smooth Scroll Enhancement (Optimized)
|
||||||
|
*/
|
||||||
|
window.addEventListener('load', function () {
|
||||||
|
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
||||||
|
anchor.addEventListener('click', function (e) {
|
||||||
|
const href = this.getAttribute('href');
|
||||||
|
if (href !== '#' && href !== '#scroll-top') {
|
||||||
|
const target = document.querySelector(href);
|
||||||
|
if (target) {
|
||||||
|
e.preventDefault();
|
||||||
|
target.scrollIntoView({
|
||||||
|
behavior: 'smooth',
|
||||||
|
block: 'start'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UX Enhancements - Add Loading States to Buttons (Optimized)
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* UX Enhancements - Add Loading States to Buttons (Optimized)
|
||||||
|
* REMOVED: Interferes with Formspree submission
|
||||||
|
*/
|
||||||
|
// window.addEventListener('load', function () {
|
||||||
|
// const submitButtons = document.querySelectorAll('button[type="submit"], .btn[type="submit"]');
|
||||||
|
//
|
||||||
|
// submitButtons.forEach(btn => {
|
||||||
|
// btn.addEventListener('click', function (e) {
|
||||||
|
// if (this.form && this.form.checkValidity()) {
|
||||||
|
// const originalText = this.innerHTML;
|
||||||
|
// this.innerHTML = '<i class="bi bi-hourglass-split"></i> Envoi en cours...';
|
||||||
|
// this.disabled = true;
|
||||||
|
//
|
||||||
|
// // Reset after 3 seconds (adjust based on your needs)
|
||||||
|
// setTimeout(() => {
|
||||||
|
// this.innerHTML = originalText;
|
||||||
|
// this.disabled = false;
|
||||||
|
// }, 3000);
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UX Enhancements - Ambient Light Effects with Parallax
|
||||||
|
*/
|
||||||
|
(function () {
|
||||||
|
// Only enable on desktop for performance
|
||||||
|
if (window.innerWidth >= 768) {
|
||||||
|
window.addEventListener('load', function () {
|
||||||
|
// Create ambient lights container
|
||||||
|
const ambientLights = document.createElement('div');
|
||||||
|
ambientLights.className = 'ambient-lights';
|
||||||
|
document.body.insertBefore(ambientLights, document.body.firstChild);
|
||||||
|
|
||||||
|
// Create light orbs
|
||||||
|
for (let i = 1; i <= 4; i++) {
|
||||||
|
const orb = document.createElement('div');
|
||||||
|
orb.className = `light-orb light-orb-${i}`;
|
||||||
|
ambientLights.appendChild(orb);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create light rays
|
||||||
|
for (let i = 1; i <= 3; i++) {
|
||||||
|
const ray = document.createElement('div');
|
||||||
|
ray.className = `light-ray light-ray-${i}`;
|
||||||
|
ambientLights.appendChild(ray);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create light particles (random positions)
|
||||||
|
for (let i = 0; i < 20; i++) {
|
||||||
|
const particle = document.createElement('div');
|
||||||
|
particle.className = 'light-particle';
|
||||||
|
particle.style.top = Math.random() * 100 + '%';
|
||||||
|
particle.style.left = Math.random() * 100 + '%';
|
||||||
|
particle.style.animationDelay = Math.random() * 3 + 's';
|
||||||
|
ambientLights.appendChild(particle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create gradient spotlights
|
||||||
|
for (let i = 1; i <= 2; i++) {
|
||||||
|
const spotlight = document.createElement('div');
|
||||||
|
spotlight.className = `gradient-spotlight gradient-spotlight-${i}`;
|
||||||
|
ambientLights.appendChild(spotlight);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parallax effect on scroll (optimized with RAF and throttling)
|
||||||
|
const orbs = document.querySelectorAll('.light-orb');
|
||||||
|
const rays = document.querySelectorAll('.light-ray');
|
||||||
|
const spotlights = document.querySelectorAll('.gradient-spotlight');
|
||||||
|
let ticking = false;
|
||||||
|
|
||||||
|
function updateLightParallax() {
|
||||||
|
const scrollY = window.pageYOffset;
|
||||||
|
|
||||||
|
// Orbs: slow parallax (different speeds)
|
||||||
|
orbs.forEach((orb, index) => {
|
||||||
|
const speed = 0.1 + (index * 0.05); // Different speed for each orb
|
||||||
|
const yPos = scrollY * speed;
|
||||||
|
orb.style.transform = `translateY(${yPos}px)`;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Rays: medium parallax
|
||||||
|
rays.forEach((ray, index) => {
|
||||||
|
const speed = 0.15 + (index * 0.03);
|
||||||
|
const yPos = scrollY * speed;
|
||||||
|
const rotation = 15 + (index * 5);
|
||||||
|
ray.style.transform = `translateY(${yPos}px) rotate(${rotation}deg)`;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Spotlights: faster parallax
|
||||||
|
spotlights.forEach((spotlight, index) => {
|
||||||
|
const speed = 0.25 + (index * 0.1);
|
||||||
|
const yPos = scrollY * speed;
|
||||||
|
if (index === 0) {
|
||||||
|
spotlight.style.transform = `translate(-50%, ${yPos}px)`;
|
||||||
|
} else {
|
||||||
|
spotlight.style.transform = `translateY(${yPos}px)`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ticking = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('scroll', function () {
|
||||||
|
if (!ticking) {
|
||||||
|
window.requestAnimationFrame(updateLightParallax);
|
||||||
|
ticking = true;
|
||||||
|
}
|
||||||
|
}, { passive: true });
|
||||||
|
|
||||||
|
// Initial position
|
||||||
|
updateLightParallax();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
/* Matrix-like background animation confined to header canvases (slower) */
|
||||||
|
(function () {
|
||||||
|
// Only run on larger screens to avoid perf issues on mobile
|
||||||
|
function shouldEnableMatrix() {
|
||||||
|
// Force-enable on Services page regardless of viewport width
|
||||||
|
if (document.body && document.body.classList.contains('services-page')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Otherwise keep default threshold to avoid perf issues on small screens
|
||||||
|
return window.innerWidth >= 900;
|
||||||
|
}
|
||||||
|
|
||||||
|
const canvases = Array.from(document.querySelectorAll('.matrix-bg-header'));
|
||||||
|
if (!canvases.length) return;
|
||||||
|
|
||||||
|
const instances = [];
|
||||||
|
|
||||||
|
const chars = '01<>/{}[]()\u25A0\u25A1'.split('');
|
||||||
|
|
||||||
|
function createInstance(canvas) {
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
let width = 0;
|
||||||
|
let height = 0;
|
||||||
|
let columns = [];
|
||||||
|
let animationId = null;
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
const dpr = Math.max(1, window.devicePixelRatio || 1);
|
||||||
|
const rect = canvas.parentElement.getBoundingClientRect();
|
||||||
|
width = canvas.width = Math.floor(rect.width * dpr);
|
||||||
|
// limit canvas height to header height
|
||||||
|
height = canvas.height = Math.floor(rect.height * dpr);
|
||||||
|
canvas.style.width = rect.width + 'px';
|
||||||
|
canvas.style.height = rect.height + 'px';
|
||||||
|
|
||||||
|
// slightly larger font for clarity, but scaled by DPR
|
||||||
|
const fontSize = Math.max(10, Math.floor((rect.width / 160) * dpr));
|
||||||
|
ctx.font = fontSize + 'px monospace';
|
||||||
|
ctx.textBaseline = 'top';
|
||||||
|
|
||||||
|
const columnCount = Math.ceil(width / fontSize);
|
||||||
|
columns = new Array(columnCount).fill(0).map(() => ({ y: Math.random() * -height }));
|
||||||
|
return fontSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
// A darker translucent fill to keep effect subtle on header
|
||||||
|
ctx.fillStyle = 'rgba(16,26,32,0.35)';
|
||||||
|
ctx.fillRect(0, 0, width, height);
|
||||||
|
|
||||||
|
const computed = getComputedStyle(document.documentElement).getPropertyValue('--matrix-pale-blue').trim() || '#8fcbe9';
|
||||||
|
ctx.fillStyle = computed;
|
||||||
|
|
||||||
|
// Slightly increase text opacity to make the falling characters a bit more visible
|
||||||
|
// Use save/restore so only the text rendering is affected (background fill stays same)
|
||||||
|
const textAlpha = 0.78; // small increase from default for better visibility
|
||||||
|
ctx.save();
|
||||||
|
ctx.globalAlpha = textAlpha;
|
||||||
|
|
||||||
|
const fontSizeMatch = ctx.font.match(/(\d+)px/);
|
||||||
|
const fontSize = fontSizeMatch ? parseInt(fontSizeMatch[1], 10) : 12;
|
||||||
|
|
||||||
|
// slower motion: smaller increments
|
||||||
|
for (let i = 0; i < columns.length; i += 1) {
|
||||||
|
const col = columns[i];
|
||||||
|
// render only rarely to greatly reduce density (slower effect)
|
||||||
|
if (Math.random() < 0.88) continue;
|
||||||
|
const text = chars[Math.floor(Math.random() * chars.length)];
|
||||||
|
const x = i * fontSize;
|
||||||
|
ctx.fillText(text, x, col.y);
|
||||||
|
// much slower falling speed and subtle randomness
|
||||||
|
col.y += (fontSize * 0.18) + Math.random() * (fontSize * 0.08);
|
||||||
|
if (col.y > height + Math.random() * 200) {
|
||||||
|
col.y = Math.random() * -height * 0.6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// restore alpha after drawing text so other canvas ops are unaffected
|
||||||
|
ctx.restore();
|
||||||
|
|
||||||
|
animationId = requestAnimationFrame(draw);
|
||||||
|
}
|
||||||
|
|
||||||
|
function start() {
|
||||||
|
cancelAnimationFrame(animationId);
|
||||||
|
init();
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
function stop() {
|
||||||
|
if (animationId) cancelAnimationFrame(animationId);
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { start, stop, init };
|
||||||
|
}
|
||||||
|
|
||||||
|
canvases.forEach(c => instances.push(createInstance(c)));
|
||||||
|
|
||||||
|
function startAll() {
|
||||||
|
if (!shouldEnableMatrix()) return;
|
||||||
|
console.debug('[matrix] startAll: starting matrix instances');
|
||||||
|
instances.forEach(i => i.start());
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopAll() {
|
||||||
|
console.debug('[matrix] stopAll: stopping matrix instances');
|
||||||
|
instances.forEach(i => i.stop());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pause on tab hidden
|
||||||
|
document.addEventListener('visibilitychange', function () {
|
||||||
|
if (document.hidden) stopAll(); else startAll();
|
||||||
|
});
|
||||||
|
|
||||||
|
let resizeTimer = null;
|
||||||
|
window.addEventListener('resize', function () {
|
||||||
|
clearTimeout(resizeTimer);
|
||||||
|
resizeTimer = setTimeout(function () {
|
||||||
|
if (shouldEnableMatrix()) startAll(); else stopAll();
|
||||||
|
}, 300);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener('load', function () {
|
||||||
|
if (shouldEnableMatrix()) startAll();
|
||||||
|
else console.debug('[matrix] load: matrix disabled by shouldEnableMatrix() (width=' + window.innerWidth + ')');
|
||||||
|
});
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Light/Dark Mode Toggle
|
||||||
|
*/
|
||||||
|
(function () {
|
||||||
|
const body = document.body;
|
||||||
|
const toggleBtn = document.createElement('button');
|
||||||
|
toggleBtn.className = 'theme-toggle-btn';
|
||||||
|
toggleBtn.innerHTML = '<i class="bi bi-moon-stars-fill"></i>';
|
||||||
|
toggleBtn.setAttribute('aria-label', 'Toggle theme');
|
||||||
|
|
||||||
|
// Style the button (fixed position)
|
||||||
|
Object.assign(toggleBtn.style, {
|
||||||
|
position: 'fixed',
|
||||||
|
bottom: '20px',
|
||||||
|
right: '20px',
|
||||||
|
zIndex: '9999',
|
||||||
|
width: '50px',
|
||||||
|
height: '50px',
|
||||||
|
borderRadius: '50%',
|
||||||
|
border: 'none',
|
||||||
|
backgroundColor: 'var(--accent-color)',
|
||||||
|
color: '#fff',
|
||||||
|
fontSize: '1.2rem',
|
||||||
|
cursor: 'pointer',
|
||||||
|
boxShadow: '0 4px 15px rgba(0,0,0,0.3)',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
transition: 'all 0.3s ease'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Hover effect
|
||||||
|
toggleBtn.addEventListener('mouseenter', () => {
|
||||||
|
toggleBtn.style.transform = 'scale(1.1)';
|
||||||
|
});
|
||||||
|
toggleBtn.addEventListener('mouseleave', () => {
|
||||||
|
toggleBtn.style.transform = 'scale(1)';
|
||||||
|
});
|
||||||
|
|
||||||
|
document.body.appendChild(toggleBtn);
|
||||||
|
|
||||||
|
function setLightMode(isLight) {
|
||||||
|
if (isLight) {
|
||||||
|
body.classList.add('light-mode');
|
||||||
|
toggleBtn.innerHTML = '<i class="bi bi-sun-fill"></i>';
|
||||||
|
} else {
|
||||||
|
body.classList.remove('light-mode');
|
||||||
|
toggleBtn.innerHTML = '<i class="bi bi-moon-stars-fill"></i>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check preference or time
|
||||||
|
const savedTheme = localStorage.getItem('theme');
|
||||||
|
if (savedTheme) {
|
||||||
|
setLightMode(savedTheme === 'light');
|
||||||
|
} else {
|
||||||
|
const hour = new Date().getHours();
|
||||||
|
// Light mode between 00:00 and 17:00
|
||||||
|
if (hour < 17) {
|
||||||
|
setLightMode(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toggle event
|
||||||
|
toggleBtn.addEventListener('click', () => {
|
||||||
|
const isLight = body.classList.contains('light-mode');
|
||||||
|
setLightMode(!isLight);
|
||||||
|
localStorage.setItem('theme', !isLight ? 'light' : 'dark');
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UX Enhancements - Smooth Page Transitions
|
||||||
|
*/
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const links = document.querySelectorAll('a:not([href^="#"]):not([target="_blank"]):not([href^="mailto:"]):not([href^="tel:"])');
|
||||||
|
|
||||||
|
links.forEach(link => {
|
||||||
|
link.addEventListener('click', (e) => {
|
||||||
|
const href = link.getAttribute('href');
|
||||||
|
|
||||||
|
// Check if it's a valid internal link
|
||||||
|
if (href && !href.startsWith('#') && !href.startsWith('javascript:')) {
|
||||||
|
e.preventDefault();
|
||||||
|
document.body.classList.add('fade-out');
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.href = href;
|
||||||
|
}, 300); // Match CSS transition duration
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
})();
|
||||||
1
assets/podcastscript.html
Normal file
2
assets/scss/Readme.txt
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
The .scss (Sass) files are only available in the pro version.
|
||||||
|
You can buy it from: https://bootstrapmade.com/folioone-bootstrap-portfolio-website-template/
|
||||||
BIN
assets/vendor/.DS_Store
vendored
Normal file
614
assets/vendor/aos/aos.cjs.js
vendored
Normal file
|
|
@ -0,0 +1,614 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
|
||||||
|
|
||||||
|
var throttle = _interopDefault(require('lodash.throttle'));
|
||||||
|
var debounce = _interopDefault(require('lodash.debounce'));
|
||||||
|
|
||||||
|
var callback = function callback() {};
|
||||||
|
|
||||||
|
function containsAOSNode(nodes) {
|
||||||
|
var i = void 0,
|
||||||
|
currentNode = void 0,
|
||||||
|
result = void 0;
|
||||||
|
|
||||||
|
for (i = 0; i < nodes.length; i += 1) {
|
||||||
|
currentNode = nodes[i];
|
||||||
|
|
||||||
|
if (currentNode.dataset && currentNode.dataset.aos) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = currentNode.children && containsAOSNode(currentNode.children);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function check(mutations) {
|
||||||
|
if (!mutations) return;
|
||||||
|
|
||||||
|
mutations.forEach(function (mutation) {
|
||||||
|
var addedNodes = Array.prototype.slice.call(mutation.addedNodes);
|
||||||
|
var removedNodes = Array.prototype.slice.call(mutation.removedNodes);
|
||||||
|
var allNodes = addedNodes.concat(removedNodes);
|
||||||
|
|
||||||
|
if (containsAOSNode(allNodes)) {
|
||||||
|
return callback();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMutationObserver() {
|
||||||
|
return window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isSupported() {
|
||||||
|
return !!getMutationObserver();
|
||||||
|
}
|
||||||
|
|
||||||
|
function ready(selector, fn) {
|
||||||
|
var doc = window.document;
|
||||||
|
var MutationObserver = getMutationObserver();
|
||||||
|
|
||||||
|
var observer = new MutationObserver(check);
|
||||||
|
callback = fn;
|
||||||
|
|
||||||
|
observer.observe(doc.documentElement, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
removedNodes: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var observer = { isSupported: isSupported, ready: ready };
|
||||||
|
|
||||||
|
var classCallCheck = function (instance, Constructor) {
|
||||||
|
if (!(instance instanceof Constructor)) {
|
||||||
|
throw new TypeError("Cannot call a class as a function");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var createClass = function () {
|
||||||
|
function defineProperties(target, props) {
|
||||||
|
for (var i = 0; i < props.length; i++) {
|
||||||
|
var descriptor = props[i];
|
||||||
|
descriptor.enumerable = descriptor.enumerable || false;
|
||||||
|
descriptor.configurable = true;
|
||||||
|
if ("value" in descriptor) descriptor.writable = true;
|
||||||
|
Object.defineProperty(target, descriptor.key, descriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return function (Constructor, protoProps, staticProps) {
|
||||||
|
if (protoProps) defineProperties(Constructor.prototype, protoProps);
|
||||||
|
if (staticProps) defineProperties(Constructor, staticProps);
|
||||||
|
return Constructor;
|
||||||
|
};
|
||||||
|
}();
|
||||||
|
|
||||||
|
var _extends = Object.assign || function (target) {
|
||||||
|
for (var i = 1; i < arguments.length; i++) {
|
||||||
|
var source = arguments[i];
|
||||||
|
|
||||||
|
for (var key in source) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
||||||
|
target[key] = source[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return target;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Device detector
|
||||||
|
*/
|
||||||
|
|
||||||
|
var fullNameRe = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i;
|
||||||
|
var prefixRe = /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i;
|
||||||
|
var fullNameMobileRe = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i;
|
||||||
|
var prefixMobileRe = /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i;
|
||||||
|
|
||||||
|
function ua() {
|
||||||
|
return navigator.userAgent || navigator.vendor || window.opera || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
var Detector = function () {
|
||||||
|
function Detector() {
|
||||||
|
classCallCheck(this, Detector);
|
||||||
|
}
|
||||||
|
|
||||||
|
createClass(Detector, [{
|
||||||
|
key: 'phone',
|
||||||
|
value: function phone() {
|
||||||
|
var a = ua();
|
||||||
|
return !!(fullNameRe.test(a) || prefixRe.test(a.substr(0, 4)));
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
key: 'mobile',
|
||||||
|
value: function mobile() {
|
||||||
|
var a = ua();
|
||||||
|
return !!(fullNameMobileRe.test(a) || prefixMobileRe.test(a.substr(0, 4)));
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
key: 'tablet',
|
||||||
|
value: function tablet() {
|
||||||
|
return this.mobile() && !this.phone();
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://browserhacks.com/#hack-acea075d0ac6954f275a70023906050c
|
||||||
|
|
||||||
|
}, {
|
||||||
|
key: 'ie11',
|
||||||
|
value: function ie11() {
|
||||||
|
return '-ms-scroll-limit' in document.documentElement.style && '-ms-ime-align' in document.documentElement.style;
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
return Detector;
|
||||||
|
}();
|
||||||
|
|
||||||
|
var detect = new Detector();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds multiple classes on node
|
||||||
|
* @param {DOMNode} node
|
||||||
|
* @param {array} classes
|
||||||
|
*/
|
||||||
|
var addClasses = function addClasses(node, classes) {
|
||||||
|
return classes && classes.forEach(function (className) {
|
||||||
|
return node.classList.add(className);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes multiple classes from node
|
||||||
|
* @param {DOMNode} node
|
||||||
|
* @param {array} classes
|
||||||
|
*/
|
||||||
|
var removeClasses = function removeClasses(node, classes) {
|
||||||
|
return classes && classes.forEach(function (className) {
|
||||||
|
return node.classList.remove(className);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var fireEvent = function fireEvent(eventName, data) {
|
||||||
|
var customEvent = void 0;
|
||||||
|
|
||||||
|
if (detect.ie11()) {
|
||||||
|
customEvent = document.createEvent('CustomEvent');
|
||||||
|
customEvent.initCustomEvent(eventName, true, true, { detail: data });
|
||||||
|
} else {
|
||||||
|
customEvent = new CustomEvent(eventName, {
|
||||||
|
detail: data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return document.dispatchEvent(customEvent);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set or remove aos-animate class
|
||||||
|
* @param {node} el element
|
||||||
|
* @param {int} top scrolled distance
|
||||||
|
*/
|
||||||
|
var applyClasses = function applyClasses(el, top) {
|
||||||
|
var options = el.options,
|
||||||
|
position = el.position,
|
||||||
|
node = el.node,
|
||||||
|
data = el.data;
|
||||||
|
|
||||||
|
|
||||||
|
var hide = function hide() {
|
||||||
|
if (!el.animated) return;
|
||||||
|
|
||||||
|
removeClasses(node, options.animatedClassNames);
|
||||||
|
fireEvent('aos:out', node);
|
||||||
|
|
||||||
|
if (el.options.id) {
|
||||||
|
fireEvent('aos:in:' + el.options.id, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
el.animated = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
var show = function show() {
|
||||||
|
if (el.animated) return;
|
||||||
|
|
||||||
|
addClasses(node, options.animatedClassNames);
|
||||||
|
|
||||||
|
fireEvent('aos:in', node);
|
||||||
|
if (el.options.id) {
|
||||||
|
fireEvent('aos:in:' + el.options.id, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
el.animated = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (options.mirror && top >= position.out && !options.once) {
|
||||||
|
hide();
|
||||||
|
} else if (top >= position.in) {
|
||||||
|
show();
|
||||||
|
} else if (el.animated && !options.once) {
|
||||||
|
hide();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scroll logic - add or remove 'aos-animate' class on scroll
|
||||||
|
*
|
||||||
|
* @param {array} $elements array of elements nodes
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
var handleScroll = function handleScroll($elements) {
|
||||||
|
return $elements.forEach(function (el, i) {
|
||||||
|
return applyClasses(el, window.pageYOffset);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get offset of DOM element
|
||||||
|
* like there were no transforms applied on it
|
||||||
|
*
|
||||||
|
* @param {Node} el [DOM element]
|
||||||
|
* @return {Object} [top and left offset]
|
||||||
|
*/
|
||||||
|
var offset = function offset(el) {
|
||||||
|
var _x = 0;
|
||||||
|
var _y = 0;
|
||||||
|
|
||||||
|
while (el && !isNaN(el.offsetLeft) && !isNaN(el.offsetTop)) {
|
||||||
|
_x += el.offsetLeft - (el.tagName != 'BODY' ? el.scrollLeft : 0);
|
||||||
|
_y += el.offsetTop - (el.tagName != 'BODY' ? el.scrollTop : 0);
|
||||||
|
el = el.offsetParent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
top: _y,
|
||||||
|
left: _x
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get inline option with a fallback.
|
||||||
|
*
|
||||||
|
* @param {Node} el [Dom element]
|
||||||
|
* @param {String} key [Option key]
|
||||||
|
* @param {String} fallback [Default (fallback) value]
|
||||||
|
* @return {Mixed} [Option set with inline attributes or fallback value if not set]
|
||||||
|
*/
|
||||||
|
|
||||||
|
var getInlineOption = (function (el, key, fallback) {
|
||||||
|
var attr = el.getAttribute('data-aos-' + key);
|
||||||
|
|
||||||
|
if (typeof attr !== 'undefined') {
|
||||||
|
if (attr === 'true') {
|
||||||
|
return true;
|
||||||
|
} else if (attr === 'false') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return attr || fallback;
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate offset
|
||||||
|
* basing on element's settings like:
|
||||||
|
* - anchor
|
||||||
|
* - offset
|
||||||
|
*
|
||||||
|
* @param {Node} el [Dom element]
|
||||||
|
* @return {Integer} [Final offset that will be used to trigger animation in good position]
|
||||||
|
*/
|
||||||
|
|
||||||
|
var getPositionIn = function getPositionIn(el, defaultOffset, defaultAnchorPlacement) {
|
||||||
|
var windowHeight = window.innerHeight;
|
||||||
|
var anchor = getInlineOption(el, 'anchor');
|
||||||
|
var inlineAnchorPlacement = getInlineOption(el, 'anchor-placement');
|
||||||
|
var additionalOffset = Number(getInlineOption(el, 'offset', inlineAnchorPlacement ? 0 : defaultOffset));
|
||||||
|
var anchorPlacement = inlineAnchorPlacement || defaultAnchorPlacement;
|
||||||
|
var finalEl = el;
|
||||||
|
|
||||||
|
if (anchor && document.querySelectorAll(anchor)) {
|
||||||
|
finalEl = document.querySelectorAll(anchor)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
var triggerPoint = offset(finalEl).top - windowHeight;
|
||||||
|
|
||||||
|
switch (anchorPlacement) {
|
||||||
|
case 'top-bottom':
|
||||||
|
// Default offset
|
||||||
|
break;
|
||||||
|
case 'center-bottom':
|
||||||
|
triggerPoint += finalEl.offsetHeight / 2;
|
||||||
|
break;
|
||||||
|
case 'bottom-bottom':
|
||||||
|
triggerPoint += finalEl.offsetHeight;
|
||||||
|
break;
|
||||||
|
case 'top-center':
|
||||||
|
triggerPoint += windowHeight / 2;
|
||||||
|
break;
|
||||||
|
case 'center-center':
|
||||||
|
triggerPoint += windowHeight / 2 + finalEl.offsetHeight / 2;
|
||||||
|
break;
|
||||||
|
case 'bottom-center':
|
||||||
|
triggerPoint += windowHeight / 2 + finalEl.offsetHeight;
|
||||||
|
break;
|
||||||
|
case 'top-top':
|
||||||
|
triggerPoint += windowHeight;
|
||||||
|
break;
|
||||||
|
case 'bottom-top':
|
||||||
|
triggerPoint += windowHeight + finalEl.offsetHeight;
|
||||||
|
break;
|
||||||
|
case 'center-top':
|
||||||
|
triggerPoint += windowHeight + finalEl.offsetHeight / 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return triggerPoint + additionalOffset;
|
||||||
|
};
|
||||||
|
|
||||||
|
var getPositionOut = function getPositionOut(el, defaultOffset) {
|
||||||
|
var windowHeight = window.innerHeight;
|
||||||
|
var anchor = getInlineOption(el, 'anchor');
|
||||||
|
var additionalOffset = getInlineOption(el, 'offset', defaultOffset);
|
||||||
|
var finalEl = el;
|
||||||
|
|
||||||
|
if (anchor && document.querySelectorAll(anchor)) {
|
||||||
|
finalEl = document.querySelectorAll(anchor)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
var elementOffsetTop = offset(finalEl).top;
|
||||||
|
|
||||||
|
return elementOffsetTop + finalEl.offsetHeight - additionalOffset;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Clearing variables */
|
||||||
|
|
||||||
|
var prepare = function prepare($elements, options) {
|
||||||
|
$elements.forEach(function (el, i) {
|
||||||
|
var mirror = getInlineOption(el.node, 'mirror', options.mirror);
|
||||||
|
var once = getInlineOption(el.node, 'once', options.once);
|
||||||
|
var id = getInlineOption(el.node, 'id');
|
||||||
|
var customClassNames = options.useClassNames && el.node.getAttribute('data-aos');
|
||||||
|
|
||||||
|
var animatedClassNames = [options.animatedClassName].concat(customClassNames ? customClassNames.split(' ') : []).filter(function (className) {
|
||||||
|
return typeof className === 'string';
|
||||||
|
});
|
||||||
|
|
||||||
|
if (options.initClassName) {
|
||||||
|
el.node.classList.add(options.initClassName);
|
||||||
|
}
|
||||||
|
|
||||||
|
el.position = {
|
||||||
|
in: getPositionIn(el.node, options.offset, options.anchorPlacement),
|
||||||
|
out: mirror && getPositionOut(el.node, options.offset)
|
||||||
|
};
|
||||||
|
|
||||||
|
el.options = {
|
||||||
|
once: once,
|
||||||
|
mirror: mirror,
|
||||||
|
animatedClassNames: animatedClassNames,
|
||||||
|
id: id
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return $elements;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate initial array with elements as objects
|
||||||
|
* This array will be extended later with elements attributes values
|
||||||
|
* like 'position'
|
||||||
|
*/
|
||||||
|
var elements = (function () {
|
||||||
|
var elements = document.querySelectorAll('[data-aos]');
|
||||||
|
return Array.prototype.map.call(elements, function (node) {
|
||||||
|
return { node: node };
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* *******************************************************
|
||||||
|
* AOS (Animate on scroll) - wowjs alternative
|
||||||
|
* made to animate elements on scroll in both directions
|
||||||
|
* *******************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private variables
|
||||||
|
*/
|
||||||
|
var $aosElements = [];
|
||||||
|
var initialized = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default options
|
||||||
|
*/
|
||||||
|
var options = {
|
||||||
|
offset: 120,
|
||||||
|
delay: 0,
|
||||||
|
easing: 'ease',
|
||||||
|
duration: 400,
|
||||||
|
disable: false,
|
||||||
|
once: false,
|
||||||
|
mirror: false,
|
||||||
|
anchorPlacement: 'top-bottom',
|
||||||
|
startEvent: 'DOMContentLoaded',
|
||||||
|
animatedClassName: 'aos-animate',
|
||||||
|
initClassName: 'aos-init',
|
||||||
|
useClassNames: false,
|
||||||
|
disableMutationObserver: false,
|
||||||
|
throttleDelay: 99,
|
||||||
|
debounceDelay: 50
|
||||||
|
};
|
||||||
|
|
||||||
|
// Detect not supported browsers (<=IE9)
|
||||||
|
// http://browserhacks.com/#hack-e71d8692f65334173fee715c222cb805
|
||||||
|
var isBrowserNotSupported = function isBrowserNotSupported() {
|
||||||
|
return document.all && !window.atob;
|
||||||
|
};
|
||||||
|
|
||||||
|
var initializeScroll = function initializeScroll() {
|
||||||
|
// Extend elements objects in $aosElements with their positions
|
||||||
|
$aosElements = prepare($aosElements, options);
|
||||||
|
// Perform scroll event, to refresh view and show/hide elements
|
||||||
|
handleScroll($aosElements);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle scroll event to animate elements on scroll
|
||||||
|
*/
|
||||||
|
window.addEventListener('scroll', throttle(function () {
|
||||||
|
handleScroll($aosElements, options.once);
|
||||||
|
}, options.throttleDelay));
|
||||||
|
|
||||||
|
return $aosElements;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh AOS
|
||||||
|
*/
|
||||||
|
var refresh = function refresh() {
|
||||||
|
var initialize = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
|
||||||
|
|
||||||
|
// Allow refresh only when it was first initialized on startEvent
|
||||||
|
if (initialize) initialized = true;
|
||||||
|
if (initialized) initializeScroll();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hard refresh
|
||||||
|
* create array with new elements and trigger refresh
|
||||||
|
*/
|
||||||
|
var refreshHard = function refreshHard() {
|
||||||
|
$aosElements = elements();
|
||||||
|
|
||||||
|
if (isDisabled(options.disable) || isBrowserNotSupported()) {
|
||||||
|
return disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable AOS
|
||||||
|
* Remove all attributes to reset applied styles
|
||||||
|
*/
|
||||||
|
var disable = function disable() {
|
||||||
|
$aosElements.forEach(function (el, i) {
|
||||||
|
el.node.removeAttribute('data-aos');
|
||||||
|
el.node.removeAttribute('data-aos-easing');
|
||||||
|
el.node.removeAttribute('data-aos-duration');
|
||||||
|
el.node.removeAttribute('data-aos-delay');
|
||||||
|
|
||||||
|
if (options.initClassName) {
|
||||||
|
el.node.classList.remove(options.initClassName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.animatedClassName) {
|
||||||
|
el.node.classList.remove(options.animatedClassName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if AOS should be disabled based on provided setting
|
||||||
|
*/
|
||||||
|
var isDisabled = function isDisabled(optionDisable) {
|
||||||
|
return optionDisable === true || optionDisable === 'mobile' && detect.mobile() || optionDisable === 'phone' && detect.phone() || optionDisable === 'tablet' && detect.tablet() || typeof optionDisable === 'function' && optionDisable() === true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializing AOS
|
||||||
|
* - Create options merging defaults with user defined options
|
||||||
|
* - Set attributes on <body> as global setting - css relies on it
|
||||||
|
* - Attach preparing elements to options.startEvent,
|
||||||
|
* window resize and orientation change
|
||||||
|
* - Attach function that handle scroll and everything connected to it
|
||||||
|
* to window scroll event and fire once document is ready to set initial state
|
||||||
|
*/
|
||||||
|
var init = function init(settings) {
|
||||||
|
options = _extends(options, settings);
|
||||||
|
|
||||||
|
// Create initial array with elements -> to be fullfilled later with prepare()
|
||||||
|
$aosElements = elements();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable mutation observing if not supported
|
||||||
|
*/
|
||||||
|
if (!options.disableMutationObserver && !observer.isSupported()) {
|
||||||
|
console.info('\n aos: MutationObserver is not supported on this browser,\n code mutations observing has been disabled.\n You may have to call "refreshHard()" by yourself.\n ');
|
||||||
|
options.disableMutationObserver = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observe [aos] elements
|
||||||
|
* If something is loaded by AJAX
|
||||||
|
* it'll refresh plugin automatically
|
||||||
|
*/
|
||||||
|
if (!options.disableMutationObserver) {
|
||||||
|
observer.ready('[data-aos]', refreshHard);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Don't init plugin if option `disable` is set
|
||||||
|
* or when browser is not supported
|
||||||
|
*/
|
||||||
|
if (isDisabled(options.disable) || isBrowserNotSupported()) {
|
||||||
|
return disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set global settings on body, based on options
|
||||||
|
* so CSS can use it
|
||||||
|
*/
|
||||||
|
document.querySelector('body').setAttribute('data-aos-easing', options.easing);
|
||||||
|
|
||||||
|
document.querySelector('body').setAttribute('data-aos-duration', options.duration);
|
||||||
|
|
||||||
|
document.querySelector('body').setAttribute('data-aos-delay', options.delay);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle initializing
|
||||||
|
*/
|
||||||
|
if (['DOMContentLoaded', 'load'].indexOf(options.startEvent) === -1) {
|
||||||
|
// Listen to options.startEvent and initialize AOS
|
||||||
|
document.addEventListener(options.startEvent, function () {
|
||||||
|
refresh(true);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
window.addEventListener('load', function () {
|
||||||
|
refresh(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.startEvent === 'DOMContentLoaded' && ['complete', 'interactive'].indexOf(document.readyState) > -1) {
|
||||||
|
// Initialize AOS if default startEvent was already fired
|
||||||
|
refresh(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh plugin on window resize or orientation change
|
||||||
|
*/
|
||||||
|
window.addEventListener('resize', debounce(refresh, options.debounceDelay, true));
|
||||||
|
|
||||||
|
window.addEventListener('orientationchange', debounce(refresh, options.debounceDelay, true));
|
||||||
|
|
||||||
|
return $aosElements;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export Public API
|
||||||
|
*/
|
||||||
|
|
||||||
|
var aos = {
|
||||||
|
init: init,
|
||||||
|
refresh: refresh,
|
||||||
|
refreshHard: refreshHard
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = aos;
|
||||||
1
assets/vendor/aos/aos.css
vendored
Normal file
610
assets/vendor/aos/aos.esm.js
vendored
Normal file
|
|
@ -0,0 +1,610 @@
|
||||||
|
import throttle from 'lodash.throttle';
|
||||||
|
import debounce from 'lodash.debounce';
|
||||||
|
|
||||||
|
var callback = function callback() {};
|
||||||
|
|
||||||
|
function containsAOSNode(nodes) {
|
||||||
|
var i = void 0,
|
||||||
|
currentNode = void 0,
|
||||||
|
result = void 0;
|
||||||
|
|
||||||
|
for (i = 0; i < nodes.length; i += 1) {
|
||||||
|
currentNode = nodes[i];
|
||||||
|
|
||||||
|
if (currentNode.dataset && currentNode.dataset.aos) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = currentNode.children && containsAOSNode(currentNode.children);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function check(mutations) {
|
||||||
|
if (!mutations) return;
|
||||||
|
|
||||||
|
mutations.forEach(function (mutation) {
|
||||||
|
var addedNodes = Array.prototype.slice.call(mutation.addedNodes);
|
||||||
|
var removedNodes = Array.prototype.slice.call(mutation.removedNodes);
|
||||||
|
var allNodes = addedNodes.concat(removedNodes);
|
||||||
|
|
||||||
|
if (containsAOSNode(allNodes)) {
|
||||||
|
return callback();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMutationObserver() {
|
||||||
|
return window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isSupported() {
|
||||||
|
return !!getMutationObserver();
|
||||||
|
}
|
||||||
|
|
||||||
|
function ready(selector, fn) {
|
||||||
|
var doc = window.document;
|
||||||
|
var MutationObserver = getMutationObserver();
|
||||||
|
|
||||||
|
var observer = new MutationObserver(check);
|
||||||
|
callback = fn;
|
||||||
|
|
||||||
|
observer.observe(doc.documentElement, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
removedNodes: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var observer = { isSupported: isSupported, ready: ready };
|
||||||
|
|
||||||
|
var classCallCheck = function (instance, Constructor) {
|
||||||
|
if (!(instance instanceof Constructor)) {
|
||||||
|
throw new TypeError("Cannot call a class as a function");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var createClass = function () {
|
||||||
|
function defineProperties(target, props) {
|
||||||
|
for (var i = 0; i < props.length; i++) {
|
||||||
|
var descriptor = props[i];
|
||||||
|
descriptor.enumerable = descriptor.enumerable || false;
|
||||||
|
descriptor.configurable = true;
|
||||||
|
if ("value" in descriptor) descriptor.writable = true;
|
||||||
|
Object.defineProperty(target, descriptor.key, descriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return function (Constructor, protoProps, staticProps) {
|
||||||
|
if (protoProps) defineProperties(Constructor.prototype, protoProps);
|
||||||
|
if (staticProps) defineProperties(Constructor, staticProps);
|
||||||
|
return Constructor;
|
||||||
|
};
|
||||||
|
}();
|
||||||
|
|
||||||
|
var _extends = Object.assign || function (target) {
|
||||||
|
for (var i = 1; i < arguments.length; i++) {
|
||||||
|
var source = arguments[i];
|
||||||
|
|
||||||
|
for (var key in source) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
||||||
|
target[key] = source[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return target;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Device detector
|
||||||
|
*/
|
||||||
|
|
||||||
|
var fullNameRe = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i;
|
||||||
|
var prefixRe = /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i;
|
||||||
|
var fullNameMobileRe = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i;
|
||||||
|
var prefixMobileRe = /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i;
|
||||||
|
|
||||||
|
function ua() {
|
||||||
|
return navigator.userAgent || navigator.vendor || window.opera || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
var Detector = function () {
|
||||||
|
function Detector() {
|
||||||
|
classCallCheck(this, Detector);
|
||||||
|
}
|
||||||
|
|
||||||
|
createClass(Detector, [{
|
||||||
|
key: 'phone',
|
||||||
|
value: function phone() {
|
||||||
|
var a = ua();
|
||||||
|
return !!(fullNameRe.test(a) || prefixRe.test(a.substr(0, 4)));
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
key: 'mobile',
|
||||||
|
value: function mobile() {
|
||||||
|
var a = ua();
|
||||||
|
return !!(fullNameMobileRe.test(a) || prefixMobileRe.test(a.substr(0, 4)));
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
key: 'tablet',
|
||||||
|
value: function tablet() {
|
||||||
|
return this.mobile() && !this.phone();
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://browserhacks.com/#hack-acea075d0ac6954f275a70023906050c
|
||||||
|
|
||||||
|
}, {
|
||||||
|
key: 'ie11',
|
||||||
|
value: function ie11() {
|
||||||
|
return '-ms-scroll-limit' in document.documentElement.style && '-ms-ime-align' in document.documentElement.style;
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
return Detector;
|
||||||
|
}();
|
||||||
|
|
||||||
|
var detect = new Detector();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds multiple classes on node
|
||||||
|
* @param {DOMNode} node
|
||||||
|
* @param {array} classes
|
||||||
|
*/
|
||||||
|
var addClasses = function addClasses(node, classes) {
|
||||||
|
return classes && classes.forEach(function (className) {
|
||||||
|
return node.classList.add(className);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes multiple classes from node
|
||||||
|
* @param {DOMNode} node
|
||||||
|
* @param {array} classes
|
||||||
|
*/
|
||||||
|
var removeClasses = function removeClasses(node, classes) {
|
||||||
|
return classes && classes.forEach(function (className) {
|
||||||
|
return node.classList.remove(className);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var fireEvent = function fireEvent(eventName, data) {
|
||||||
|
var customEvent = void 0;
|
||||||
|
|
||||||
|
if (detect.ie11()) {
|
||||||
|
customEvent = document.createEvent('CustomEvent');
|
||||||
|
customEvent.initCustomEvent(eventName, true, true, { detail: data });
|
||||||
|
} else {
|
||||||
|
customEvent = new CustomEvent(eventName, {
|
||||||
|
detail: data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return document.dispatchEvent(customEvent);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set or remove aos-animate class
|
||||||
|
* @param {node} el element
|
||||||
|
* @param {int} top scrolled distance
|
||||||
|
*/
|
||||||
|
var applyClasses = function applyClasses(el, top) {
|
||||||
|
var options = el.options,
|
||||||
|
position = el.position,
|
||||||
|
node = el.node,
|
||||||
|
data = el.data;
|
||||||
|
|
||||||
|
|
||||||
|
var hide = function hide() {
|
||||||
|
if (!el.animated) return;
|
||||||
|
|
||||||
|
removeClasses(node, options.animatedClassNames);
|
||||||
|
fireEvent('aos:out', node);
|
||||||
|
|
||||||
|
if (el.options.id) {
|
||||||
|
fireEvent('aos:in:' + el.options.id, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
el.animated = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
var show = function show() {
|
||||||
|
if (el.animated) return;
|
||||||
|
|
||||||
|
addClasses(node, options.animatedClassNames);
|
||||||
|
|
||||||
|
fireEvent('aos:in', node);
|
||||||
|
if (el.options.id) {
|
||||||
|
fireEvent('aos:in:' + el.options.id, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
el.animated = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (options.mirror && top >= position.out && !options.once) {
|
||||||
|
hide();
|
||||||
|
} else if (top >= position.in) {
|
||||||
|
show();
|
||||||
|
} else if (el.animated && !options.once) {
|
||||||
|
hide();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scroll logic - add or remove 'aos-animate' class on scroll
|
||||||
|
*
|
||||||
|
* @param {array} $elements array of elements nodes
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
var handleScroll = function handleScroll($elements) {
|
||||||
|
return $elements.forEach(function (el, i) {
|
||||||
|
return applyClasses(el, window.pageYOffset);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get offset of DOM element
|
||||||
|
* like there were no transforms applied on it
|
||||||
|
*
|
||||||
|
* @param {Node} el [DOM element]
|
||||||
|
* @return {Object} [top and left offset]
|
||||||
|
*/
|
||||||
|
var offset = function offset(el) {
|
||||||
|
var _x = 0;
|
||||||
|
var _y = 0;
|
||||||
|
|
||||||
|
while (el && !isNaN(el.offsetLeft) && !isNaN(el.offsetTop)) {
|
||||||
|
_x += el.offsetLeft - (el.tagName != 'BODY' ? el.scrollLeft : 0);
|
||||||
|
_y += el.offsetTop - (el.tagName != 'BODY' ? el.scrollTop : 0);
|
||||||
|
el = el.offsetParent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
top: _y,
|
||||||
|
left: _x
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get inline option with a fallback.
|
||||||
|
*
|
||||||
|
* @param {Node} el [Dom element]
|
||||||
|
* @param {String} key [Option key]
|
||||||
|
* @param {String} fallback [Default (fallback) value]
|
||||||
|
* @return {Mixed} [Option set with inline attributes or fallback value if not set]
|
||||||
|
*/
|
||||||
|
|
||||||
|
var getInlineOption = (function (el, key, fallback) {
|
||||||
|
var attr = el.getAttribute('data-aos-' + key);
|
||||||
|
|
||||||
|
if (typeof attr !== 'undefined') {
|
||||||
|
if (attr === 'true') {
|
||||||
|
return true;
|
||||||
|
} else if (attr === 'false') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return attr || fallback;
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate offset
|
||||||
|
* basing on element's settings like:
|
||||||
|
* - anchor
|
||||||
|
* - offset
|
||||||
|
*
|
||||||
|
* @param {Node} el [Dom element]
|
||||||
|
* @return {Integer} [Final offset that will be used to trigger animation in good position]
|
||||||
|
*/
|
||||||
|
|
||||||
|
var getPositionIn = function getPositionIn(el, defaultOffset, defaultAnchorPlacement) {
|
||||||
|
var windowHeight = window.innerHeight;
|
||||||
|
var anchor = getInlineOption(el, 'anchor');
|
||||||
|
var inlineAnchorPlacement = getInlineOption(el, 'anchor-placement');
|
||||||
|
var additionalOffset = Number(getInlineOption(el, 'offset', inlineAnchorPlacement ? 0 : defaultOffset));
|
||||||
|
var anchorPlacement = inlineAnchorPlacement || defaultAnchorPlacement;
|
||||||
|
var finalEl = el;
|
||||||
|
|
||||||
|
if (anchor && document.querySelectorAll(anchor)) {
|
||||||
|
finalEl = document.querySelectorAll(anchor)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
var triggerPoint = offset(finalEl).top - windowHeight;
|
||||||
|
|
||||||
|
switch (anchorPlacement) {
|
||||||
|
case 'top-bottom':
|
||||||
|
// Default offset
|
||||||
|
break;
|
||||||
|
case 'center-bottom':
|
||||||
|
triggerPoint += finalEl.offsetHeight / 2;
|
||||||
|
break;
|
||||||
|
case 'bottom-bottom':
|
||||||
|
triggerPoint += finalEl.offsetHeight;
|
||||||
|
break;
|
||||||
|
case 'top-center':
|
||||||
|
triggerPoint += windowHeight / 2;
|
||||||
|
break;
|
||||||
|
case 'center-center':
|
||||||
|
triggerPoint += windowHeight / 2 + finalEl.offsetHeight / 2;
|
||||||
|
break;
|
||||||
|
case 'bottom-center':
|
||||||
|
triggerPoint += windowHeight / 2 + finalEl.offsetHeight;
|
||||||
|
break;
|
||||||
|
case 'top-top':
|
||||||
|
triggerPoint += windowHeight;
|
||||||
|
break;
|
||||||
|
case 'bottom-top':
|
||||||
|
triggerPoint += windowHeight + finalEl.offsetHeight;
|
||||||
|
break;
|
||||||
|
case 'center-top':
|
||||||
|
triggerPoint += windowHeight + finalEl.offsetHeight / 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return triggerPoint + additionalOffset;
|
||||||
|
};
|
||||||
|
|
||||||
|
var getPositionOut = function getPositionOut(el, defaultOffset) {
|
||||||
|
var windowHeight = window.innerHeight;
|
||||||
|
var anchor = getInlineOption(el, 'anchor');
|
||||||
|
var additionalOffset = getInlineOption(el, 'offset', defaultOffset);
|
||||||
|
var finalEl = el;
|
||||||
|
|
||||||
|
if (anchor && document.querySelectorAll(anchor)) {
|
||||||
|
finalEl = document.querySelectorAll(anchor)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
var elementOffsetTop = offset(finalEl).top;
|
||||||
|
|
||||||
|
return elementOffsetTop + finalEl.offsetHeight - additionalOffset;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Clearing variables */
|
||||||
|
|
||||||
|
var prepare = function prepare($elements, options) {
|
||||||
|
$elements.forEach(function (el, i) {
|
||||||
|
var mirror = getInlineOption(el.node, 'mirror', options.mirror);
|
||||||
|
var once = getInlineOption(el.node, 'once', options.once);
|
||||||
|
var id = getInlineOption(el.node, 'id');
|
||||||
|
var customClassNames = options.useClassNames && el.node.getAttribute('data-aos');
|
||||||
|
|
||||||
|
var animatedClassNames = [options.animatedClassName].concat(customClassNames ? customClassNames.split(' ') : []).filter(function (className) {
|
||||||
|
return typeof className === 'string';
|
||||||
|
});
|
||||||
|
|
||||||
|
if (options.initClassName) {
|
||||||
|
el.node.classList.add(options.initClassName);
|
||||||
|
}
|
||||||
|
|
||||||
|
el.position = {
|
||||||
|
in: getPositionIn(el.node, options.offset, options.anchorPlacement),
|
||||||
|
out: mirror && getPositionOut(el.node, options.offset)
|
||||||
|
};
|
||||||
|
|
||||||
|
el.options = {
|
||||||
|
once: once,
|
||||||
|
mirror: mirror,
|
||||||
|
animatedClassNames: animatedClassNames,
|
||||||
|
id: id
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return $elements;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate initial array with elements as objects
|
||||||
|
* This array will be extended later with elements attributes values
|
||||||
|
* like 'position'
|
||||||
|
*/
|
||||||
|
var elements = (function () {
|
||||||
|
var elements = document.querySelectorAll('[data-aos]');
|
||||||
|
return Array.prototype.map.call(elements, function (node) {
|
||||||
|
return { node: node };
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* *******************************************************
|
||||||
|
* AOS (Animate on scroll) - wowjs alternative
|
||||||
|
* made to animate elements on scroll in both directions
|
||||||
|
* *******************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private variables
|
||||||
|
*/
|
||||||
|
var $aosElements = [];
|
||||||
|
var initialized = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default options
|
||||||
|
*/
|
||||||
|
var options = {
|
||||||
|
offset: 120,
|
||||||
|
delay: 0,
|
||||||
|
easing: 'ease',
|
||||||
|
duration: 400,
|
||||||
|
disable: false,
|
||||||
|
once: false,
|
||||||
|
mirror: false,
|
||||||
|
anchorPlacement: 'top-bottom',
|
||||||
|
startEvent: 'DOMContentLoaded',
|
||||||
|
animatedClassName: 'aos-animate',
|
||||||
|
initClassName: 'aos-init',
|
||||||
|
useClassNames: false,
|
||||||
|
disableMutationObserver: false,
|
||||||
|
throttleDelay: 99,
|
||||||
|
debounceDelay: 50
|
||||||
|
};
|
||||||
|
|
||||||
|
// Detect not supported browsers (<=IE9)
|
||||||
|
// http://browserhacks.com/#hack-e71d8692f65334173fee715c222cb805
|
||||||
|
var isBrowserNotSupported = function isBrowserNotSupported() {
|
||||||
|
return document.all && !window.atob;
|
||||||
|
};
|
||||||
|
|
||||||
|
var initializeScroll = function initializeScroll() {
|
||||||
|
// Extend elements objects in $aosElements with their positions
|
||||||
|
$aosElements = prepare($aosElements, options);
|
||||||
|
// Perform scroll event, to refresh view and show/hide elements
|
||||||
|
handleScroll($aosElements);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle scroll event to animate elements on scroll
|
||||||
|
*/
|
||||||
|
window.addEventListener('scroll', throttle(function () {
|
||||||
|
handleScroll($aosElements, options.once);
|
||||||
|
}, options.throttleDelay));
|
||||||
|
|
||||||
|
return $aosElements;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh AOS
|
||||||
|
*/
|
||||||
|
var refresh = function refresh() {
|
||||||
|
var initialize = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
|
||||||
|
|
||||||
|
// Allow refresh only when it was first initialized on startEvent
|
||||||
|
if (initialize) initialized = true;
|
||||||
|
if (initialized) initializeScroll();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hard refresh
|
||||||
|
* create array with new elements and trigger refresh
|
||||||
|
*/
|
||||||
|
var refreshHard = function refreshHard() {
|
||||||
|
$aosElements = elements();
|
||||||
|
|
||||||
|
if (isDisabled(options.disable) || isBrowserNotSupported()) {
|
||||||
|
return disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable AOS
|
||||||
|
* Remove all attributes to reset applied styles
|
||||||
|
*/
|
||||||
|
var disable = function disable() {
|
||||||
|
$aosElements.forEach(function (el, i) {
|
||||||
|
el.node.removeAttribute('data-aos');
|
||||||
|
el.node.removeAttribute('data-aos-easing');
|
||||||
|
el.node.removeAttribute('data-aos-duration');
|
||||||
|
el.node.removeAttribute('data-aos-delay');
|
||||||
|
|
||||||
|
if (options.initClassName) {
|
||||||
|
el.node.classList.remove(options.initClassName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.animatedClassName) {
|
||||||
|
el.node.classList.remove(options.animatedClassName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if AOS should be disabled based on provided setting
|
||||||
|
*/
|
||||||
|
var isDisabled = function isDisabled(optionDisable) {
|
||||||
|
return optionDisable === true || optionDisable === 'mobile' && detect.mobile() || optionDisable === 'phone' && detect.phone() || optionDisable === 'tablet' && detect.tablet() || typeof optionDisable === 'function' && optionDisable() === true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializing AOS
|
||||||
|
* - Create options merging defaults with user defined options
|
||||||
|
* - Set attributes on <body> as global setting - css relies on it
|
||||||
|
* - Attach preparing elements to options.startEvent,
|
||||||
|
* window resize and orientation change
|
||||||
|
* - Attach function that handle scroll and everything connected to it
|
||||||
|
* to window scroll event and fire once document is ready to set initial state
|
||||||
|
*/
|
||||||
|
var init = function init(settings) {
|
||||||
|
options = _extends(options, settings);
|
||||||
|
|
||||||
|
// Create initial array with elements -> to be fullfilled later with prepare()
|
||||||
|
$aosElements = elements();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable mutation observing if not supported
|
||||||
|
*/
|
||||||
|
if (!options.disableMutationObserver && !observer.isSupported()) {
|
||||||
|
console.info('\n aos: MutationObserver is not supported on this browser,\n code mutations observing has been disabled.\n You may have to call "refreshHard()" by yourself.\n ');
|
||||||
|
options.disableMutationObserver = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observe [aos] elements
|
||||||
|
* If something is loaded by AJAX
|
||||||
|
* it'll refresh plugin automatically
|
||||||
|
*/
|
||||||
|
if (!options.disableMutationObserver) {
|
||||||
|
observer.ready('[data-aos]', refreshHard);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Don't init plugin if option `disable` is set
|
||||||
|
* or when browser is not supported
|
||||||
|
*/
|
||||||
|
if (isDisabled(options.disable) || isBrowserNotSupported()) {
|
||||||
|
return disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set global settings on body, based on options
|
||||||
|
* so CSS can use it
|
||||||
|
*/
|
||||||
|
document.querySelector('body').setAttribute('data-aos-easing', options.easing);
|
||||||
|
|
||||||
|
document.querySelector('body').setAttribute('data-aos-duration', options.duration);
|
||||||
|
|
||||||
|
document.querySelector('body').setAttribute('data-aos-delay', options.delay);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle initializing
|
||||||
|
*/
|
||||||
|
if (['DOMContentLoaded', 'load'].indexOf(options.startEvent) === -1) {
|
||||||
|
// Listen to options.startEvent and initialize AOS
|
||||||
|
document.addEventListener(options.startEvent, function () {
|
||||||
|
refresh(true);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
window.addEventListener('load', function () {
|
||||||
|
refresh(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.startEvent === 'DOMContentLoaded' && ['complete', 'interactive'].indexOf(document.readyState) > -1) {
|
||||||
|
// Initialize AOS if default startEvent was already fired
|
||||||
|
refresh(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh plugin on window resize or orientation change
|
||||||
|
*/
|
||||||
|
window.addEventListener('resize', debounce(refresh, options.debounceDelay, true));
|
||||||
|
|
||||||
|
window.addEventListener('orientationchange', debounce(refresh, options.debounceDelay, true));
|
||||||
|
|
||||||
|
return $aosElements;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export Public API
|
||||||
|
*/
|
||||||
|
|
||||||
|
var aos = {
|
||||||
|
init: init,
|
||||||
|
refresh: refresh,
|
||||||
|
refreshHard: refreshHard
|
||||||
|
};
|
||||||
|
|
||||||
|
export default aos;
|
||||||