1(function () { 2 'use strict'; 3 4 function getDirectHeadings(article) { 5 return Array.prototype.filter.call(article.children, function (element) { 6 return element.tagName === 'H1' || element.tagName === 'H2'; 7 }); 8 } 9 10 function getAllHeadings(article) { 11 return Array.prototype.slice.call(article.querySelectorAll('h2')); 12 } 13 14 function appearsBefore(firstElement, secondElement) { 15 return firstElement !== secondElement && Boolean( 16 firstElement.compareDocumentPosition(secondElement) & window.Node.DOCUMENT_POSITION_FOLLOWING 17 ); 18 } 19 20 function isBetweenHeadings(element, startHeading, endHeading) { 21 return appearsBefore(startHeading, element) && (!endHeading || appearsBefore(element, endHeading)); 22 } 23 24 function getTopLevelArticleBlock(element, article) { 25 var block = element; 26 27 while (block && block.parentElement && block.parentElement !== article) { 28 block = block.parentElement; 29 } 30 31 return block && block.parentElement === article ? block : null; 32 } 33 34 function createUniqueId(text, index) { 35 var base = text 36 .toLowerCase() 37 .normalize('NFD') 38 .replace(/[\u0300-\u036f]/g, '') 39 .replace(/[^a-z0-9]+/g, '-') 40 .replace(/^-+|-+$/g, '') || 'article-section-' + index; 41 var id = base; 42 var suffix = 2; 43 44 while (document.getElementById(id)) { 45 id = base + '-' + suffix; 46 suffix += 1; 47 } 48 49 return id; 50 } 51 52 function normalizeComparableText(text) { 53 return (text || '').replace(/\s+/g, ' ').trim().toLowerCase(); 54 } 55 56 function getArticleHeadings(article) { 57 return Array.prototype.filter.call(article.querySelectorAll('h1, h2'), function (heading) { 58 return !heading.closest('.estable-article-meta, .estable-feature-strip, .estable-faq-list, .estable-info-grid'); 59 }); 60 } 61 62 function getTextNodes(root) { 63 var walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT); 64 var nodes = []; 65 var node; 66 67 while ((node = walker.nextNode())) { 68 nodes.push(node); 69 } 70 71 return nodes; 72 } 73 74 function getTextPosition(root, characterOffset) { 75 var nodes = getTextNodes(root); 76 var traversed = 0; 77 var position = null; 78 79 nodes.some(function (node) { 80 var next = traversed + node.data.length; 81 82 if (characterOffset <= next) { 83 position = { 84 node: node, 85 offset: Math.max(0, Math.min(node.data.length, characterOffset - traversed)) 86 }; 87 return true; 88 } 89 90 traversed = next; 91 return false; 92 }); 93 94 if (!position && nodes.length) { 95 position = { node: nodes[nodes.length - 1], offset: nodes[nodes.length - 1].data.length }; 96 } 97 98 return position; 99 } 100 101 function enhanceHeading(heading) { 102 heading.removeAttribute('style'); 103 Array.prototype.forEach.call(heading.querySelectorAll('[style]'), function (element) { 104 element.removeAttribute('style'); 105 }); 106 107 if (heading.classList.contains('estable-heading-enhanced') || heading.classList.contains('estable-no-accent')) { 108 return; 109 } 110 111 var fullText = heading.textContent; 112 var wordMatches = Array.from(fullText.matchAll(/\S+/g)); 113 var requestedCount = parseInt(heading.getAttribute('data-accent-words'), 10); 114 var accentCount = Number.isFinite(requestedCount) ? requestedCount : 2; 115 116 accentCount = Math.max(1, Math.min(accentCount, wordMatches.length - 1)); 117 if (wordMatches.length < 3) { 118 return; 119 } 120 121 var firstAccentWord = wordMatches[wordMatches.length - accentCount]; 122 var lastWord = wordMatches[wordMatches.length - 1]; 123 var start = getTextPosition(heading, firstAccentWord.index); 124 var end = getTextPosition(heading, lastWord.index + lastWord[0].length); 125 var range; 126 var accentSpan = document.createElement('span'); 127 accentSpan.className = 'estable-heading-accent'; 128 129 if (!start || !end) { 130 return; 131 } 132 133 range = document.createRange(); 134 range.setStart(start.node, start.offset); 135 range.setEnd(end.node, end.offset); 136 accentSpan.appendChild(range.extractContents());
137 range.insertNode(accentSpan); 138 heading.classList.add('estable-heading-enhanced'); 139 } 140 141 function enhanceHeadings(article) { 142 Array.prototype.forEach.call(article.querySelectorAll('h1, h2'), enhanceHeading); 143 } 144 145 function normalizeLegacyContentStyles(article) { 146 var finalParagraph = article.lastElementChild; 147 148 if (!finalParagraph || finalParagraph.tagName !== 'P') { 149 return; 150 } 151 152 [finalParagraph].concat(Array.prototype.slice.call(finalParagraph.querySelectorAll('[style]'))).forEach(function (element) { 153 element.style.removeProperty('color'); 154 if (!element.getAttribute('style')) { 155 element.removeAttribute('style'); 156 } 157 }); 158 } 159 160 function classifyStandaloneCta(article) { 161 var finalParagraph = article.lastElementChild; 162 var controls; 163 var control; 164 var paragraphText; 165 var controlText; 166 167 if (!finalParagraph || finalParagraph.tagName !== 'P') { 168 return; 169 } 170 171 controls = finalParagraph.querySelectorAll('a, button'); 172 173 if (controls.length !== 1) { 174 return; 175 } 176 177 control = controls[0]; 178 paragraphText = finalParagraph.textContent.replace(/\s+/g, ' ').trim(); 179 controlText = control.textContent.replace(/\s+/g, ' ').trim(); 180 181 /* A normal sentence containing a link remains a normal sentence. */ 182 if (!controlText || paragraphText !== controlText) { 183 return; 184 } 185 186 finalParagraph.classList.add('estable-cta-row'); 187 } 188 189 function enhanceCardLists(article) { 190 var lists = article.querySelectorAll('ul:not(.list-inline):not(.estable-info-grid)'); 191 192 Array.prototype.forEach.call(lists, function (list) { 193 var items = Array.prototype.slice.call(list.children); 194 if (items.length < 3 || items.length > 4 || items.some(function (item) { return item.tagName !== 'LI'; })) { 195 return; 196 } 197 198 var cardData = items.map(function (item) { 199 var text = item.textContent.replace(/^[\s\u2713\u2714\u2022-]+/, '').trim(); 200 var divider = text.indexOf(':'); 201 202 if (divider < 2 || divider >= text.length - 2) { 203 return null; 204 } 205 206 return { 207 title: text.slice(0, divider).trim(), 208 body: text.slice(divider + 1).trim() 209 }; 210 }); 211 212 if (cardData.some(function (card) { return !card; })) { 213 return; 214 } 215 216 list.classList.add('estable-info-grid'); 217 items.forEach(function (item, index) { 218 var title = document.createElement('span'); 219 var body = document.createElement('span'); 220 221 title.className = 'estable-info-card-title'; 222 title.textContent = cardData[index].title; 223 body.className = 'estable-info-card-body'; 224 body.textContent = cardData[index].body; 225 226 item.textContent = ''; 227 item.classList.add('estable-info-card'); 228 item.appendChild(title); 229 item.appendChild(body); 230 }); 231 }); 232 } 233 234 function enhanceLongChecklists(article) { 235 var lists = article.querySelectorAll('ul:not(.list-inline):not(.estable-info-grid)'); 236 237 Array.prototype.forEach.call(lists, function (list) { 238 var items = Array.prototype.slice.call(list.children).filter(function (item) { 239 return item.tagName === 'LI'; 240 }); 241 242 if (items.length <= 6 || items.length !== list.children.length) { 243 return; 244 } 245 246 var columns = Math.min(3, Math.ceil(items.length / 6)); 247 var rows = Math.ceil(items.length / columns); 248 249 list.classList.add('estable-long-checklist'); 250 list.style.setProperty('--estable-checklist-columns', columns); 251 list.style.setProperty('--estable-checklist-rows', rows); 252 }); 253 } 254 255 function addOptionalPanelImage(panel, rawImage) { 256 if (!rawImage) { 257 return; 258 } 259 260 try { 261 var imageUrl = new URL(rawImage, window.location.href); 262 if (imageUrl.protocol !== 'http:' && imageUrl.protocol !== 'https:') { 263 return; 264 } 265 266 panel.style.setProperty('--estable-panel-image', 'url("' + encodeURI(imageUrl.href).replace(/"/g, '%22') + '")'); 267 panel.classList.add('has-image'); 268 } catch (error) { 269 // Invalid optional images simply fall back to the color background. 270 } 271 } 272 273 function getSafePanelLink(rawLink, fallback) { 274 if (!rawLink) { 275 return fallback; 276 } 277 278 if (rawLink.charAt(0) === '#') { 279 return rawLink; 280 } 281 282 try { 283 var linkUrl = new URL(rawLink, window.location.href); 284 if (linkUrl.protocol === 'http:' || linkUrl.protocol === 'https:') { 285 return linkUrl.href; 286 } 287 } catch (error) { 288 // Invalid optional links keep the safe in-page destination. 289 } 290 291 return fallback; 292 } 293 294 function getFeatureVisualSelector() { 295 return [ 296 'table', 297 'figure', 298 'img', 299 'ul:not(.list-inline)', 300 'ol', 301 'blockquote', 302 'details', 303 '.estable-faq-list', 304 '.estable-info-grid', 305 '.estable-info-card', 306 '.wp-block-buttons', 307 '.wp-block-button', 308 '.estable-cta-row', 309 '.card', 310 '[class*="cards"]', 311 '.button', 312 'button', 313 'iframe', 314 'video', 315 'audio', 316 'input', 317 'select', 318 'textarea', 319 '[class*="slider"]', 320 '[class*="carousel"]', 321 '[class*="gallery"]' 322 ].join(', '); 323 } 324 325 function findTextOnlyFeatureSection(article, headings, preferredHeading) { 326 var visualSelector = getFeatureVisualSelector(); 327 var paragraphs = Array.prototype.slice.call(article.querySelectorAll('p')).filter(function (paragraph) { 328 return paragraph.textContent.trim() && 329 !paragraph.closest('.estable-article-lead, .author-card, .estable-faq-list, .estable-info-grid, .estable-feature-strip'); 330 }); 331 var visuals = Array.prototype.slice.call(article.querySelectorAll(visualSelector)).filter(function (element) { 332 return !element.closest('.estable-article-lead, .author-card, .estable-feature-strip'); 333 }); 334 var preferredIndex = Math.max(0, headings.indexOf(preferredHeading)); 335 var indexes = []; 336 337 for (var offset = 0; offset < headings.length; offset += 1) { 338 indexes.push((preferredIndex + offset) % headings.length); 339 } 340 341 for (var position = 0; position < indexes.length; position += 1) { 342 var index = indexes[position]; 343 var heading = headings[index]; 344 var nextHeading = headings[index + 1] || null; 345 346 if (heading.classList.contains('estable-no-widget') || 347 heading.classList.contains('estable-lead-title') || 348 heading.classList.contains('estable-faq-heading')) { 349 continue; 350 } 351 352 var sectionParagraphs = paragraphs.filter(function (paragraph) { 353 return isBetweenHeadings(paragraph, heading, nextHeading); 354 }); 355 var hasVisual = visuals.some(function (element) { 356 return isBetweenHeadings(element, heading, nextHeading); 357 }); 358 359 if (sectionParagraphs.length && !hasVisual) { 360 return { 361 heading: heading, 362 nextHeading: nextHeading, 363 lastParagraph: sectionParagraphs[sectionParagraphs.length - 1] 364 }; 365 } 366 } 367 368 return null; 369 } 370 371 function insertFeatureStripInSection(article, strip, section) { 372 if (section.nextHeading) { 373 var sharedContainer = section.heading.parentElement; 374 375 while (sharedContainer && sharedContainer !== article && !sh
375aredContainer.contains(section.nextHeading)) { 376 sharedContainer = sharedContainer.parentElement; 377 } 378 379 sharedContainer = sharedContainer || article; 380 381 var nextSectionBlock = section.nextHeading; 382 while (nextSectionBlock.parentElement && nextSectionBlock.parentElement !== sharedContainer) { 383 nextSectionBlock = nextSectionBlock.parentElement; 384 } 385 386 sharedContainer.insertBefore(strip, nextSectionBlock); 387 return; 388 } 389 390 var lastSectionBlock = getTopLevelArticleBlock(section.lastParagraph, article) || 391 getTopLevelArticleBlock(section.heading, article); 392 393 article.insertBefore(strip, lastSectionBlock ? lastSectionBlock.nextSibling : null); 394 } 395 396 function buildFeatureStrip(article) { 397 if (article.querySelector('.estable-feature-strip')) { 398 return; 399 } 400 401 var headings = getAllHeadings(article); 402 var finalHeading = headings[headings.length - 1]; 403 var selected = headings.filter(function (heading) { 404 return heading !== finalHeading && 405 !heading.classList.contains('estable-no-widget') && 406 !heading.classList.contains('estable-lead-title') && 407 !heading.classList.contains('estable-faq-heading'); 408 }).slice(0, 3); 409 410 if (selected.length < 3) { 411 return; 412 } 413 414 var insertionSection = findTextOnlyFeatureSection(article, headings, selected[2]); 415 416 if (!insertionSection) { 417 return; 418 } 419 420 var subtitles = ['Explore the benefits', 'Compare your options', 'Review the steps']; 421 var strip = document.createElement('nav'); 422 strip.className = 'estable-feature-strip'; 423 strip.setAttribute('aria-label', 'Featured article topics'); 424 425 selected.forEach(function (heading, index) { 426 var headingText = heading.textContent.trim(); 427 var panel = document.createElement('a'); 428 var subtitle = document.createElement('span'); 429 430 if (!heading.id) { 431 heading.id = createUniqueId(headingText, index + 1); 432 } 433 434 panel.className = 'estable-feature-panel'; 435 panel.href = getSafePanelLink(heading.getAttribute('data-widget-link'), '#' + heading.id); 436 subtitle.className = 'estable-feature-subtitle'; 437 subtitle.textContent = heading.getAttribute('data-widget-subtitle') || subtitles[index]; 438 439 addOptionalPanelImage(panel, heading.getAttribute('data-widget-image')); 440 panel.appendChild(subtitle); 441 strip.appendChild(panel); 442 }); 443 444 insertFeatureStripInSection(article, strip, insertionSection); 445 } 446 447 function restoreLeadSummary(lead) { 448 var summary = lead.querySelector('.estable-lead-summary'); 449 var continuation = lead.querySelector('.estable-lead-continuation'); 450 var originalHtml = summary ? summary.getAttribute('data-estable-original-html') : null; 451 452 if (!summary || !continuation || originalHtml === null) { 453 return; 454 } 455 456 summary.innerHTML = originalHtml; 457 summary.hidden = false; 458 continuation.innerHTML = ''; 459 continuation.hidden = true; 460 } 461 462 function cloneSummaryRange(summary, startOffset, endOffset) { 463 var start = getTextPosition(summary, startOffset); 464 var end = getTextPosition(summary, endOffset); 465 var range; 466 467 if (!start || !end) { 468 return document.createDocumentFragment(); 469 } 470 471 range = document.createRange(); 472 range.setStart(start.node, start.offset); 473 range.setEnd(end.node, end.offset); 474 return range.cloneContents(); 475 } 476 477 function showSummaryPrefix(summary, endOffset) { 478 var prefix; 479 480 summary.innerHTML = summary.getAttribute('data-estable-original-html') || ''; 481 prefix = cloneSummaryRange(summary, 0, endOffset); 482 summary.innerHTML = ''; 483 summary.appendChild(prefix); 484 summary.hidden = false; 485 } 486 487 function balanceArticleLead(lead) { 488 var copy = lead.querySelector('.estable-article-lead-copy'); 489 var summary = lead.querySelector('.estable-lead-summary'); 490 var continuation = lead.querySelector('.estable-lead-continuation'); 491 var mediaBlock = lead.querySelector('.estable-article-lead-media'); 492 var image = lead.querySelector('.estable-article-lead-media img'); 493 494 restoreLeadSummary(lead); 495 496 if (!copy || !summary || !continuation || !image || window.matchMedia('(max-width: 991.98px)').matches) { 497 return; 498 } 499 500 var imageRect = image.getBoundingClientRect(); 501 var mediaRect = mediaBlock ? mediaBlock.getBoundingClientRect() : imageRect; 502 var imageHeight = imageRect.height; 503 var imageBottom = mediaRect.bottom || imageRect.bottom; 504 if (!imageHeight || copy.getBoundingClientRect().bottom <= imageBottom + 1) { 505 return; 506 } 507
508 var originalText = summary.getAttribute('data-estable-original-text') || summary.textContent; 509 var wordMatches = Array.from(originalText.matchAll(/\S+/g)); 510 var low = 0; 511 var high = wordMatches.length; 512 var middle; 513 var splitOffset; 514 var prefix; 515 var suffix; 516 517 while (low < high) { 518 middle = Math.ceil((low + high) / 2); 519 showSummaryPrefix(summary, wordMatches[middle - 1].index + wordMatches[middle - 1][0].length); 520 521 if (copy.getBoundingClientRect().bottom <= imageBottom + 1) { 522 low = middle; 523 } else { 524 high = middle - 1; 525 } 526 } 527 528 summary.innerHTML = summary.getAttribute('data-estable-original-html') || ''; 529 530 if (!low) { 531 continuation.innerHTML = summary.getAttribute('data-estable-original-html') || ''; 532 summary.innerHTML = ''; 533 summary.hidden = true; 534 continuation.hidden = false; 535 return; 536 } 537 538 splitOffset = wordMatches[low - 1].index + wordMatches[low - 1][0].length; 539 prefix = cloneSummaryRange(summary, 0, splitOffset); 540 suffix = cloneSummaryRange(summary, splitOffset, originalText.length); 541 summary.innerHTML = ''; 542 summary.appendChild(prefix); 543 summary.hidden = false; 544 continuation.innerHTML = ''; 545 continuation.appendChild(suffix); 546 continuation.hidden = !continuation.textContent.trim(); 547 } 548 549 function balanceArticleLeads() { 550 var leads = document.querySelectorAll('.estable-article-lead'); 551 552 Array.prototype.forEach.call(leads, balanceArticleLead); 553 } 554 555 function isBeforeBoundary(element, boundary) { 556 return !boundary || appearsBefore(element, boundary); 557 } 558 559 function findOpeningParagraph(article, startElement, boundary) { 560 return Array.prototype.find.call(article.querySelectorAll('p'), function (paragraph) { 561 return (!startElement || appearsBefore(startElement, paragraph)) && 562 isBeforeBoundary(paragraph, boundary) && 563 !paragraph.querySelector('img') && 564 !paragraph.closest('.estable-article-meta, .author-card, .estable-feature-strip, .estable-faq-list, .estable-info-grid, figure, figcaption') && 565 paragraph.textContent.trim().length > 30; 566 }) || null; 567 } 568 569 function findOpeningImageBlock(article, startElement, boundary) { 570 var image = Array.prototype.find.call(article.querySelectorAll('figure img, .wp-block-image img, p img, img'), function (candidate) { 571 return (!startElement || appearsBefore(startElement, candidate)) && 572 isBeforeBoundary(candidate, boundary) && 573 isEligibleOpeningImage(candidate) && 574 !candidate.closest('.estable-article-meta, .author-card, .estable-feature-strip, .estable-faq-list, .estable-info-grid, figure.wp-block-table'); 575 }); 576 577 return image ? (image.closest('figure, .wp-block-image, p') || image) : null; 578 } 579 580 function getImageDimensions(image) { 581 var rect = image.getBoundingClientRect(); 582 var width = image.naturalWidth || parseFloat(image.getAttribute('width')) || rect.width || 0; 583 var height = image.naturalHeight || parseFloat(image.getAttribute('height')) || rect.height || 0; 584 585 return { width: width, height: height }; 586 } 587 588 function isEligibleOpeningImage(image) { 589 var hint = [image.className, image.id, image.alt, image.getAttribute('src') || ''].join(' ').toLowerCase(); 590 var dimensions = getImageDimensions(image); 591 592 if (/infograph|infograf|portrait|vertical/.test(hint)) { 593 return false; 594 } 595 596 if (!dimensions.width || !dimensions.height) { 597 return false; 598 } 599 600 return dimensions.width >= 480 && dimensions.width / dimensions.height >= 1.2; 601 } 602 603 function ensurePrimaryArticleTitle(article) { 604 var postTitle = (article.getAttribute('data-estable-post-title') || '').replace(/\s+/g, ' ').trim(); 605 var headings; 606 var primary; 607 608 if (!postTitle) { 609 return null; 610 } 611 612 headings = getArticleHeadings(article); 613 primary = headings[0] && 614 normalizeComparableText(headings[0].textContent) === normalizeComparableText(postTitle) ? 615 headings[0] : null; 616 617 if (!primary) { 618 return null; 619 } 620 621 primary.classList.add('estable-opening-primary-title'); 622 623 if (!primary.id) { 624 primary.id = createUniqueId(postTitle, 0); 625 } 626 627 enhanceHeading(primary); 628 return primary; 629 } 630 631 function isOpeningDateText(text) { 632 return /(?:^|\b)(?:last\s+updated?|updated\s+(?:on|for)\b|updated\s+regularly\b)/i.test((text || '').trim()); 633 } 634 635 function isOpeningAuthorText(text) { 636 var value = (text || '').trim(); 637 return /\beditorial\s+team\b/i.test(value) || /^by\s*:/i.test(value) || /^by\s+[A-Z]/.test(value); 638 } 639
640 function isOpeningEditorialNoteText(text) { 641 return /^(?:educational\s+content|reviewed\s+by|fact[-\s]?checked\s+by)\b/i.test((text || '').trim()); 642 } 643 644 function isOpeningMetadataText(text) { 645 return isOpeningDateText(text) || isOpeningAuthorText(text) || isOpeningEditorialNoteText(text); 646 } 647 648 function isSafeOpeningMetadataNode(element) { 649 var text = element.textContent.replace(/\s+/g, ' ').trim(); 650 var classHint = (element.className || '').toString().toLowerCase(); 651 var hasEditorialClass = /author|byline|editorial|ya-card/.test(classHint); 652 var isAuthorContainer = hasEditorialClass && 653 (isOpeningAuthorText(text) || /\bauthor\b/i.test(text)); 654 655 if (!text || text.length > 1100 || (!isOpeningMetadataText(text) && !isAuthorContainer) || 656 element.closest('.estable-article-lead, .estable-feature-strip, .estable-faq-list, .estable-info-grid')) { 657 return false; 658 } 659 660 if (isAuthorContainer && !element.querySelector('h1, h2, h3, iframe, video, table, ul, ol, form')) { 661 return true; 662 } 663 664 if (element.querySelector('h1, h2, h3, img, picture, iframe, video, table, ul, ol, form')) { 665 return false; 666 } 667 668 if (element.matches('p, time, details, .author-card')) { 669 return true; 670 } 671 672 if (element.matches('div, span, small, em, i') && isOpeningDateText(text) && text.length < 240 && 673 !element.querySelector('p, div, section, article, details')) { 674 return true; 675 } 676 677 return hasEditorialClass || (isOpeningAuthorText(text) && (isOpeningDateText(text) || isOpeningEditorialNoteText(text))); 678 } 679 680 function getOpeningMetadataNodes(article) { 681 var candidates = Array.prototype.slice.call(article.querySelectorAll( 682 '.author-card, details, time, p, small, em, i, [class*="date"], [class*="updated"], [class*="author"], [class*="byline"], [class*="editorial"], [class*="ya-card"], div, section' 683 )).filter(function (candidate) { 684 return !candidate.closest('.estable-article-meta') && isSafeOpeningMetadataNode(candidate); 685 }); 686 687 return candidates.filter(function (candidate) { 688 return !candidates.some(function (possibleParent) { 689 return possibleParent !== candidate && possibleParent.contains(candidate) && 690 /^(?:DIV|SECTION|DETAILS)$/.test(possibleParent.tagName); 691 }); 692 }); 693 } 694 695 function hasExpandableAuthorContent(details, summary) { 696 return Array.prototype.some.call(details.childNodes, function (node) { 697 if (node === summary) { 698 return false; 699 } 700 701 if (node.nodeType === 3) { 702 return node.textContent.trim().length > 0; 703 } 704 705 return node.nodeType === 1 && ( 706 node.textContent.trim().length > 0 || 707 node.querySelector('img, picture, video, audio, iframe, table, ul, ol') 708 ); 709 }); 710 } 711 712 function normalizeExpandableAuthorCard(authorCard) { 713 var details = authorCard.matches('details') ? authorCard : authorCard.querySelector('details'); 714 var summary; 715 var spans; 716 var name; 717 var role; 718 var identity; 719 var label; 720 var byNode; 721 var summaryText; 722 var roleMatch; 723 var expandable; 724 725 if (!details) { 726 authorCard.classList.add('estable-author-plain'); 727 authorCard.classList.remove('estable-author-static'); 728 return; 729 } 730 731 authorCard.classList.remove('estable-author-plain'); 732 summary = Array.prototype.find.call(details.children, function (child) { 733 return child.tagName === 'SUMMARY'; 734 }) || details.querySelector('summary'); 735 736 if (!summary) { 737 authorCard.classList.add('estable-author-static'); 738 return; 739 } 740 741 identity = summary.querySelector('.estable-author-identity'); 742 if (!identity) { 743 spans = Array.prototype.filter.call(summary.children, function (child) { 744 return child.tagName === 'SPAN' && !child.classList.contains('caret'); 745 }); 746 name = spans[0] || null; 747 role = spans[1] || null; 748 749 byNode = Array.prototype.find.call(authorCard.querySelectorAll('div, p, span'), function (candidate) { 750 return !summary.contains(candidate) && !candidate.contains(details) && /^by\s*:?$/i.test(candidate.textContent.trim()); 751 }) || null; 752 753 if (!name) { 754 summaryText = summary.textContent.replace(/\s+/g, ' ').trim(); 755 roleMatch = summaryText.match(/(\([^)]*\)|—\s*.+)$/); 756 name = document.createElement('span'); 757 name.textContent = summaryText 758 .replace(/\bby\s*:?\s*/ig, '') 759 .replace(roleMatch ? roleMatch[0] : '', '') 760 .trim(); 761 762 if (roleMatch) { 763 role = document.createElement('span'); 764 role.textContent = roleMatch[0].trim(); 765 } 766 767 summary.textContent = ''; 768 } 769 770 identity = document.createElement('span'); 771 identity.className = 'estable-author-identity'; 772 label = document.createElement('span'); 773 label.className = 'estable-author-by-label'; 774 label.textContent = 'By:'; 775 name.classList.add('estable-author-name'); 776 identity.appendChild(label); 777 identity.appendChild(name); 778 summary.insertBefore(identity, summary.firstChild); 779 780 if (role) { 781 role.classList.add('estable-author-role'); 782 summary.appendChild(role); 783 } 784 785 if (byNode) { 786 byNode.remove(); 787 } 788 } 789 790 if (identity) { 791 Array.prototype.forEach.call(summary.childNodes, function (node) { 792 if (node.nodeType === 3 && /^by\s*:?$/i.test(node.textContent.trim())) { 793 summary.removeChild(node); 794 } 795 }); 796 } 797 798 expandable = hasExpandableAuthorContent(details, summary); 799 authorCard.classList.toggle('estable-author-static', !expandable);
800 details.classList.toggle('estable-author-static-details', !expandable); 801 802 if (!expandable) { 803 details.open = false; 804 } 805 } 806 807 function normalizeAuthorCardPresentation(authorCard) { 808 [authorCard].concat(Array.prototype.slice.call(authorCard.querySelectorAll('[style]'))).forEach(function (element) { 809 element.removeAttribute('style'); 810 }); 811 812 Array.prototype.forEach.call(authorCard.querySelectorAll('style'), function (style) { 813 style.remove(); 814 }); 815 816 normalizeExpandableAuthorCard(authorCard); 817 } 818 819 function normalizeOpeningMetadata(article, primaryTitle) { 820 var existing = article.querySelector('.estable-article-meta'); 821 var nodes; 822 var dateNodes; 823 var editorialNodes; 824 var meta; 825 var dateWrap; 826 var authorCard; 827 var anchor; 828 829 nodes = getOpeningMetadataNodes(article); 830 dateNodes = nodes.filter(function (element) { 831 return isOpeningDateText(element.textContent) && !isOpeningAuthorText(element.textContent) && 832 !isOpeningEditorialNoteText(element.textContent); 833 }); 834 editorialNodes = nodes.filter(function (element) { 835 return dateNodes.indexOf(element) === -1; 836 }); 837 838 if (!existing && !nodes.length) { 839 return null; 840 } 841 842 meta = existing || document.createElement('div'); 843 meta.classList.add('estable-article-meta'); 844 845 if (dateNodes.length) { 846 dateWrap = meta.querySelector(':scope > .estable-meta-date') || document.createElement('div'); 847 dateWrap.classList.add('estable-meta-date'); 848 dateNodes.forEach(function (node) { 849 node.removeAttribute('style'); 850 dateWrap.appendChild(node); 851 }); 852 meta.insertBefore(dateWrap, meta.firstChild); 853 } 854 855 authorCard = meta.querySelector(':scope > .author-card'); 856 857 if (!authorCard && editorialNodes.length === 1 && editorialNodes[0].classList.contains('author-card')) { 858 authorCard = editorialNodes[0]; 859 } else if (!authorCard && editorialNodes.length) { 860 authorCard = document.createElement('div'); 861 authorCard.className = 'author-card'; 862 } 863 864 if (authorCard && editorialNodes.length) { 865 editorialNodes.forEach(function (node) { 866 if (node === authorCard || authorCard.contains(node)) { 867 return; 868 } 869 node.removeAttribute('style'); 870 authorCard.appendChild(node); 871 }); 872 } 873 874 if (authorCard) { 875 authorCard.classList.add('author-card'); 876 normalizeAuthorCardPresentation(authorCard); 877 if (authorCard.parentElement !== meta) { 878 meta.appendChild(authorCard); 879 } 880 } 881 882 anchor = primaryTitle ? getTopLevelArticleBlock(primaryTitle, article) : null; 883 if (meta.parentElement !== article || (anchor && anchor.parentElement === article && meta.nextElementSibling !== anchor)) { 884 article.insertBefore(meta, anchor && anchor.parentElement === article ? anchor : article.firstChild); 885 } 886 return meta; 887 } 888 889 function buildFeaturedImageBlock(article) { 890 var rawImage = article.getAttribute('data-estable-featured-image'); 891 var imageUrl; 892 var figure; 893 var image; 894 895 if (!rawImage) { 896 return null; 897 } 898 899 try { 900 imageUrl = new URL(rawImage, window.location.href); 901 if (imageUrl.protocol !== 'http:' && imageUrl.protocol !== 'https:') { 902 return null; 903 } 904 } catch (error) { 905 return null; 906 } 907 908 figure = document.createElement('figure'); 909 image = document.createElement('img'); 910 image.src = imageUrl.href; 911 image.alt = article.getAttribute('data-estable-featured-image-alt') || ''; 912 image.decoding = 'async'; 913 figure.appendChild(image); 914 915 return figure; 916 } 917 918 function removeEmptyOpeningContainer(container, article) { 919 var current = container; 920 921 while (current && current !== article && 922 !current.textContent.trim() && 923 !current.querySelector('img, video, audio, iframe, input, select, textarea, button')) { 924 var parent = current.parentElement; 925 current.remove(); 926 current = parent; 927 } 928 } 929 930 function buildArticleLead(article) { 931 if (article.querySelector('.estable-article-lead')) { 932 return; 933 } 934 935 var primaryTitle = article.querySelector('.estable-opening-primary-title'); 936 var headings = getArticleHeadings(article).filter(function (heading) { 937 return heading !== primaryTitle && !heading.classList.contains('estable-generated-primary-title'); 938 }); 939 var leadHeading = headings[0] || null; 940 var headingText = leadHeading ? normalizeComparableText(leadHeading.textContent) : ''; 941 var openingBoundary = headings[1] || null; 942 var startElement = leadHeading || primaryTitle || null; 943 var introParagraph; 944 var nextParagraph; 945 var imageBlock; 946 var imageBoundary; 947 var leadAnchor; 948 var introOriginalParent; 949 var imageOriginalParent; 950 var leadMarker; 951 952 if (leadHeading && (leadHeading.classList.contains('estable-no-lead') || headingText === 'faq' || headingText.indexOf('frequently asked question') !== -1)) { 953 return; 954 } 955 956 introParagraph = findOpeningParagraph(article, startElement, openingBoundary); 957 958 if (!introParagraph) { 959 return; 960 } 961 962 nextParagraph = findOpeningParagraph(article, introParagraph, openingBoundary); 963 imageBoundary = nextParagraph || openingBoundary; 964 imageBlock = findOpeningImageBlock(article, startElement, imageBoundary) || buildFeaturedImageBlock(article); 965 966 introOriginalParent = introParagraph.parentElement; 967 imageOriginalParent = imageBlock && imageBlock.parentElement ? imageBlock.parentElement : null; 968 leadAnchor = getTopLevelArticleBlock(leadHeading || introParagraph, article); 969 leadMarker = document.createComment('estable-article-lead'); 970 article.insertBefore(leadMarker, leadAnchor || article.firstChild); 971 972 var lead = document.createElement('section'); 973 var copy = document.createElement('div'); 974 var continuation = document.createElement('p'); 975 976 lead.className = 'estable-article-lead'; 977 copy.className = 'estable-article-lead-copy'; 978 introParagraph.classList.add('estable-lead-summary');
979 introParagraph.setAttribute('data-estable-original-html', introParagraph.innerHTML); 980 introParagraph.setAttribute('data-estable-original-text', introParagraph.textContent.trim()); 981 continuation.className = 'estable-lead-continuation'; 982 continuation.hidden = true; 983 984 if (leadHeading) { 985 if (!leadHeading.id) { 986 leadHeading.id = createUniqueId(leadHeading.textContent, 1); 987 } 988 leadHeading.classList.add('estable-lead-title', 'is-visible'); 989 lead.setAttribute('aria-labelledby', leadHeading.id); 990 copy.appendChild(leadHeading); 991 } 992 993 copy.appendChild(introParagraph); 994 lead.appendChild(copy); 995 996 if (imageBlock) { 997 imageBlock.classList.add('estable-article-lead-media'); 998 lead.appendChild(imageBlock); 999 } else { 1000 lead.classList.add('without-image'); 1001 } 1002 1003 lead.appendChild(continuation); 1004 1005 article.insertBefore(lead, leadMarker); 1006 leadMarker.remove(); 1007 removeEmptyOpeningContainer(introOriginalParent, article); 1008 removeEmptyOpeningContainer(imageOriginalParent, article); 1009 1010 var leadImage = lead.querySelector('.estable-article-lead-media img'); 1011 if (leadImage && !leadImage.complete) { 1012 leadImage.addEventListener('load', function () { 1013 balanceArticleLead(lead); 1014 }, { once: true }); 1015 } 1016 1017 window.requestAnimationFrame(function () { 1018 balanceArticleLead(lead); 1019 }); 1020 } 1021 1022 function classifyArticleMedia(article) { 1023 Array.prototype.forEach.call(article.querySelectorAll('img'), function (image) { 1024 var classify = function () { 1025 var ratio; 1026 1027 if (image.closest('.estable-article-lead-media, .estable-article-meta, .author-card, .estable-feature-strip, .estable-faq-list, .estable-info-grid') || 1028 (image.naturalWidth < 240 && image.naturalHeight < 180)) { 1029 return; 1030 } 1031 1032 ratio = image.naturalWidth ? image.naturalHeight / image.naturalWidth : 0; 1033 image.classList.add('estable-centered-media'); 1034 1035 if (ratio >= 2 && image.naturalHeight >= 700) { 1036 image.classList.add('estable-tall-infographic'); 1037 } else if (ratio >= 1.35 && image.naturalHeight >= 600) { 1038 image.classList.add('estable-portrait-media'); 1039 } 1040 1041 var wrapper = image.closest('figure, .wp-block-image, p'); 1042 if (wrapper && !wrapper.matches('figure.wp-block-table')) { 1043 wrapper.classList.add('estable-centered-media-wrap'); 1044 } 1045 }; 1046 1047 if (image.complete) { 1048 classify(); 1049 } else { 1050 image.addEventListener('load', classify, { once: true }); 1051 } 1052 }); 1053 1054 Array.prototype.forEach.call(article.querySelectorAll('iframe[src*="youtube"], iframe[src*="vimeo"], video'), function (media) { 1055 media.classList.add('estable-centered-media'); 1056 var wrapper = media.closest('figure, .wp-block-embed__wrapper, .wp-block-video, .embed-responsive, .video-container'); 1057 if (wrapper) { 1058 wrapper.classList.add('estable-centered-media-wrap'); 1059 } 1060 }); 1061 } 1062 1063 var h3RevealObserver = null; 1064 1065 function getH3RevealObserver() { 1066 if (h3RevealObserver || typeof window.IntersectionObserver === 'undefined') { 1067 return h3RevealObserver; 1068 } 1069 1070 h3RevealObserver = new IntersectionObserver(function (entries) { 1071 entries.forEach(function (entry) { 1072 if (!entry.isIntersecting) { 1073 return; 1074 } 1075 1076 entry.target.classList.add('is-visible'); 1077 h3RevealObserver.unobserve(entry.target); 1078 }); 1079 }, { 1080 threshold: 0.12 1081 }); 1082 1083 return h3RevealObserver; 1084 } 1085 1086 function enhanceArticleH3s(article) { 1087 var reduceMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches; 1088 var observer = reduceMotion ? null : getH3RevealObserver(); 1089 var excludedContainers = '.estable-faq-list, .estable-info-grid, .estable-info-card, .estable-article-lead, .estable-feature-strip, .author-card, details, summary, blockquote, .button'; 1090 1091 Array.prototype.forEach.call(article.querySelectorAll('h3'), function (heading) { 1092 if (!heading.textContent.trim() || heading.closest(excludedContainers
1092)) { 1093 return; 1094 } 1095 1096 heading.removeAttribute('style'); 1097 heading.classList.add('estable-section-h3'); 1098 1099 if (observer) { 1100 observer.observe(heading); 1101 } else { 1102 heading.classList.add('is-visible'); 1103 } 1104 }); 1105 } 1106 1107 function enhanceFaq(article) { 1108 if (article.querySelector('.estable-faq-list')) { 1109 return; 1110 } 1111 1112 var headings = getAllHeadings(article); 1113 var faqHeading = headings.find(function (heading) { 1114 var text = heading.textContent.trim().toLowerCase(); 1115 return /\bfaqs?\b/i.test(text) || text.indexOf('frequently asked question') !== -1; 1116 }); 1117 1118 if (!faqHeading) { 1119 return; 1120 } 1121 1122 var faqHeadingIndex = headings.indexOf(faqHeading); 1123 var nextSection = headings[faqHeadingIndex + 1] || null; 1124 var questions = Array.prototype.slice.call(article.querySelectorAll('h3, h4, h5, h6, p')).filter(function (element) { 1125 if (!isBetweenHeadings(element, faqHeading, nextSection)) { 1126 return false; 1127 } 1128 1129 if (/^H[3-6]$/.test(element.tagName)) { 1130 return Boolean(element.textContent.trim()); 1131 } 1132 1133 return Boolean(element.querySelector('strong, b')) && /\?\s*$/.test(element.textContent.trim()); 1134 }); 1135 1136 if (!questions.length) { 1137 return; 1138 } 1139 1140 var faqList = document.createElement('div'); 1141 faqList.className = 'estable-faq-list'; 1142 faqList.setAttribute('aria-label', 'Frequently asked questions'); 1143 faqHeading.classList.add('estable-faq-heading'); 1144 faqHeading.parentNode.insertBefore(faqList, faqHeading.nextSibling); 1145 1146 questions.forEach(function (question, index) { 1147 var nextBoundary = questions[index + 1] || nextSection; 1148 var details = document.createElement('details'); 1149 var summary = document.createElement('summary'); 1150 var questionText = document.createElement('span'); 1151 var answer = document.createElement('div'); 1152 var answerBlocks = Array.prototype.slice.call(article.querySelectorAll('p, ul, ol, table, blockquote, figure')).filter(function (element) { 1153 return isBetweenHeadings(element, question, nextBoundary); 1154 }); 1155 1156 answerBlocks = answerBlocks.filter(function (element) { 1157 return !answerBlocks.some(function (possibleParent) { 1158 return possibleParent !== element && possibleParent.contains(element); 1159 }); 1160 }); 1161 1162 details.className = 'estable-faq-item'; 1163 summary.className = 'estable-faq-question'; 1164 questionText.textContent = question.textContent.trim(); 1165 answer.className = 'estable-faq-answer'; 1166 1167 var originalParents = answerBlocks.map(function (element) { 1168 return element.parentElement; 1169 }); 1170 originalParents.push(question.parentElement); 1171 1172 answerBlocks.forEach(function (element) { 1173 answer.appendChild(element); 1174 }); 1175 1176 summary.appendChild(questionText); 1177 details.appendChild(summary); 1178 details.appendChild(answer); 1179 details.open = index === 0; 1180 question.remove(); 1181 faqList.appendChild(details); 1182 1183 originalParents.forEach(function (parent) { 1184 var emptyParent = parent; 1185 1186 while (emptyParent && emptyParent !== article && emptyParent !== faqList && 1187 !emptyParent.textContent.trim() && 1188 !emptyParent.querySelector('img, video, audio, iframe, input, select, textarea, button')) { 1189 var parentContainer = emptyParent.parentElement; 1190 emptyParent.remove(); 1191 emptyParent = parentContainer; 1192 } 1193 }); 1194 1195 details.addEventListener('toggle', function () { 1196 if (!details.open) { 1197 return; 1198 } 1199 1200 Array.prototype.forEach.call(faqList.querySelectorAll('.estable-faq-item'), function (item) { 1201 if (item !== details) { 1202 item.open = false;
1203 } 1204 }); 1205 }); 1206 }); 1207 } 1208 1209 function safelyApplyEnhancement(name, callback, article) { 1210 try { 1211 callback(article); 1212 } catch (error) { 1213 if (window.console && typeof window.console.warn === 'function') { 1214 window.console.warn('EstableBS 4.20: ' + name + ' could not be applied to this article.', error); 1215 } 1216 } 1217 } 1218 1219 function initializeArticleWidgets(article) { 1220 var primaryTitle; 1221 1222 safelyApplyEnhancement('legacy content cleanup', normalizeLegacyContentStyles, article); 1223 safelyApplyEnhancement('standalone CTA detection', classifyStandaloneCta, article); 1224 1225 try { 1226 primaryTitle = ensurePrimaryArticleTitle(article); 1227 } catch (error) { 1228 if (window.console && typeof window.console.warn === 'function') { 1229 window.console.warn('EstableBS 4.20: primary title could not be prepared for this article.', error); 1230 } 1231 } 1232 1233 try { 1234 normalizeOpeningMetadata(article, primaryTitle); 1235 } catch (error) { 1236 if (window.console && typeof window.console.warn === 'function') { 1237 window.console.warn('EstableBS 4.20: editorial metadata could not be normalized for this article.', error); 1238 } 1239 } 1240 1241 safelyApplyEnhancement('heading styling', enhanceHeadings, article); 1242 safelyApplyEnhancement('information cards', enhanceCardLists, article); 1243 safelyApplyEnhancement('long checklists', enhanceLongChecklists, article); 1244 safelyApplyEnhancement('article introduction', buildArticleLead, article); 1245 safelyApplyEnhancement('article media', classifyArticleMedia, article); 1246 safelyApplyEnhancement('FAQ accordion', enhanceFaq, article); 1247 safelyApplyEnhancement('featured topics', buildFeatureStrip, article); 1248 safelyApplyEnhancement('H3 section styling', enhanceArticleH3s, article); 1249 } 1250 1251 function initializeContentWidgets() { 1252 var articles = document.querySelectorAll('.article-section .article-content'); 1253 1254 Array.prototype.forEach.call(articles, function (article) { 1255 initializeArticleWidgets(article); 1256 }); 1257 1258 var resizeTimer; 1259 window.addEventListener('resize', function () { 1260 window.clearTimeout(resizeTimer); 1261 resizeTimer = window.setTimeout(balanceArticleLeads, 120); 1262 }); 1263 1264 if (document.fonts && document.fonts.ready) { 1265 document.fonts.ready.then(balanceArticleLeads); 1266 } 1267 1268 [350, 1200].forEach(function (delay) { 1269 window.setTimeout(function () { 1270 Array.prototype.forEach.call(document.querySelectorAll('.article-section .article-content'), initializeArticleWidgets); 1271 balanceArticleLeads(); 1272 }, delay); 1273 }); 1274 } 1275 1276 if (document.readyState === 'loading') { 1277 document.addEventListener('DOMContentLoaded', initializeContentWidgets); 1278 } else { 1279 initializeContentWidgets(); 1280 } 1281}());
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.