570 lines
17 KiB
JavaScript
570 lines
17 KiB
JavaScript
document.addEventListener("DOMContentLoaded", function () {
|
|
|
|
|
|
|
|
const sections = document.querySelectorAll(".section");
|
|
const navLinks = document.querySelectorAll(".fbs__net-navbar .scroll-link");
|
|
|
|
function removeActiveClasses() {
|
|
if (navLinks) {
|
|
navLinks.forEach((link) => link.classList.remove("active"));
|
|
}
|
|
}
|
|
|
|
function addActiveClass(currentSectionId) {
|
|
const activeLink = document.querySelector(
|
|
`.fbs__net-navbar .scroll-link[href="#${currentSectionId}"]`
|
|
);
|
|
if (activeLink) {
|
|
activeLink.classList.add("active");
|
|
}
|
|
}
|
|
|
|
function openNewPatient(){
|
|
document.getElementById('modal-new').showModal();
|
|
}
|
|
|
|
function exportCSV(){
|
|
const rows = [['Paciente','CPF','Idade','Status']];
|
|
document.querySelectorAll('#patients tbody tr').forEach(tr=>{
|
|
const tds = [...tr.querySelectorAll('td')].map(td=>td.innerText.replace(/\n.*/,'').trim());
|
|
rows.push([tds[0],tds[1],tds[2],tds[3]]);
|
|
});
|
|
const csv = rows.map(r=>r.map(s=>`"${s.replaceAll('"','""')}"`).join(',')).join('\n');
|
|
const blob = new Blob([csv], {type:'text/csv;charset=utf-8;'});
|
|
const url = URL.createObjectURL(blob);
|
|
const a = Object.assign(document.createElement('a'), {href:url, download:'pacientes.csv'});
|
|
document.body.appendChild(a); a.click(); a.remove();
|
|
setTimeout(()=>URL.revokeObjectURL(url), 1000);
|
|
}
|
|
|
|
function filterTable(q){
|
|
q = (q||'').toLowerCase();
|
|
document.querySelectorAll('#patients tbody tr').forEach(tr => {
|
|
const text = tr.innerText.toLowerCase();
|
|
tr.style.display = text.includes(q) ? '' : 'none';
|
|
});
|
|
}
|
|
|
|
function getCurrentSection() {
|
|
let currentSection = null;
|
|
let minDistance = Infinity;
|
|
if (sections) {
|
|
sections.forEach((section) => {
|
|
const rect = section.getBoundingClientRect();
|
|
const distance = Math.abs(rect.top - window.innerHeight / 4);
|
|
|
|
if (distance < minDistance && rect.top < window.innerHeight) {
|
|
minDistance = distance;
|
|
currentSection = section.getAttribute("id");
|
|
}
|
|
});
|
|
}
|
|
|
|
return currentSection;
|
|
}
|
|
|
|
function updateActiveLink() {
|
|
const currentSectionId = getCurrentSection();
|
|
if (currentSectionId) {
|
|
removeActiveClasses();
|
|
addActiveClass(currentSectionId);
|
|
}
|
|
}
|
|
|
|
window.addEventListener("scroll", updateActiveLink);
|
|
|
|
const portfolioGrid = document.querySelector('#portfolio-grid');
|
|
if (portfolioGrid) {
|
|
var iso = new Isotope("#portfolio-grid", {
|
|
itemSelector: ".portfolio-item",
|
|
layoutMode: "masonry",
|
|
});
|
|
|
|
if (iso) {
|
|
iso.on("layoutComplete", updateActiveLink);
|
|
|
|
imagesLoaded("#portfolio-grid", function () {
|
|
iso.layout();
|
|
updateActiveLink();
|
|
});
|
|
}
|
|
|
|
var filterButtons = document.querySelectorAll(".filter-button");
|
|
if (filterButtons) {
|
|
filterButtons.forEach(function (button) {
|
|
button.addEventListener("click", function (e) {
|
|
e.preventDefault();
|
|
var filterValue = button.getAttribute("data-filter");
|
|
iso.arrange({ filter: filterValue });
|
|
|
|
filterButtons.forEach(function (btn) {
|
|
btn.classList.remove("active");
|
|
});
|
|
button.classList.add("active");
|
|
updateActiveLink();
|
|
});
|
|
});
|
|
}
|
|
|
|
updateActiveLink();
|
|
}
|
|
});
|
|
|
|
const navbarScrollInit = () => {
|
|
var navbar = document.querySelector(".fbs__net-navbar");
|
|
|
|
var scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
|
if (navbar) {
|
|
if (scrollTop > 0) {
|
|
navbar.classList.add("active");
|
|
} else {
|
|
navbar.classList.remove("active");
|
|
}
|
|
}
|
|
};
|
|
|
|
const navbarInit = () => {
|
|
document.querySelectorAll('.dropdown-toggle[href="#"]').forEach(function (el, index) {
|
|
el.addEventListener("click", function (event) {
|
|
event.stopPropagation();
|
|
});
|
|
});
|
|
};
|
|
|
|
// ======= Marquee =======
|
|
const logoMarqueeInit = () => {
|
|
const wrapper = document.querySelector(".logo-wrapper");
|
|
const boxes = gsap.utils.toArray(".logo-item");
|
|
|
|
if (boxes.length > 0) {
|
|
const loop = horizontalLoop(boxes, {
|
|
paused: false,
|
|
repeat: -1,
|
|
speed: 0.25,
|
|
reversed: false,
|
|
});
|
|
|
|
function horizontalLoop(items, config) {
|
|
items = gsap.utils.toArray(items);
|
|
config = config || {};
|
|
let tl = gsap.timeline({
|
|
repeat: config.repeat,
|
|
paused: config.paused,
|
|
defaults: { ease: "none" },
|
|
onReverseComplete: () =>
|
|
tl.totalTime(tl.rawTime() + tl.duration() * 100),
|
|
}),
|
|
length = items.length,
|
|
startX = items[0].offsetLeft,
|
|
times = [],
|
|
widths = [],
|
|
xPercents = [],
|
|
curIndex = 0,
|
|
pixelsPerSecond = (config.speed || 1) * 100,
|
|
snap =
|
|
config.snap === false ? (v) => v : gsap.utils.snap(config.snap || 1), // some browsers shift by a pixel to accommodate flex layouts, so for example if width is 20% the first element's width might be 242px, and the next 243px, alternating back and forth. So we snap to 5 percentage points to make things look more natural
|
|
totalWidth,
|
|
curX,
|
|
distanceToStart,
|
|
distanceToLoop,
|
|
item,
|
|
i;
|
|
gsap.set(items, {
|
|
// convert "x" to "xPercent" to make things responsive, and populate the widths/xPercents Arrays to make lookups faster.
|
|
xPercent: (i, el) => {
|
|
let w = (widths[i] = parseFloat(gsap.getProperty(el, "width", "px")));
|
|
xPercents[i] = snap(
|
|
(parseFloat(gsap.getProperty(el, "x", "px")) / w) * 100 +
|
|
gsap.getProperty(el, "xPercent")
|
|
);
|
|
return xPercents[i];
|
|
},
|
|
});
|
|
gsap.set(items, { x: 0 });
|
|
totalWidth =
|
|
items[length - 1].offsetLeft +
|
|
(xPercents[length - 1] / 100) * widths[length - 1] -
|
|
startX +
|
|
items[length - 1].offsetWidth *
|
|
gsap.getProperty(items[length - 1], "scaleX") +
|
|
(parseFloat(config.paddingRight) || 0);
|
|
for (i = 0; i < length; i++) {
|
|
item = items[i];
|
|
curX = (xPercents[i] / 100) * widths[i];
|
|
distanceToStart = item.offsetLeft + curX - startX;
|
|
distanceToLoop =
|
|
distanceToStart + widths[i] * gsap.getProperty(item, "scaleX");
|
|
tl.to(
|
|
item,
|
|
{
|
|
xPercent: snap(((curX - distanceToLoop) / widths[i]) * 100),
|
|
duration: distanceToLoop / pixelsPerSecond,
|
|
},
|
|
0
|
|
)
|
|
.fromTo(
|
|
item,
|
|
{
|
|
xPercent: snap(
|
|
((curX - distanceToLoop + totalWidth) / widths[i]) * 100
|
|
),
|
|
},
|
|
{
|
|
xPercent: xPercents[i],
|
|
duration:
|
|
(curX - distanceToLoop + totalWidth - curX) / pixelsPerSecond,
|
|
immediateRender: false,
|
|
},
|
|
distanceToLoop / pixelsPerSecond
|
|
)
|
|
.add("label" + i, distanceToStart / pixelsPerSecond);
|
|
times[i] = distanceToStart / pixelsPerSecond;
|
|
}
|
|
function toIndex(index, vars) {
|
|
vars = vars || {};
|
|
Math.abs(index - curIndex) > length / 2 &&
|
|
(index += index > curIndex ? -length : length); // always go in the shortest direction
|
|
let newIndex = gsap.utils.wrap(0, length, index),
|
|
time = times[newIndex];
|
|
if (time > tl.time() !== index > curIndex) {
|
|
// if we're wrapping the timeline's playhead, make the proper adjustments
|
|
vars.modifiers = { time: gsap.utils.wrap(0, tl.duration()) };
|
|
time += tl.duration() * (index > curIndex ? 1 : -1);
|
|
}
|
|
curIndex = newIndex;
|
|
vars.overwrite = true;
|
|
return tl.tweenTo(time, vars);
|
|
}
|
|
tl.next = (vars) => toIndex(curIndex + 1, vars);
|
|
tl.previous = (vars) => toIndex(curIndex - 1, vars);
|
|
tl.current = () => curIndex;
|
|
tl.toIndex = (index, vars) => toIndex(index, vars);
|
|
tl.times = times;
|
|
tl.progress(1, true).progress(0, true); // pre-render for performance
|
|
if (config.reversed) {
|
|
tl.vars.onReverseComplete();
|
|
tl.reverse();
|
|
}
|
|
return tl;
|
|
}
|
|
}
|
|
};
|
|
|
|
document.addEventListener("DOMContentLoaded", logoMarqueeInit);
|
|
|
|
// ======= Navbar Scroll =======
|
|
document.addEventListener("DOMContentLoaded", function () {
|
|
logoMarqueeInit();
|
|
navbarInit();
|
|
window.addEventListener("scroll", navbarScrollInit);
|
|
});
|
|
|
|
// ======= Swiper =======
|
|
const swiperInit = () => {
|
|
var swiper = new Swiper(".testimonialSwiper", {
|
|
slidesPerView: 1,
|
|
speed: 700,
|
|
spaceBetween: 30,
|
|
loop: true,
|
|
pagination: {
|
|
el: ".swiper-pagination",
|
|
clickable: true,
|
|
},
|
|
breakpoints: {
|
|
640: {
|
|
slidesPerView: 1.5,
|
|
spaceBetween: 20,
|
|
},
|
|
768: {
|
|
slidesPerView: 2.5,
|
|
spaceBetween: 30,
|
|
},
|
|
1024: {
|
|
slidesPerView: 2.5,
|
|
spaceBetween: 30,
|
|
},
|
|
},
|
|
navigation: {
|
|
nextEl: ".custom-button-next",
|
|
prevEl: ".custom-button-prev",
|
|
},
|
|
});
|
|
|
|
const progressCircle = document.querySelector(".autoplay-progress svg");
|
|
const progressContent = document.querySelector(".autoplay-progress span");
|
|
if (progressCircle && progressContent ) {
|
|
var swiper2 = new Swiper(".sliderSwiper", {
|
|
slidesPerView: 1,
|
|
speed: 700,
|
|
spaceBetween: 0,
|
|
loop: true,
|
|
centeredSlides: true,
|
|
autoplay: {
|
|
delay: 7000,
|
|
disableOnInteraction: false
|
|
},
|
|
pagination: {
|
|
el: ".swiper-pagination",
|
|
clickable: true,
|
|
},
|
|
navigation: {
|
|
nextEl: ".custom-button-next",
|
|
prevEl: ".custom-button-prev",
|
|
},
|
|
|
|
on: {
|
|
autoplayTimeLeft(s, time, progress) {
|
|
progressCircle.style.setProperty("--progress", 1 - progress);
|
|
progressContent.textContent = `${Math.ceil(time / 1000)}s`;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
};
|
|
|
|
document.addEventListener("DOMContentLoaded", swiperInit);
|
|
|
|
// ======= Glightbox =======
|
|
const glightBoxInit = () => {
|
|
const lightbox = GLightbox({
|
|
touchNavigation: true,
|
|
loop: true,
|
|
autoplayVideos: true,
|
|
});
|
|
};
|
|
document.addEventListener("DOMContentLoaded", glightBoxInit);
|
|
|
|
// ======= BS OffCanvass =======
|
|
const bsOffCanvasInit = () => {
|
|
var offcanvasElement = document.getElementById("fbs__net-navbars");
|
|
if (offcanvasElement) {
|
|
offcanvasElement.addEventListener("show.bs.offcanvas", function () {
|
|
document.body.classList.add("offcanvas-active");
|
|
});
|
|
|
|
offcanvasElement.addEventListener("hidden.bs.offcanvas", function () {
|
|
document.body.classList.remove("offcanvas-active");
|
|
});
|
|
}
|
|
};
|
|
document.addEventListener("DOMContentLoaded", bsOffCanvasInit);
|
|
|
|
// ======= Back To Top =======
|
|
const backToTopInit = () => {
|
|
const backToTopButton = document.getElementById("back-to-top");
|
|
if (backToTopButton) {
|
|
window.addEventListener("scroll", () => {
|
|
if (window.scrollY > 170) {
|
|
backToTopButton.classList.add("show");
|
|
} else {
|
|
backToTopButton.classList.remove("show");
|
|
}
|
|
});
|
|
backToTopButton.addEventListener("click", () => {
|
|
window.scrollTo({
|
|
top: 0,
|
|
behavior: "smooth",
|
|
});
|
|
});
|
|
}
|
|
};
|
|
|
|
document.addEventListener("DOMContentLoaded", backToTopInit);
|
|
|
|
|
|
// ======= Inline SVG =======
|
|
const inlineSvgInit = () => {
|
|
const imgElements = document.querySelectorAll(".js-img-to-inline-svg");
|
|
if (imgElements) {
|
|
imgElements.forEach((imgElement) => {
|
|
const imgURL = imgElement.getAttribute("src");
|
|
|
|
fetch(imgURL)
|
|
.then((response) => response.text())
|
|
.then((svgText) => {
|
|
const parser = new DOMParser();
|
|
const svgDocument = parser.parseFromString(svgText, "image/svg+xml");
|
|
const svgElement = svgDocument.documentElement;
|
|
|
|
Array.from(imgElement.attributes).forEach((attr) => {
|
|
if (attr.name !== "class") {
|
|
svgElement.setAttribute(attr.name, attr.value);
|
|
} else {
|
|
const classes = attr.value
|
|
.split(" ")
|
|
.filter((className) => className !== "js-img-to-inline-svg");
|
|
if (classes.length > 0) {
|
|
svgElement.setAttribute("class", classes.join(" "));
|
|
}
|
|
}
|
|
});
|
|
|
|
imgElement.replaceWith(svgElement);
|
|
})
|
|
.catch((error) => console.error("Error fetching SVG:", error));
|
|
});
|
|
}
|
|
};
|
|
|
|
document.addEventListener("DOMContentLoaded", inlineSvgInit);
|
|
|
|
// ======= AOS =======
|
|
const aosInit = () => {
|
|
AOS.init({
|
|
duration: 800,
|
|
easing: 'slide',
|
|
once: true
|
|
});
|
|
}
|
|
document.addEventListener("DOMContentLoaded", aosInit);
|
|
|
|
// ======= PureCounter =======
|
|
const pureCounterInit = () => {
|
|
new PureCounter({
|
|
selector: ".purecounter",
|
|
});
|
|
}
|
|
document.addEventListener("DOMContentLoaded", pureCounterInit);
|
|
|
|
// ======= Disable Click Navbar Dropdown =======
|
|
const addHoverEvents = (dropdown) => {
|
|
const dropdownToggle = dropdown.querySelector('.dropdown-toggle');
|
|
|
|
const preventClick = (event) => event.preventDefault();
|
|
const showDropdown = () => {
|
|
dropdown.classList.add('show');
|
|
dropdownToggle.setAttribute('aria-expanded', 'true');
|
|
const dropdownMenu = dropdown.querySelector('.dropdown-menu');
|
|
dropdownMenu.classList.add('show');
|
|
};
|
|
const hideDropdown = () => {
|
|
dropdown.classList.remove('show');
|
|
dropdownToggle.setAttribute('aria-expanded', 'false');
|
|
const dropdownMenu = dropdown.querySelector('.dropdown-menu');
|
|
dropdownMenu.classList.remove('show');
|
|
};
|
|
|
|
// Disable the click event for toggling the dropdown
|
|
dropdownToggle.addEventListener('click', preventClick);
|
|
|
|
// Open dropdown on hover
|
|
dropdown.addEventListener('mouseover', showDropdown);
|
|
|
|
// Close dropdown when mouse leaves
|
|
dropdown.addEventListener('mouseleave', hideDropdown);
|
|
|
|
// Store references to the event listeners for later removal
|
|
dropdown.__events = { preventClick, showDropdown, hideDropdown };
|
|
};
|
|
|
|
const removeHoverEvents = (dropdown) => {
|
|
const dropdownToggle = dropdown.querySelector('.dropdown-toggle');
|
|
const { preventClick, showDropdown, hideDropdown } = dropdown.__events || {};
|
|
|
|
if (preventClick) {
|
|
// Remove the event listeners
|
|
dropdownToggle.removeEventListener('click', preventClick);
|
|
dropdown.removeEventListener('mouseover', showDropdown);
|
|
dropdown.removeEventListener('mouseleave', hideDropdown);
|
|
|
|
// Remove the reference to the stored events
|
|
delete dropdown.__events;
|
|
}
|
|
};
|
|
|
|
const handleNavbarEvents = () => {
|
|
const dropdowns = document.querySelectorAll('.navbar .dropdown');
|
|
const dropstarts = document.querySelectorAll('.navbar .dropstart');
|
|
const dropends = document.querySelectorAll('.navbar .dropend');
|
|
|
|
if (window.innerWidth >= 992) {
|
|
|
|
// Add hover events to dropdowns
|
|
dropdowns.forEach(addHoverEvents);
|
|
dropstarts.forEach(addHoverEvents);
|
|
dropends.forEach(addHoverEvents);
|
|
} else {
|
|
|
|
// Remove hover events from dropdowns
|
|
dropdowns.forEach(removeHoverEvents);
|
|
dropstarts.forEach(removeHoverEvents);
|
|
dropends.forEach(removeHoverEvents);
|
|
}
|
|
};
|
|
|
|
// Function to handle resizing
|
|
const handleResize = () => {
|
|
const dropdowns = document.querySelectorAll('.navbar .dropdown');
|
|
const dropstarts = document.querySelectorAll('.navbar .dropstart');
|
|
const dropends = document.querySelectorAll('.navbar .dropend');
|
|
|
|
// Remove hover events before rechecking window size
|
|
dropdowns.forEach(removeHoverEvents);
|
|
dropstarts.forEach(removeHoverEvents);
|
|
dropends.forEach(removeHoverEvents);
|
|
|
|
// Re-apply hover events based on window size
|
|
handleNavbarEvents();
|
|
};
|
|
|
|
// Call the function on resize event and initially
|
|
window.addEventListener('resize', handleResize);
|
|
handleNavbarEvents();
|
|
|
|
|
|
|
|
// ======= Coming Soon Countdown =======
|
|
const countdownInit = () => {
|
|
|
|
// Get the current year
|
|
const currentYear = new Date().getFullYear();
|
|
const nextYear = currentYear + 1;
|
|
const launchDate = new Date(`December 31, ${nextYear} 23:59:59`).getTime();
|
|
|
|
// Change this "December 31, 2024 23:59:59" to your your website launch date
|
|
// const launchDate = new Date("December 31, 2024 23:59:59").getTime();
|
|
|
|
|
|
const x = setInterval(function () {
|
|
|
|
const now = new Date().getTime();
|
|
|
|
const distance = launchDate - now;
|
|
|
|
const days = Math.floor(distance / (1000 * 60 * 60 * 24));
|
|
const hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
|
|
const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
|
|
const seconds = Math.floor((distance % (1000 * 60)) / 1000);
|
|
|
|
// Output the result in an element with id
|
|
const daysEl = document.getElementById("days");
|
|
const hoursEl = document.getElementById("hours");
|
|
const minutesEl = document.getElementById("minutes");
|
|
const secondsEl = document.getElementById("seconds");
|
|
if (daysEl) {
|
|
daysEl.innerText = days;
|
|
}
|
|
if (hoursEl) {
|
|
hoursEl.innerText = hours;
|
|
}
|
|
if (minutesEl) {
|
|
minutesEl.innerText = minutes;
|
|
}
|
|
if (secondsEl) {
|
|
secondsEl.innerText = seconds;
|
|
}
|
|
|
|
// If the count down is finished, write some text
|
|
if (distance < 0) {
|
|
clearInterval(x);
|
|
document.querySelector(".countdown").innerText = "Launched!";
|
|
}
|
|
}, 1000);
|
|
};
|
|
document.addEventListener('DOMContentLoaded', countdownInit);
|
|
|
|
window.openNewPatient = openNewPatient;
|
|
window.exportCSV = exportCSV;
|
|
window.filterTable = filterTable; |