/** * Template Name: Bootslander * Template URL: https://bootstrapmade.com/bootslander-free-bootstrap-landing-page-template/ * Updated: Mar 17 2024 with Bootstrap v5.3.3 * 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) } } }); /** * Preloader */ let preloader = select('#preloader'); if (preloader) { window.addEventListener('load', () => { preloader.remove() }); } /** * Initiate glightbox */ const glightbox = GLightbox({ selector: '.glightbox' }); /** * Initiate gallery lightbox */ const galleryLightbox = GLightbox({ selector: '.gallery-lightbox' }); /** * Testimonials slider */ new Swiper('.testimonials-slider', { speed: 600, loop: true, autoplay: { delay: 5000, disableOnInteraction: false }, slidesPerView: 'auto', pagination: { el: '.swiper-pagination', type: 'bullets', clickable: true } }); /** * Animation on scroll */ window.addEventListener('load', () => { AOS.init({ duration: 1000, easing: 'ease-in-out', once: true, mirror: false }) }); /** * Initiate Pure Counter */ new PureCounter(); })(); // VISU 1 document.addEventListener("DOMContentLoaded", function () { function sleepAnalysis() { d3.json("../static/js/sleep_combined_clean.json").then(function (data) { // Pré-traitement des données data.forEach((d) => { d.date = d3.timeParse("%Y-%m-%d")(d.date); d.month = d3.timeMonth(d.date); }); // Initialisation des constantes const svgWidth = 800; const svgHeight = 400; const margin = { top: 20, right: 60, bottom: 60, left: 60 }; const colorScale = d3.scaleOrdinal(["#ff6347", "#4682b4", "#32cd32", "#ff1493"]); // Fonction pour afficher le graphique principal (par mois) function renderMonthlyChart(year, members) { const monthsForYear = d3.timeMonths(new Date(`${year}-01-01`), new Date(`${year}-12-31`)); const groupedData = d3.group(data, (d) => d.month); const nestedData = monthsForYear.map((month) => { const valuesForMonth = groupedData.get(month) || []; const avgMonthSleep = members.map((member) => { const memberData = valuesForMonth.filter((d) => d[member] !== null); return { member, value: memberData.length > 0 ? d3.mean(memberData, (v) => v[member]) : null, }; }); return { month, values: avgMonthSleep }; }); const svg = d3.select("#chart").attr("width", svgWidth).attr("height", svgHeight); svg.selectAll("*").remove(); const x = d3.scaleBand() .domain(monthsForYear) .range([margin.left, svgWidth - margin.right]) .padding(0.05); const y = d3.scaleLinear() .domain([0, d3.max(nestedData, (d) => d3.max(d.values, (v) => v.value))]) .nice() .range([svgHeight - margin.bottom, margin.top]); svg.append("g") .attr("transform", `translate(0,${svgHeight - margin.bottom})`) .call(d3.axisBottom(x).tickFormat(d3.timeFormat("%B"))); svg.append("g") .attr("transform", `translate(${margin.left},0)`) .call(d3.axisLeft(y)); svg.append("g") .selectAll("g") .data(nestedData) .enter() .append("g") .attr("transform", (d) => `translate(${x(d.month)},0)`) .selectAll("rect") .data((d) => d.values.filter((v) => v.value !== null)) .enter() .append("rect") .attr("x", (d, i) => i * (x.bandwidth() / members.length)) .attr("y", (d) => y(d.value)) .attr("width", x.bandwidth() / members.length - 1) .attr("height", (d) => svgHeight - margin.bottom - y(d.value)) .attr("fill", (d) => colorScale(d.member)) .on("click", (event, d) => { renderDailyChart(year, d.member, d.month, colorScale(d.member)); }); } // Fonction pour afficher le graphique journalier (par jour) function renderDailyChart(year, member, month, color) { const monthData = data.filter( (d) => d.month.getMonth() === month.getMonth() && d.date.getFullYear() === year && d[member] !== null ); const daysInMonth = Array.from({ length: 31 }, (_, i) => i + 1); const dailyData = daysInMonth.map((day) => { const dayData = monthData.find((d) => d.date.getDate() === day); return { day, sleepHours: dayData ? dayData[member] : null }; }); const svg = d3.select("#dailyChart").attr("width", svgWidth).attr("height", svgHeight); svg.selectAll("*").remove(); const x = d3.scaleBand() .domain(dailyData.map((d) => d.day)) .range([margin.left, svgWidth - margin.right]) .padding(0.1); const y = d3.scaleLinear() .domain([0, d3.max(dailyData, (d) => d.sleepHours)]) .nice() .range([svgHeight - margin.bottom, margin.top]); svg.append("g") .attr("transform", `translate(0,${svgHeight - margin.bottom})`) .call(d3.axisBottom(x)); svg.append("g") .attr("transform", `translate(${margin.left},0)`) .call(d3.axisLeft(y)); svg.append("g") .selectAll("rect") .data(dailyData) .enter() .append("rect") .attr("x", (d) => x(d.day)) .attr("y", (d) => (d.sleepHours ? y(d.sleepHours) : svgHeight - margin.bottom)) .attr("width", x.bandwidth()) .attr("height", (d) => (d.sleepHours ? svgHeight - margin.bottom - y(d.sleepHours) : 0)) .attr("fill", color); } // Gestion des interactions function initializeInteractions() { const yearSelect = document.getElementById("yearSelect"); const memberSelect = document.getElementById("memberSelect"); const backButton = document.getElementById("backButton"); yearSelect.addEventListener("change", () => { const year = +yearSelect.value; const members = Array.from(memberSelect.querySelectorAll("input:checked")).map((c) => c.value); renderMonthlyChart(year, members); }); memberSelect.querySelectorAll("input").forEach((input) => { input.addEventListener("change", () => { const year = +yearSelect.value; const members = Array.from(memberSelect.querySelectorAll("input:checked")).map((c) => c.value); renderMonthlyChart(year, members); }); }); backButton.addEventListener("click", () => { document.getElementById("dailyChart").style.display = "none"; document.getElementById("chart").style.display = "block"; backButton.style.display = "none"; }); } // Initialisation complète renderMonthlyChart(2024, ["Anis", "Maya", "Corentin", "Amira"]); initializeInteractions(); }); } // Appel de la fonction principale sleepAnalysis(); });