1// Function to fetch the latest articles from the API 2async function fetchLatestArticles() { 3 const urls = [ 4 'https://www.equilar.com/wp-json/wp/v2/publication?offset=0', 5 'https://www.equilar.com/wp-json/wp/v2/posts?offset=0', 6 'https://www.equilar.com/wp-json/wp/v2/event?offset=0', 7 'https://www.equilar.com/wp-json/wp/v2/webinar?offset=0' 8 ]; 9 10 try { 11 const [publicationResponse, blogResponse, eventResponse, webinarResponse] = await Promise.all(urls.map(url => fetch(url))); 12 13 const publicationData = await publicationResponse.json(); 14 const blogData = await blogResponse.json(); 15 const eventData = await eventResponse.json(); 16 const webinarData = await webinarResponse.json(); 17 18 // Filter and sort events 19 const filteredEventData = eventData.filter(event => 20 event.acf.register_now_link || event.title.rendered.toLowerCase().includes('save the date') 21 ).sort((a, b) => new Date(a.acf.event_date) - new Date(b.acf.event_date)).slice(0, 2); 22 23 // Filter webinars with register_now_link 24 const filteredWebinarData = webinarData.filter(webinar => webinar.acf.register_now_link); 25 26 const selectedWebinarEventData = [...filteredEventData, ...filteredWebinarData]; 27 28 const allArticles = [...selectedWebinarEventData, ...publicationData, ...blogData].sort((a, b) => new Date(b.date) - new Date(a.date)); 29 30 return allArticles; 31 } catch (error) { 32 console.error('No content available', error); 33 return []; 34 } 35} 36 37// Function to format the date 38function formatDate(dateString, isWebinar = false) { 39 const options = { month: 'long', day: 'numeric', year: 'numeric' }; 40 return new Date(dateString).toLocaleDateString('en-US', options); 41} 42 43// Function to format the date for events 44function formatDateEvent(dateString) { 45 const parts = dateString.match(/(\w+), (\w+) (\d+), (\d+)/); 46 if (!parts) { 47 return dateString; 48 } 49 50 const [, , month, day, year] = parts; 51 const options = { year: 'numeric', month: 'long', day: 'numeric' }; 52 53 return new Date(`${month} ${day}, ${year}`).toLocaleDateString('en-US', options); 54} 55 56// Function to generate HTML for each article 57function generateArticleHtml(article, index, isMobile = false) { 58 const typeClass = `horizontal-article-type-${index}`; 59 const imageClass = `horizontal-article-image-${index}`; 60 const linkClass = `horizontal-article-link-${index}`; 61 const titleClass = `horizontal-article-title-${index}`; 62 const dateClass = `horizontal-article-date-${index}`; 63 64 const coreClass = isMobile ? 'horizontal-article-core-mobile' : 'horizontal-article-core'; 65 66 const type = determineArticleType(article); 67 const imageSrc = article.acf.feature_listing_image; 68 const linkHref = determineArticleLink(article); 69 const title = article.title.rendered; 70 const date = type === 'Event' ? formatDateEvent(article.acf.event_date) : (type === 'Webinar' ? formatDate(article.acf.webinar_date, true) : formatDate(article.date)); 71 72 return ` 73<div class="${coreClass}"> 74 <p class="horizontal-article-type-text ${typeClass}">${type}</p> 75 <a class="${linkClass}" href="${linkHref}"> 76 <img class="${imageClass}" src="${imageSrc}" alt="${title}"> 77 </a> 78 <a class="${linkClass}" href="${linkHref}"> 79 <h3 class="${titleClass}">${title}</h3> 80 </a> 81 <p class="${dateClass}">${date}</p> 82</div> 83`; 84} 85 86// Function to determine the article type 87function determineArticleType(article) { 88 if (article.type === 'post') { 89 return 'Blog'; 90 } else if (article.type === 'event') { 91 return 'Event'; 92 } else if (article.type === 'webinar' && article.acf.register_now_link) { 93 return 'Webinar'; 94 } else if (article.type === 'publication' && !article.acf.request_report_button_link) { 95 return 'Report'; 96 } else { 97 return 'Publication'; 98 } 99} 100 101// Function to determine the appropriate link for each article type 102function determineArticleLink(article) { 103 if (article.type === 'event') { 104 return article.title.rendered.toLowerCase().includes('save the date') ? article.acf.learn_more : article.acf.register_now_link; 105 } else if (article.type === 'webinar') { 106 return article.acf.register_now_link; 107 } else { 108 return article.acf.cta_link || article.acf.read_more_button_link || article.acf.request_report_button_link || ''; 109 } 110} 111 112// Function to apply opacity with delay 113function applyOpacityWithDelay(selector, delay) { 114 setTimeout(() => { 115 document.querySelectorAll(selector).forEach(element => { 116 element.style.opacity = 1; 117 }); 118 });
119} 120 121// Function to get the current og:url from meta tag 122function getOgUrl() { 123 const metaOgUrl = document.querySelector('meta[property="og:url"]'); 124 return metaOgUrl ? metaOgUrl.getAttribute('content') : ''; 125} 126 127// Function to populate the slider with articles 128async function populateSlider(startIndex = 0) { 129 const metaRoles = document.querySelector('meta[name="data-roles"]'); 130 const roles = metaRoles ? JSON.parse(metaRoles.getAttribute('content')) : []; 131 const ogUrl = getOgUrl(); 132 133 const latestArticles = await fetchLatestArticles(); 134 135 if (latestArticles.length === 0) { 136 console.error('No articles found.'); 137 return; 138 } 139 140 // Filter out articles with URLs matching og:url 141 const filteredArticles = latestArticles.filter(article => determineArticleLink(article) !== ogUrl); 142 143 // Separate events and other articles 144 const events = filteredArticles.filter(article => article.type === 'event' && (article.acf.register_now_link || article.title.rendered.toLowerCase().includes('save the date'))); 145 const otherArticles = filteredArticles.filter(article => article.type !== 'event'); 146 147 // Ensure to take top 1 event 148 const topEvents = events.slice(0, 2).reverse(); 149 150 // Filter other articles based on roles if roles are defined 151 const filteredOtherArticles = roles.length > 0 ? otherArticles.filter(article => { 152 if (article.acf.roles) { 153 return article.acf.roles.some(role => roles.includes(role)); 154 } 155 return false; // If roles are defined but article has no roles, it won't be included 156 }) : otherArticles; 157 158 // Combine top events with the filtered other articles 159 let combinedArticles = [...topEvents, ...filteredOtherArticles]; 160 161 // If there aren't enough articles, add non-matching articles 162 if (combinedArticles.length < 8) { 163 // Sort articles by date 164 const sortedFilteredOtherArticles = otherArticles 165 .filter(article => !filteredOtherArticles.includes(article)) 166 .sort((a, b) => new Date(b.date) - new Date(a.date)); 167 168 // Exclude newer articles 169 const oldestMatchingDate = combinedArticles.length > 0 ? new Date(combinedArticles[combinedArticles.length - 1].date) : new Date(); 170 const additionalArticles = sortedFilteredOtherArticles.filter(article => new Date(article.date) < oldestMatchingDate); 171 172 // Add additional articles to fill the slots 173 combinedArticles = [...combinedArticles, ...additionalArticles.slice(0, 8 - combinedArticles.length)]; 174 } 175 176 // Limit to a maximum of 8 articles 177 const limitedArticles = combinedArticles.slice(0, 8); 178 const articlesToShow = limitedArticles.slice(startIndex, startIndex + 4); 179 180 // Container for desktop view 181 const desktopContainer = document.querySelector('.horizontal-article-slider-boxes'); 182 // Container for mobile view 183 const mobileOuterContainer = document.querySelector('.horizontal-article-slider-outer-container-mobile'); 184 const mobileContainer = mobileOuterContainer.querySelector('.horizontal-article-slider-boxes'); 185 186 // Populate desktop slider 187 desktopContainer.innerHTML = articlesToShow.map((article, index) => generateArticleHtml(article, startIndex + index + 1)).join(''); 188 // Populate mobile slider 189 mobileContainer.innerHTML = articlesToShow.map((article, index) => generateArticleHtml(article, startIndex + index + 1, true)).join(''); 190 191 // Apply opacity with a delay for specified images 192 applyOpacityWithDelay('.horizontal-article-image-1, .horizontal-article-image-2, .horizontal-article-image-3, .horizontal-article-image-4, .horizontal-article-image-5, .horizontal-article-image-6, .horizontal-article-image-7, .horizontal-article-image-8', 0); 193} 194 195// Variables to track the current start index 196let startIndex = 0; 197 198// Event listener for right arrow button 199document.querySelector('.horizontal-right-arrow-container').addEventListener('click', async () => { 200 const latestArticles = await fetchLatestArticles(); 201 const rightArrow = document.querySelector('.fa-arrow-right'); 202 const leftArrow = document.querySelector('.fa-arrow-left'); 203 const overlayLine = document.querySelector('.horizontal-overlay-line'); 204 205 if (startIndex + 4 < Math.min(latestArticles.length, 8)) { 206 overlayLine.addEventListener('transitionend', () => { 207 rightArrow.style.transition = 'color 300ms'; 208 rightArrow.style.color = '#d4d4d4'; 209 leftArrow.style.transition = 'color 300ms'; 210 leftArrow.style.color = 'rgb(50, 50, 50)'; 211 }, { once: true });
212 213 startIndex += 4; 214 populateSlider(startIndex); 215 overlayLine.style.transition = 'left 300ms'; 216 overlayLine.style.left = '80px'; 217 218 applyOpacityWithDelay('.horizontal-article-image-2, .horizontal-article-link-2, .horizontal-article-title-2, .horizontal-article-date-2, .horizontal-overlay-line > .horizontal-overlay-line-inner > div:nth-child(2)', 0); 219 } 220}); 221 222// Event listener for left arrow button 223document.querySelector('.horizontal-left-arrow-container').addEventListener('click', () => { 224 const leftArrow = document.querySelector('.fa-arrow-left'); 225 const rightArrow = document.querySelector('.fa-arrow-right'); 226 const overlayLine = document.querySelector('.horizontal-overlay-line'); 227 228 if (startIndex - 4 >= 0) { 229 overlayLine.addEventListener('transitionend', () => { 230 leftArrow.style.transition = 'color 300ms'; 231 leftArrow.style.color = '#d4d4d4'; 232 rightArrow.style.transition = 'color 300ms'; 233 rightArrow.style.color = 'rgb(50, 50, 50)'; 234 }, { once: true }); 235 236 startIndex -= 4; 237 populateSlider(startIndex); 238 overlayLine.style.transition = 'left 300ms'; 239 overlayLine.style.left = '0px'; 240 241 applyOpacityWithDelay('.horizontal-article-image-1, .horizontal-article-link-1, .horizontal-article-title-1, .horizontal-article-date-1, .horizontal-overlay-line > .horizontal-overlay-line-inner > div:nth-child(1)', 0); 242 } 243}); 244 245// Initialize slider 246populateSlider(); 247 248function addImageLinks(linkElements, imageElements) { 249 for (let i = 0; i < linkElements.length; i++) { 250 const link = linkElements[i]; 251 const image = imageElements[i]; 252 253 const imageLink = link.cloneNode(true); 254 imageLink.innerHTML = ''; 255 imageLink.appendChild(image); 256 257 link.parentNode.insertBefore(imageLink, link); 258 } 259} 260 261// Get all the desktop link and image elements 262const desktopLinkElements = document.querySelectorAll('.horizontal-article-core a'); 263const desktopImageElements = document.querySelectorAll('.horizontal-article-core img'); 264 265// Add image links for desktop version 266addImageLinks(desktopLinkElements, desktopImageElements); 267 268// Get all the mobile link and image elements 269const mobileLinkElements = document.querySelectorAll('.horizontal-article-core-mobile a'); 270const mobileImageElements = document.querySelectorAll('.horizontal-article-core-mobile img'); 271 272// Add image links for mobile version 273addImageLinks(mobileLinkElements, mobileImageElements);
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.