1const defaultNewsTypeColors = { 2 All: "#003366", 3 Articles: "#4A5568", 4 "Client Wins": "#2D3748", 5 Community: "#1A365D", 6 DEI: "#718096", 7 News: "#2B6CB0", 8 Press: "#4C51BF", 9 Speaking: "#6B46C1", 10}; 11 12const defaultNewsGroups = [ 13 { 14 label: "Firm News and Recognition", 15 name: "firm-news-recognition", 16 value: ["News", "Press"], 17 }, 18 { 19 label: "Client Success & Case Highlights", 20 name: "client-success-case-highlights", 21 value: ["Client Wins"], 22 }, 23 { 24 label: "In the Community", 25 name: "in-the-community", 26 value: ["Community", "DEI"], 27 }, 28 { 29 label: "Insights & Articles", 30 name: "insights-articles", 31 value: ["Articles", "Speaking"], 32 }, 33]; 34 35let newsTypeColors = defaultNewsTypeColors; 36let newsGroups = defaultNewsGroups; 37 38try { 39 if (document.querySelector("#news-type-colors")) 40 newsTypeColors = JSON.parse( 41 document.querySelector("#news-type-colors").textContent 42 ); 43} catch (error) { 44 console.error("Error parsing news type colors:", error); 45} 46 47try { 48 if (document.querySelector("#news-groups")) 49 newsGroups = JSON.parse( 50 document.querySelector("#news-groups").textContent 51 ); 52 if (Object.values(newsGroups).every((group) => group.value.length === 0)) 53 newsGroups = defaultNewsGroups; 54} catch (error) { 55 console.error("Error parsing news groups:", error); 56} 57 58// ================================================================================================ 59 60/** 61 * Set the color of the filter buttons (FacetWP has client code that we need to wait for). 62 */ 63const setFilterColors = () => { 64 try { 65 const filters = document.querySelector(".facetwp-facet-news_types"); 66 if (!filters) return; 67 68 for (const filter of filters.querySelectorAll(".facetwp-radio")) { 69 const filterKey = 70 filter.querySelector(".facetwp-display-value")?.textContent || 71 "All"; 72 filter.style.backgroundColor = 73 newsTypeColors[filterKey]?.color || 74 Object.values(newsTypeColors).find( 75 (obj) => typeof obj === "object" && obj.label === filterKey 76 )?.color || 77 newsTypeColors.All; 78 } 79 80 const active = filters.querySelector(".checked"); 81 if (!active) return; 82 active.style.fontWeight = "bolder"; 83 active.style.textDecoration = "underline"; 84 } catch (error) { 85 console.error("Error setting filter colors:", error); 86 } 87}; 88 89const setBadgeColors = () => { 90 const badges = document.querySelectorAll("p.dto-news-type-badge"); 91 for (const badge of badges) 92 badge.style.backgroundColor = 93 newsTypeColors[badge.textContent]?.color || 94 Object.values(newsTypeColors).find( 95 (color) => color.label === badge.textContent 96 )?.color || 97 newsTypeColors.All; 98}; 99 100/** 101 * Groups news entries by type and displays them in sections. 102 * Watches for changes in the original list and regroups when needed. 103 * Doing this client-side is better for cost/time, but we may consider server-side in the future. 104 */ 105const groupNewsEntries = () => { 106 if (!location.pathname.includes("/news/")) return; 107 108 try { 109 const gridContainer = document.querySelector( 110 "#primary > section.dto-page-news__news-grid > div" 111 ); 112 if (!gridContainer) return; 113 114 const originalList = gridContainer.querySelector("ul"); 115 if (!originalList) return; 116 117 const existingGroups = 118 gridContainer.querySelectorAll("section.news-group"); 119 existingGroups.forEach((group) => gridContainer.removeChild(group)); 120 121 const entries = originalList.querySelectorAll("li"); 122 if (entries.length === 0) return (originalList.style.display = ""); 123 originalList.style.display = "none"; 124 125 const entriesByType = new Map(); 126 entries.forEach((entry) => { 127 const badge = entry.querySelector("p.dto-news-type-badge"); 128 const type = badge ? badge.textContent.trim() : "Uncategorized"; 129 if (!entriesByType.has(type)) entriesByType.set(type, []);
130 entriesByType.get(type).push(entry.cloneNode(true)); 131 }); 132 133 for (const group of newsGroups) { 134 const groupName = group.label; 135 const typesInGroup = group.value; 136 const section = document.createElement("section"); 137 section.className = "news-group"; 138 section.style.marginBottom = "4rem"; 139 140 const heading = document.createElement("h1"); 141 const representativeType = typesInGroup[0]; 142 const color = 143 newsTypeColors[representativeType]?.color || 144 Object.values(newsTypeColors).find( 145 (obj) => 146 typeof obj === "object" && 147 obj.label === representativeType 148 )?.color || 149 newsTypeColors.All; 150 151 const textElement = document.createElement("span"); 152 textElement.textContent = groupName; 153 154 heading.appendChild(textElement); 155 heading.className = "news-group__heading"; 156 heading.style.marginBlock = "1rem"; 157 // heading.style.borderBottom = "1px solid " + color; 158 heading.style.borderBottom = "1px solid " + "#000"; 159 // heading.style.color = color; 160 section.appendChild(heading); 161 162 const newList = document.createElement("ul"); 163 newList.className = "dto-page-news__news-list"; 164 newList.dataset.type = groupName; 165 newList.dataset.types = typesInGroup.join(","); 166 167 let hasEntriesInGroup = false; 168 for (const type of typesInGroup) { 169 if (entriesByType.has(type)) { 170 const groupEntries = entriesByType.get(type); 171 groupEntries.forEach((entry) => newList.appendChild(entry)); 172 hasEntriesInGroup = true; 173 } 174 } 175 176 if (hasEntriesInGroup) { 177 section.appendChild(newList); 178 gridContainer.appendChild(section); 179 } 180 } 181 182 // console.log(entriesByType); 183 // console.log(newsGroups); 184 185 let uncategorizedEntries = []; 186 let uncategorizedTypeNames = []; 187 188 // Handle uncategorized entries or entries whose types are not in headingGroups 189 entriesByType.forEach((groupEntries, type) => { 190 const isInAnyHeadingGroup = Object.values(newsGroups).some( 191 (types) => types.value.includes(type) 192 ); 193 if (!isInAnyHeadingGroup) { 194 uncategorizedEntries = 195 uncategorizedEntries.concat(groupEntries); 196 uncategorizedTypeNames.push(type); 197 } 198 }); 199 200 if (uncategorizedEntries.length > 0) { 201 const section = document.createElement("section"); 202 section.className = "news-group news-group--uncategorized"; 203 section.style.marginBottom = "4rem"; 204 205 const heading = document.createElement("h1"); 206 const representativeType = uncategorizedTypeNames[0] || "All"; 207 const color = 208 newsTypeColors[representativeType]?.color || newsTypeColors.All; 209 210 const textElement = document.createElement("span"); 211 textElement.textContent = "Other News"; 212 213 heading.appendChild(textElement); 214 heading.className = "news-group__heading"; 215 heading.style.marginBlock = "1rem"; 216 // heading.style.borderBottom = "1px solid " + color; 217 heading.style.borderBottom = "1px solid " + "#000"; 218 // heading.style.color = color; 219 section.appendChild(heading); 220 221 const newList = document.createElement("ul"); 222 newList.className = "dto-page-news__news-list"; 223 uncategorizedEntries.forEach((entry) => newList.appendChild(entry)); 224 section.appendChild(newList); 225 226 gridContainer.appendChild(section); 227 } 228 } catch (error) { 229 console.error("Error grouping news entries:", error); 230 } 231}; 232 233/** 234 * Move the load more button to the end of the grid. 235 */ 236const moveLoadMoreButton = (gridContainer) => { 237 if (!gridContainer) return; 238 239 try {
240 const loadMoreButton = gridContainer.querySelector( 241 '[data-type="pager"]' 242 ); 243 if (loadMoreButton) gridContainer.appendChild(loadMoreButton); 244 } catch (error) { 245 console.error("Error moving load more button:", error); 246 } 247}; 248 249/** 250 * Limit Firm News category to latest 3 posts. 251 */ 252const limitFirmNewsCategory = () => { 253 if (!location.pathname.includes("/news/")) return; 254 255 const firmNewsCategory = document.querySelectorAll( 256 'ul.dto-page-news__news-list[data-type="Firm News and Recognition"] li' 257 ); 258 if (!firmNewsCategory) return; 259 260 const allFirmNewsPosts = Array.from(firmNewsCategory); 261 const latestThree = allFirmNewsPosts.slice(0, 3); 262 const rest = allFirmNewsPosts.slice(3); 263 for (const post of latestThree) post.style.display = "block"; 264 for (const post of rest) post.style.display = "none"; 265 266 const firmNewsList = document.querySelector( 267 'ul.dto-page-news__news-list[data-type="Firm News and Recognition"]' 268 ); 269 const firmNewsSection = firmNewsList ? firmNewsList.parentElement : null; 270 if (!firmNewsSection) return; 271 272 let viewAllButton = firmNewsSection.querySelector( 273 ".dto-view-all-firm-news" 274 ); 275 276 if (rest.length > 0) { 277 if (!viewAllButton) { 278 viewAllButton = document.createElement("button"); 279 viewAllButton.className = "dto-link-button dto-view-all-firm-news"; 280 viewAllButton.textContent = "View all news"; 281 viewAllButton.style.marginTop = "1rem"; 282 viewAllButton.style.width = "100%"; 283 firmNewsSection.appendChild(viewAllButton); 284 } else { 285 viewAllButton.style.display = ""; 286 } 287 288 viewAllButton.onclick = () => { 289 for (const post of rest) post.style.display = "block"; 290 viewAllButton.style.display = "none"; 291 }; 292 } else if (viewAllButton) { 293 viewAllButton.style.display = "none"; 294 } 295}; 296 297/** 298 * Initialize the featured news carousel. 299 */ 300const initializeFeaturedNewsCarousel = () => { 301 if (!location.pathname.includes("/news/")) return; 302 303 const carousel = document.querySelector(".dto-page-news__carousel"); 304 if (!carousel) return; 305 306 const carouselContent = carousel.querySelector( 307 ".dto-page-news__carousel-content" 308 ); 309 if (!carouselContent) return; 310 311 const carouselItems = Array.from( 312 carouselContent.querySelectorAll(".dto-page-news__carousel-item") 313 ); 314 if (carouselItems.length === 0) return; 315 316 const carouselLeftButton = carousel.querySelector( 317 ".dto-page-news__carousel-left-button button" 318 ); 319 const carouselRightButton = carousel.querySelector( 320 ".dto-page-news__carousel-right-button button" 321 ); 322 323 let currentIndex = 0; 324 325 const showSlide = (index) => { 326 for (const item of carouselItems) item.style.display = "none"; 327 carouselItems[index].style.display = "flex"; 328 }; 329 330 showSlide(currentIndex); 331 332 if (carouselLeftButton) { 333 carouselLeftButton.addEventListener("click", () => { 334 currentIndex = 335 (currentIndex - 1 + carouselItems.length) % 336 carouselItems.length; 337 showSlide(currentIndex); 338 }); 339 } 340 341 if (carouselRightButton) { 342 carouselRightButton.addEventListener("click", () => { 343 currentIndex = (currentIndex + 1) % carouselItems.length; 344 showSlide(currentIndex); 345 }); 346 } 347}; 348 349/** 350 * Run once on load and observe changes. 351 */ 352document.addEventListener("DOMContentLoaded", () => { 353 if (!location.pathname.includes("/news/")) return; 354 355 const gridContainer = document.querySelector( 356 "#primary > section.dto-page-news__news-grid > div" 357 ); 358 if (!gridContainer) return; 359 360 const originalList = gridContainer.querySelector("ul"); 361 if (!originalList) return; 362 363 const recalculate = () => { 364 try { 365 groupNewsEntries(); 366 setBadgeColors(); 367 setFilterColors();
368 moveLoadMoreButton(gridContainer); 369 limitFirmNewsCategory(); 370 } catch (error) { 371 console.error("Error recalculating news:", error); 372 } 373 }; 374 375 const observer = new MutationObserver((mutationsList, observer) => { 376 for (const mutation of mutationsList) { 377 if (mutation.type === "childList") { 378 recalculate(); 379 break; 380 } 381 } 382 }); 383 384 observer.observe(originalList, { childList: true }); 385 document.addEventListener("facetwp-loaded", recalculate); 386 initializeFeaturedNewsCarousel(); 387});
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.