2023-09-01 11:45:11 +02:00
|
|
|
/**
|
2023-09-02 15:07:03 +02:00
|
|
|
* Template Name: Laura
|
|
|
|
* Updated: Jul 27 2023 with Bootstrap v5.3.1
|
|
|
|
* Template URL: https://bootstrapmade.com/laura-free-creative-bootstrap-theme/
|
|
|
|
* Author: BootstrapMade.com
|
|
|
|
* License: https://bootstrapmade.com/license/
|
|
|
|
*/
|
|
|
|
(function() {
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Easy selector helper function
|
|
|
|
*/
|
|
|
|
const select = (el, all = false) => {
|
|
|
|
el = el.trim();
|
|
|
|
if (all) {
|
|
|
|
return [...document.querySelectorAll(el)];
|
|
|
|
} else {
|
|
|
|
return document.querySelector(el);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Easy event listener function
|
|
|
|
*/
|
|
|
|
const on = (type, el, listener, all = false) => {
|
|
|
|
let selectEl = select(el, all);
|
|
|
|
if (selectEl) {
|
|
|
|
if (all) {
|
|
|
|
selectEl.forEach((e) => e.addEventListener(type, listener));
|
|
|
|
} else {
|
|
|
|
selectEl.addEventListener(type, listener);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Easy on scroll event listener
|
|
|
|
*/
|
|
|
|
const onscroll = (el, listener) => {
|
|
|
|
el.addEventListener("scroll", listener);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Navbar links active state on scroll
|
|
|
|
*/
|
|
|
|
let navbarlinks = select("#navbar .scrollto", true);
|
|
|
|
const navbarlinksActive = () => {
|
|
|
|
let position = window.scrollY + 200;
|
|
|
|
navbarlinks.forEach((navbarlink) => {
|
|
|
|
if (!navbarlink.hash) return;
|
|
|
|
let section = select(navbarlink.hash);
|
|
|
|
if (!section) return;
|
|
|
|
if (
|
|
|
|
position >= section.offsetTop &&
|
|
|
|
position <= section.offsetTop + section.offsetHeight
|
|
|
|
) {
|
|
|
|
navbarlink.classList.add("active");
|
|
|
|
} else {
|
|
|
|
navbarlink.classList.remove("active");
|
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|
|
|
|
window.addEventListener("load", navbarlinksActive);
|
|
|
|
onscroll(document, navbarlinksActive);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Scrolls to an element with header offset
|
|
|
|
*/
|
|
|
|
const scrollto = (el) => {
|
|
|
|
let header = select("#header");
|
|
|
|
let offset = header.offsetHeight;
|
|
|
|
|
|
|
|
if (!header.classList.contains("header-scrolled")) {
|
|
|
|
offset -= 20;
|
|
|
|
}
|
|
|
|
|
|
|
|
let elementPos = select(el).offsetTop;
|
|
|
|
window.scrollTo({
|
|
|
|
top: elementPos - offset,
|
|
|
|
behavior: "smooth",
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Toggle .header-scrolled class to #header when page is scrolled
|
|
|
|
*/
|
|
|
|
let selectHeader = select("#header");
|
|
|
|
if (selectHeader) {
|
|
|
|
const headerScrolled = () => {
|
|
|
|
if (window.scrollY > 100) {
|
|
|
|
selectHeader.classList.add("header-scrolled");
|
|
|
|
} else {
|
|
|
|
selectHeader.classList.remove("header-scrolled");
|
|
|
|
}
|
|
|
|
};
|
|
|
|
window.addEventListener("load", headerScrolled);
|
|
|
|
onscroll(document, headerScrolled);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Back to top button
|
|
|
|
*/
|
|
|
|
let backtotop = select(".back-to-top");
|
|
|
|
if (backtotop) {
|
|
|
|
const toggleBacktotop = () => {
|
|
|
|
if (window.scrollY > 100) {
|
|
|
|
backtotop.classList.add("active");
|
|
|
|
} else {
|
|
|
|
backtotop.classList.remove("active");
|
|
|
|
}
|
|
|
|
};
|
|
|
|
window.addEventListener("load", toggleBacktotop);
|
|
|
|
onscroll(document, toggleBacktotop);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Mobile nav toggle
|
|
|
|
*/
|
|
|
|
on("click", ".mobile-nav-toggle", function(e) {
|
|
|
|
select("#navbar").classList.toggle("navbar-mobile");
|
|
|
|
this.classList.toggle("bi-list");
|
|
|
|
this.classList.toggle("bi-x");
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Mobile nav dropdowns activate
|
|
|
|
*/
|
|
|
|
on(
|
|
|
|
"click",
|
|
|
|
".navbar .dropdown > a",
|
|
|
|
function(e) {
|
|
|
|
if (select("#navbar").classList.contains("navbar-mobile")) {
|
|
|
|
e.preventDefault();
|
|
|
|
this.nextElementSibling.classList.toggle("dropdown-active");
|
|
|
|
}
|
|
|
|
},
|
|
|
|
true,
|
|
|
|
);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Scrool with ofset on links with a class name .scrollto
|
|
|
|
*/
|
|
|
|
on(
|
|
|
|
"click",
|
|
|
|
".scrollto",
|
|
|
|
function(e) {
|
|
|
|
if (select(this.hash)) {
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
|
|
let navbar = select("#navbar");
|
|
|
|
if (navbar.classList.contains("navbar-mobile")) {
|
|
|
|
navbar.classList.remove("navbar-mobile");
|
|
|
|
let navbarToggle = select(".mobile-nav-toggle");
|
|
|
|
navbarToggle.classList.toggle("bi-list");
|
|
|
|
navbarToggle.classList.toggle("bi-x");
|
|
|
|
}
|
|
|
|
scrollto(this.hash);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
true,
|
|
|
|
);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Scroll with ofset on page load with hash links in the url
|
|
|
|
*/
|
|
|
|
window.addEventListener("load", () => {
|
|
|
|
if (window.location.hash) {
|
|
|
|
if (select(window.location.hash)) {
|
|
|
|
scrollto(window.location.hash);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Skills animation
|
|
|
|
*/
|
2023-09-07 13:55:54 +02:00
|
|
|
let skillsContent = select(".progress", true);
|
2023-09-02 17:10:48 +02:00
|
|
|
if (skillsContent) {
|
|
|
|
skillsContent.forEach((skill) => {
|
|
|
|
new Waypoint({
|
|
|
|
element: skill,
|
|
|
|
offset: "100%",
|
|
|
|
handler: function(direction) {
|
|
|
|
let progress = skill.querySelectorAll(".progress-bar");
|
|
|
|
progress.forEach((el) => {
|
2023-09-07 13:55:54 +02:00
|
|
|
let percent =
|
|
|
|
100 *
|
|
|
|
(+el.getAttribute("aria-valuenow") /
|
|
|
|
(+el.getAttribute("aria-valuemax") -
|
|
|
|
+el.getAttribute("aria-valuemin")));
|
|
|
|
el.style.width = percent + "%";
|
2023-09-02 17:10:48 +02:00
|
|
|
});
|
|
|
|
},
|
|
|
|
});
|
2023-09-02 15:07:03 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Testimonials slider
|
|
|
|
*/
|
|
|
|
new Swiper(".testimonials-slider", {
|
|
|
|
speed: 600,
|
|
|
|
loop: true,
|
|
|
|
autoplay: {
|
2023-09-13 10:14:51 +02:00
|
|
|
delay: 10000,
|
2023-09-02 15:07:03 +02:00
|
|
|
disableOnInteraction: false,
|
|
|
|
},
|
|
|
|
slidesPerView: "auto",
|
|
|
|
pagination: {
|
|
|
|
el: ".swiper-pagination",
|
|
|
|
type: "bullets",
|
|
|
|
clickable: true,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Porfolio isotope and filter
|
|
|
|
*/
|
|
|
|
window.addEventListener("load", () => {
|
|
|
|
let portfolioContainer = select(".portfolio-container");
|
|
|
|
if (portfolioContainer) {
|
|
|
|
let portfolioIsotope = new Isotope(portfolioContainer, {
|
|
|
|
itemSelector: ".portfolio-item",
|
|
|
|
});
|
|
|
|
|
|
|
|
let portfolioFilters = select("#portfolio-flters li", true);
|
|
|
|
|
|
|
|
on(
|
|
|
|
"click",
|
|
|
|
"#portfolio-flters li",
|
|
|
|
function(e) {
|
|
|
|
e.preventDefault();
|
|
|
|
portfolioFilters.forEach(function(el) {
|
|
|
|
el.classList.remove("filter-active");
|
|
|
|
});
|
|
|
|
this.classList.add("filter-active");
|
|
|
|
|
|
|
|
portfolioIsotope.arrange({
|
|
|
|
filter: this.getAttribute("data-filter"),
|
|
|
|
});
|
|
|
|
},
|
|
|
|
true,
|
|
|
|
);
|
|
|
|
}
|
2023-09-03 00:10:57 +02:00
|
|
|
// let horizontal_aoss = document.querySelectorAll("[data-aos~='left'], [data-aos~='right']")
|
|
|
|
if (window.matchMedia("(max-width: 768px)").matches) {
|
|
|
|
let horizontal_aoss = document.querySelectorAll(
|
|
|
|
`[data-aos$="left"], [data-aos$="right"]`,
|
|
|
|
);
|
|
|
|
horizontal_aoss.forEach((el) => el.setAttribute("data-aos", "fade-up"));
|
|
|
|
}
|
2023-09-02 16:36:37 +02:00
|
|
|
AOS.init();
|
2023-09-02 15:07:03 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initiate portfolio lightbox
|
|
|
|
*/
|
|
|
|
const portfolioLightbox = GLightbox({
|
|
|
|
selector: ".portfolio-lightbox",
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Portfolio details slider
|
|
|
|
*/
|
|
|
|
new Swiper(".portfolio-details-slider", {
|
|
|
|
speed: 400,
|
|
|
|
loop: true,
|
|
|
|
autoplay: {
|
|
|
|
delay: 5000,
|
|
|
|
disableOnInteraction: false,
|
|
|
|
},
|
|
|
|
pagination: {
|
|
|
|
el: ".swiper-pagination",
|
|
|
|
type: "bullets",
|
|
|
|
clickable: true,
|
|
|
|
},
|
|
|
|
});
|
2023-09-03 00:10:57 +02:00
|
|
|
})();
|