1function calculateReadingTime(string) { 2 const words = string.split(' ').length; 3 return Math.ceil(words / 225); // 225 word per minute 4} 5function displayReadingTime(readingTime, Drupal) { 6 const textToRead = document.querySelector('.article-content').innerText.replaceAll('\n\n', ' '); 7 readingTime.textContent = Drupal.formatPlural(calculateReadingTime(textToRead), '1 minute', '@count minutes');; 8} 9 10(function (Drupal) { 11 Drupal.behaviors.paragraph_summary= { 12 attach: function (context, settings) { 13 //***********************************************************************************// 14 // Paragraph Sommaire 15 //***********************************************************************************// 16 once('paragraphSummaryBehavior', 'html').forEach(function (element) { 17 const readingTime = document.getElementById('reading-time'); 18 if (readingTime) { 19 displayReadingTime(readingTime, Drupal); 20 } 21 22 const paraSummary = document.getElementsByClassName('summary'); 23 if (paraSummary.length === 0) { 24 return; 25 } 26 27 let headerHeight = document.querySelector('.header-topbar').offsetHeight; 28 const summaryMenu = document.getElementById('summary-menu'); 29 const summaryLinks = summaryMenu.querySelectorAll('a'); 30 31 const sectionPositions = Array.from(summaryLinks).map((link, i, array) => { 32 33 const targetId = link.getAttribute('data-target'); 34 const targetElement = document.getElementById(targetId); 35 let top = (targetElement.getBoundingClientRect().top + window.scrollY) - headerHeight; 36 if (i + 1 === array.length) { 37 // gestion des petits contenus sur le dernier paragraph 38 const footerHeight = document.querySelector('footer').offsetHeight; 39 if (footerHeight > targetElement.offsetHeight) { 40 top -= footerHeight / 1.5; 41 } 42 } 43 return { 44 link: link, 45 target: targetElement, 46 top, 47 }; 48 }); 49 const topHigherPara = sectionPositions[0].top; 50 const topLowestPara = sectionPositions[sectionPositions.length-1].top; 51 52 const updateActiveLink = () => { 53 // + 250 pour que le lien actif change avant la disparition complète du paragraph précédent 54 const scrollPosition = window.scrollY + 250; 55 if (scrollPosition <= topHigherPara) { 56 // plus haut que le 1er paragraph, on active le 1er lien 57 resetLinksAndActiveOne(sectionPositions[0].link); 58 return; 59 } else if (scrollPosition >= topLowestPara) { 60 // à partir du dernier paragraph, on active le dernier lien 61 resetLinksAndActiveOne(sectionPositions[sectionPositions.length-1].link); 62 return; 63 } 64 // Mise à jour de la classe active en fonction du défilement 65 for (let i = 0; i < sectionPositions.length; i++) { 66 if (scrollPosition >= sectionPositions[i].top && scrollPosition < sectionPositions[i+1].top) { 67 resetLinksAndActiveOne(sectionPositions[i].link); 68 sectionPositions[i].link.classList.add(('active')); 69 break; 70 } 71 } 72 } 73 74 function resetLinksAndActiveOne(link) { 75 sectionPositions.forEach(elt => elt.link.classList.remove('active')); 76 link.classList.add(('active')); 77 } 78 79 window.addEventListener('DOMContentLoaded', updateActiveLink); 80 window.addEventListener('scroll', updateActiveLink); 81 82 }); 83 } 84 }; 85})(Drupal);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.