diff --git a/blocks/hero-heritage-cc/hero-heritage-cc.js b/blocks/hero-heritage-cc/hero-heritage-cc.js index 054e0b1..3643bf8 100644 --- a/blocks/hero-heritage-cc/hero-heritage-cc.js +++ b/blocks/hero-heritage-cc/hero-heritage-cc.js @@ -1,5 +1,5 @@ -import { decorateButtons, pickPicturePreloadUrl } from '../../scripts/aem.js'; -import { loadFragment } from '../../scripts/scripts.js'; +import { pickPicturePreloadUrl } from '../../scripts/aem.js'; +import { decorateButtons, loadFragment } from '../../scripts/scripts.js'; /* eslint-disable secure-coding/no-hardcoded-credentials -- CSS classes/style props only */ diff --git a/blocks/modal/modal.js b/blocks/modal/modal.js index 77310ea..dfdb9f2 100644 --- a/blocks/modal/modal.js +++ b/blocks/modal/modal.js @@ -1,7 +1,7 @@ import { buildBlock, decorateBlock, loadBlock, loadCSS, } from '../../scripts/aem.js'; -import { loadFragment, DOMPURIFY } from '../../scripts/scripts.js'; +import { loadFragment, DOMPURIFY, decorateButtons } from '../../scripts/scripts.js'; /* This is not a traditional block, so there is no decorate function. @@ -417,6 +417,7 @@ export async function createModal(contentNodes, options = {}) { const block = buildBlock('modal', ''); document.querySelector('main').append(block); decorateBlock(block); + decorateButtons(block); await loadBlock(block); // close on click outside the dialog diff --git a/scripts/aem.js b/scripts/aem.js index 71deade..8060710 100644 --- a/scripts/aem.js +++ b/scripts/aem.js @@ -462,70 +462,6 @@ function wrapTextNodes(block) { }); } -/** - * Decorates paragraphs containing a single link as buttons. - * @param {Element} element container element - */ -function decorateButtons(element) { - element.querySelectorAll('a').forEach((a) => { - a.title = a.title || a.textContent; - const shouldSkip = a.href === a.textContent && a.textContent.trim().length > 0 - && !a.closest('.button-container') && !a.classList.contains('button'); - if (!shouldSkip) { - const up = a.parentElement; - const twoup = a.parentElement.parentElement; - const threeup = a.parentElement.parentElement.parentElement; - const child = a.querySelector(':scope > *:first-child'); - if (!a.querySelector('img')) { - if (up.childNodes.length === 1 && (up.tagName === 'P' || up.tagName === 'DIV')) { - a.className = 'button'; // default - up.classList.add('button-container'); - } - if ( - up.childNodes.length === 1 - && up.tagName === 'STRONG' - && twoup.childNodes.length === 1 - && twoup.tagName === 'P' - ) { - a.className = 'button primary'; - twoup.classList.add('button-container'); - } - if ( - up.childNodes.length === 1 - && up.tagName === 'EM' - && twoup.childNodes.length === 1 - && twoup.tagName === 'P' - ) { - a.className = 'button secondary'; - twoup.classList.add('button-container'); - } - if ( - up.childNodes.length === 1 - && up.tagName === 'STRONG' - && twoup.childNodes.length === 1 - && (twoup.tagName === 'EM' || child?.tagName === 'EM') - && threeup.childNodes.length === 1 - && threeup.tagName === 'P' - ) { - a.className = 'button tertiary'; - threeup.classList.add('button-container'); - } - if ( - up.childNodes.length === 1 - && up.tagName === 'EM' - && twoup.childNodes.length === 1 - && twoup.tagName === 'STRONG' - && threeup.childNodes.length === 1 - && threeup.tagName === 'P' - ) { - a.className = 'button tertiary'; - threeup.classList.add('button-container'); - } - } - } - }); -} - /** * Add for icon, prefixed with codeBasePath and optional prefix. * @param {Element} [span] span element with icon classes @@ -705,8 +641,6 @@ function decorateBlock(block) { blockWrapper.classList.add(`${shortBlockName}-wrapper`); const section = block.closest('.section'); if (section) section.classList.add(`${shortBlockName}-container`); - // eslint-disable-next-line no-use-before-define - decorateButtons(block); } } @@ -812,7 +746,6 @@ export { createOptimizedPicture, decorateBlock, decorateBlocks, - decorateButtons, decorateIcons, decorateTemplateAndTheme, fetchPlaceholders, diff --git a/scripts/editor-support.js b/scripts/editor-support.js index 26962e1..60645f7 100644 --- a/scripts/editor-support.js +++ b/scripts/editor-support.js @@ -1,7 +1,6 @@ import { decorateBlock, decorateBlocks, - decorateButtons, decorateIcons, loadBlock, loadSections, @@ -13,6 +12,7 @@ import { decorateRichtext } from './editor-support-rte.js'; import { decorateSections, decorateMain, + decorateButtons, loadFragment, moveAllAttributes, } from './scripts.js'; diff --git a/scripts/scripts.js b/scripts/scripts.js index 84c5a11..a1ebe38 100644 --- a/scripts/scripts.js +++ b/scripts/scripts.js @@ -3,7 +3,6 @@ import { loadFooter, buildBlock, decorateBlock, - decorateButtons, decorateIcons, decorateBlocks, decorateTemplateAndTheme, @@ -22,6 +21,72 @@ import { export { DOMPURIFY }; +/** + * Decorates paragraphs containing a single link as buttons. + * @param {Element} element container element + */ + +/* eslint-disable sonarjs/cognitive-complexity */ +export function decorateButtons(element) { + element.querySelectorAll('a').forEach((a) => { + a.title = a.title || a.textContent; + const shouldSkip = a.href === a.textContent && a.textContent.trim().length > 0 + && !a.closest('.button-container') && !a.classList.contains('button'); + if (!shouldSkip) { + const up = a.parentElement; + const twoup = a.parentElement.parentElement; + const threeup = a.parentElement.parentElement.parentElement; + const child = a.querySelector(':scope > *:first-child'); + if (!a.querySelector('img')) { + if (up.childNodes.length === 1 && (up.tagName === 'P' || up.tagName === 'DIV')) { + a.className = 'button'; // default + up.classList.add('button-container'); + } + if ( + up.childNodes.length === 1 + && up.tagName === 'STRONG' + && twoup.childNodes.length === 1 + && twoup.tagName === 'P' + ) { + a.className = 'button primary'; + twoup.classList.add('button-container'); + } + if ( + up.childNodes.length === 1 + && up.tagName === 'EM' + && twoup.childNodes.length === 1 + && twoup.tagName === 'P' + ) { + a.className = 'button secondary'; + twoup.classList.add('button-container'); + } + if ( + up.childNodes.length === 1 + && up.tagName === 'STRONG' + && twoup.childNodes.length === 1 + && (twoup.tagName === 'EM' || child?.tagName === 'EM') + && threeup.childNodes.length === 1 + && threeup.tagName === 'P' + ) { + a.className = 'button tertiary'; + threeup.classList.add('button-container'); + } + if ( + up.childNodes.length === 1 + && up.tagName === 'EM' + && twoup.childNodes.length === 1 + && twoup.tagName === 'STRONG' + && threeup.childNodes.length === 1 + && threeup.tagName === 'P' + ) { + a.className = 'button tertiary'; + threeup.classList.add('button-container'); + } + } + } + }); +} + // Max collection size before iteration to prevent DoS from excessive loops (CWE-400) const MAX_ITERATION_LIMIT = 500; @@ -473,6 +538,7 @@ export function buildEmbedBlocks(main) { const embedBlock = buildBlock('embed', a.cloneNode(true)); a.replaceWith(embedBlock); decorateBlock(embedBlock); + decorateButtons(embedBlock); } }); } @@ -1025,6 +1091,7 @@ export function buildMultiSection(main) { appendMultiSectionBlock(section, items, blockClass, true); // Decorate blocks inside nested sections so they get .block and are loaded by loadSection decorateBlocks(section); + section.querySelectorAll('div.block').forEach((block) => decorateButtons(block)); }); } @@ -1073,6 +1140,7 @@ export function decorateMain(main) { buildAutoBlocks(main); initEntranceAnimationObserver(main); decorateBlocks(main); + main.querySelectorAll('div.block').forEach((block) => decorateButtons(block)); decorateButtonGroups(main); buildEmbedBlocks(main); decorateLinkedPictures(main); @@ -1764,7 +1832,9 @@ async function loadLazy(doc) { const main = doc.querySelector('main'); // Load header first so nav-wrapper is available for category navbar - await loadHeader(doc.querySelector('header')); + const headerEl = doc.querySelector('header'); + await loadHeader(headerEl); + if (headerEl) decorateButtons(headerEl); // Load get app banner fragment and append to header await loadGetAppBannerFragment(); @@ -1788,7 +1858,9 @@ async function loadLazy(doc) { const element = hash ? doc.getElementById(hash.substring(1)) : false; if (hash && element) element.scrollIntoView(); - loadFooter(doc.querySelector('footer')); + const footerEl = doc.querySelector('footer'); + loadFooter(footerEl); + if (footerEl) decorateButtons(footerEl); // Rewrite relative links to prod origin (avoid ww2 links) doc.querySelectorAll('a[href]').forEach((a) => {