diff --git a/blocks/breadcrumb/breadcrumb.js b/blocks/breadcrumb/breadcrumb.js
index e2dc305ac..4b0b8f573 100644
--- a/blocks/breadcrumb/breadcrumb.js
+++ b/blocks/breadcrumb/breadcrumb.js
@@ -2,12 +2,13 @@ import {
a, div, li, ul,
} from '../../scripts/dom-builder.js';
import ffetch from '../../scripts/ffetch.js';
+import { getEdgeDeliveryPath } from '../../scripts/scripts.js';
const TEMPLATE_PATH_PATTERN = /\/us\/en\/[^/]+\/topics-template/;
async function getItems() {
+ const path = getEdgeDeliveryPath(window.location.pathname);
// get the breadcrumb items from the page path, only after '/us/en'
- const path = window.location.pathname;
const pathParts = path.split('/');
const itemPaths = pathParts.length > 2 ? pathParts.slice(3).map((_, i) => pathParts.slice(0, i + 4).join('/')) : [];
const articles = await ffetch('/us/en/article-index.json')
@@ -18,7 +19,8 @@ async function getItems() {
return itemPaths.map((itemPath) => {
// get the title from the article, based on its path
const article = articles.find((entry) => entry.path === itemPath);
- const title = (article && article.navTitle !== '') ? article.navTitle : itemPath.split('/').pop();
+ let title = (article && article.navTitle !== '') ? article.navTitle : itemPath.split('/').pop();
+ title = title.charAt(0).toUpperCase() + title.slice(1);
return {
title,
href: `${itemPath}.html`,
diff --git a/blocks/card-list/card-list.js b/blocks/card-list/card-list.js
index ec5d0fe25..61ea21b5d 100644
--- a/blocks/card-list/card-list.js
+++ b/blocks/card-list/card-list.js
@@ -8,6 +8,7 @@ import createArticleCard from './articleCard.js';
import createLibraryCard from './libraryCard.js';
import createApplicationCard from './applicationCard.js';
import { makePublicUrl } from '../../scripts/scripts.js';
+import { buildItemListSchema } from '../../scripts/schema.js';
const getSelectionFromUrl = () => (window.location.pathname.indexOf('topics') > -1 ? toClassName(window.location.pathname.replace('.html', '').split('/').pop()) : '');
const getPageFromUrl = () => toClassName(new URLSearchParams(window.location.search).get('page')) || '';
@@ -162,7 +163,7 @@ export default async function decorate(block) {
(item) => toClassName(item.topics).toLowerCase().indexOf(activeTagFilter) > -1,
);
}
-
+ buildItemListSchema(filteredArticles, 'resources');
// render cards application style
if (articleType === 'application' || articleType === 'info') {
filteredArticles.sort((card1, card2) => card1.title.localeCompare(card2.title));
diff --git a/blocks/product-card/product-card.js b/blocks/product-card/product-card.js
index 0f87c8732..87166b608 100644
--- a/blocks/product-card/product-card.js
+++ b/blocks/product-card/product-card.js
@@ -2,6 +2,7 @@ import { getProductsOnSolutionsResponse, onClickCoveoAnalyticsResponse } from '.
import {
ul, a, p, div, span, h4, li,
} from '../../scripts/dom-builder.js';
+import { buildItemListSchema } from '../../scripts/schema.js';
import { makePublicUrl, imageHelper } from '../../scripts/scripts.js';
export function createCard(product, idx, firstCard = false) {
@@ -29,6 +30,9 @@ export function createCard(product, idx, firstCard = false) {
export default async function decorate(block) {
const response = await getProductsOnSolutionsResponse();
if (response?.results.length > 0) {
+ if (window.location.pathname.includes('process-steps')) buildItemListSchema(response?.results, 'solution-products-steps');
+ else buildItemListSchema(response?.results, 'solution-products');
+
const cardList = ul({
class: 'container grid max-w-7xl w-full mx-auto gap-6 grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 px-4 pt-8 sm:px-0 justify-items-center mt-3 mb-3',
});
diff --git a/blocks/product-category/product-category.js b/blocks/product-category/product-category.js
index c93ae88e1..ea1de857f 100644
--- a/blocks/product-category/product-category.js
+++ b/blocks/product-category/product-category.js
@@ -4,7 +4,7 @@ import {
} from '../../scripts/dom-builder.js';
import { makePublicUrl, imageHelper } from '../../scripts/scripts.js';
import { getMetadata } from '../../scripts/lib-franklin.js';
-import { buildProductCategorySchema } from '../../scripts/schema.js';
+import { buildItemListSchema } from '../../scripts/schema.js';
export function createCard(product, firstCard = false) {
const cardWrapper = a(
@@ -52,7 +52,7 @@ export default async function decorate(block) {
products.forEach((product, index) => {
cardList.append(createCard(product, index === 0));
});
- if (products.length > 0) buildProductCategorySchema(products);
+ if (products.length > 0) buildItemListSchema(products, 'product-category');
block.textContent = '';
block.append(cardList);
diff --git a/blocks/product-family/product-family.js b/blocks/product-family/product-family.js
index e80d29ef3..7cf73f865 100644
--- a/blocks/product-family/product-family.js
+++ b/blocks/product-family/product-family.js
@@ -7,7 +7,7 @@ import {
div, span, button, fieldset, ul, li, input, a, img, p,
} from '../../scripts/dom-builder.js';
import { decorateIcons } from '../../scripts/lib-franklin.js';
-import { buildProductCategorySchema } from '../../scripts/schema.js';
+import { buildItemListSchema } from '../../scripts/schema.js';
const productSkeleton = div(
{ class: 'coveo-skeleton flex flex-col w-full lg:flex-row grid-rows-1 lg:grid-cols-5 gap-x-10 gap-y-4' },
@@ -523,7 +523,7 @@ export async function decorateProductList(block) {
block.removeChild(productSkeleton);
return;
}
- if (res.totalCount > 0) buildProductCategorySchema(res.results);
+ if (res.totalCount > 0) buildItemListSchema(res.results, 'product-family');
facets(res, facetDiv);
resultList(res, categoryDiv);
block.removeChild(productSkeleton);
diff --git a/blocks/recent-articles/recent-articles.js b/blocks/recent-articles/recent-articles.js
index 557e43101..f773fce47 100644
--- a/blocks/recent-articles/recent-articles.js
+++ b/blocks/recent-articles/recent-articles.js
@@ -2,16 +2,17 @@ import ffetch from '../../scripts/ffetch.js';
import {
div, ul, li, a, p, span,
} from '../../scripts/dom-builder.js';
-import { formatDateUTCSeconds, makePublicUrl } from '../../scripts/scripts.js';
+import { formatDateUTCSeconds, getEdgeDeliveryPath, makePublicUrl } from '../../scripts/scripts.js';
import { getMetadata } from '../../scripts/lib-franklin.js';
export default async function decorate(block) {
if (block.className.includes('recent-articles')) block.classList.add(...'flex-shrink-0 bg-danaherpurple-25'.split(' '));
const articleType = getMetadata('template').toLowerCase();
- const url = new URL(getMetadata('og:url'));
+ const url = new URL(getMetadata('og:url'), window.location.origin);
+ const path = getEdgeDeliveryPath(url.pathname);
let articles = await ffetch('/us/en/article-index.json')
.filter(({ type }) => type.toLowerCase() === articleType)
- .filter((article) => url.pathname !== article.path)
+ .filter((article) => path !== article.path)
.all();
articles = articles.sort((item1, item2) => item2.publishDate - item1.publishDate).slice(0, 6);
diff --git a/blocks/related-articles/related-articles.js b/blocks/related-articles/related-articles.js
index c6605f4d0..4cac593d7 100644
--- a/blocks/related-articles/related-articles.js
+++ b/blocks/related-articles/related-articles.js
@@ -4,15 +4,17 @@ import {
ul, span,
} from '../../scripts/dom-builder.js';
import createCard from '../card-list/articleCard.js';
+import { getEdgeDeliveryPath } from '../../scripts/scripts.js';
export default async function decorate(block) {
const articleType = getMetadata('template').toLowerCase();
const articleTopics = getMetadata('topics')?.toLowerCase();
- const url = new URL(getMetadata('og:url'));
+ const url = new URL(getMetadata('og:url'), window.location.origin);
+ const path = getEdgeDeliveryPath(url.pathname);
let articles = await ffetch('/us/en/article-index.json')
.filter(({ type }) => type.toLowerCase() === articleType)
.filter(({ topics }) => topics.toLowerCase() === articleTopics)
- .filter((article) => url.pathname !== article.path)
+ .filter((article) => path !== article.path)
.all();
articles = articles.sort((item1, item2) => item2.publishDate - item1.publishDate).slice(0, 3);
diff --git a/blocks/tags-list/tags-list.js b/blocks/tags-list/tags-list.js
index 3ae74dd0e..48ce76dee 100644
--- a/blocks/tags-list/tags-list.js
+++ b/blocks/tags-list/tags-list.js
@@ -5,15 +5,17 @@ import {
import ffetch from '../../scripts/ffetch.js';
import { getMetadata, decorateIcons } from '../../scripts/lib-franklin.js';
import { createFilters } from '../card-list/card-list.js';
+import { getEdgeDeliveryPath } from '../../scripts/scripts.js';
export default async function decorate(block) {
const articleType = getMetadata('template').toLowerCase();
const articleTopics = getMetadata('topics')?.toLowerCase();
- const url = new URL(getMetadata('og:url'));
+ const url = new URL(getMetadata('og:url'), window.location.origin);
+ const path = getEdgeDeliveryPath(url.pathname);
let articles = await ffetch('/us/en/article-index.json')
.filter(({ type }) => type.toLowerCase() === articleType)
.filter(({ topics }) => topics.toLowerCase() === articleTopics)
- .filter((article) => url.pathname === article.path)
+ .filter((article) => path === article.path)
.all();
articles = articles.sort((item1, item2) => item2.publishDate - item1.publishDate).slice(0, 1);
diff --git a/blocks/timeline/timeline.js b/blocks/timeline/timeline.js
index d77a6b681..1666b7cdd 100644
--- a/blocks/timeline/timeline.js
+++ b/blocks/timeline/timeline.js
@@ -6,6 +6,7 @@ import {
label,
span,
} from '../../scripts/dom-builder.js';
+import { buildItemListSchema } from '../../scripts/schema.js';
function updateMenu(target, block) {
const clickedMenu = target.closest('.menu-item');
@@ -98,6 +99,7 @@ export default function decorate(block) {
if (type !== 'menu') {
block.classList.add(...'w-full h-full top-14 bottom-0'.split(' '));
const items = block.children;
+ buildItemListSchema([...block.children], 'process-steps');
[...items].forEach((item, idx) => {
const picture = item.querySelector('div:last-child > p > picture');
const timeline = (idx % 2 === 0)
diff --git a/blocks/workflow-tabs/workflow-tabs.js b/blocks/workflow-tabs/workflow-tabs.js
index 62f912a11..a7b90c532 100644
--- a/blocks/workflow-tabs/workflow-tabs.js
+++ b/blocks/workflow-tabs/workflow-tabs.js
@@ -1,7 +1,8 @@
import {
div, li, ul, a, span,
} from '../../scripts/dom-builder.js';
-import { processEmbedFragment } from '../../scripts/scripts.js';
+import { processEmbedFragment, getFragmentFromFile } from '../../scripts/scripts.js';
+import { buildItemListSchema } from '../../scripts/schema.js';
const classActive = 'active';
const danaherPurpleClass = 'bg-danaherpurple-500';
@@ -74,6 +75,16 @@ async function buildTabs(block) {
const tabPanes = block.querySelectorAll('.workflow-tabs > div > div:last-child');
const tabList = div({ class: 'tabs-list' });
const decoratedPanes = await Promise.all([...tabPanes].map(async (pane, index) => {
+ // Added for SEO Schema generation
+ if (pane.textContent?.includes('workflow-carousels/master')) {
+ const fragment = await getFragmentFromFile(`${pane.textContent}.plain.html`);
+ const wfCarousel = document.createElement('div');
+ wfCarousel.innerHTML = fragment;
+ const childs = wfCarousel.querySelector('div.workflow-carousel').children;
+ buildItemListSchema([...childs].splice(1, childs.length), 'workflow');
+ }
+ // End of SEO Schema generation
+
const isActive = index === 0;
pane.classList.add('tab-pane', isActive ? classActive : 'off-screen');
const decoratedPane = await processEmbedFragment(pane);
diff --git a/component-models.json b/component-models.json
index 692aaebe5..df435f339 100644
--- a/component-models.json
+++ b/component-models.json
@@ -28,7 +28,6 @@
"component": "aem-tag",
"valueType": "string",
"name": "cq:tags",
- "multi": true,
"label": "AEM Tag Picker"
},
{
@@ -71,7 +70,7 @@
},
{
"component": "date-time",
- "valueType": "date",
+ "valueType": "date-time",
"name": "publishDate",
"label": "Publish Date",
"placeholder": "YYYY-MM-DD",
@@ -83,6 +82,13 @@
"valueType": "string",
"name": "navTitle",
"label": "Navigation Title"
+ },
+ {
+ "component": "text",
+ "valueType": "string",
+ "name": "template",
+ "label": "Template (temporary)",
+ "description": "Until we get the metadata spreadsheet fixed"
}
]
}
diff --git a/paths.json b/paths.json
index cbb8084b1..d123f7b8e 100644
--- a/paths.json
+++ b/paths.json
@@ -8,7 +8,10 @@
"/content/dam/danaher/franklin/metadata.json:/metadata.json",
"/content/dam/danaher/franklin/metadata-products.json:/metadata-products.json",
"/content/dam/danaher/franklin/metadata-articles.json:/metadata-articles.json",
- "/content/dam/danaher/franklin/redirects.json:/redirects.json"
+ "/content/dam/danaher/franklin/redirects.json:/redirects.json",
+ "/content/danaher.resource/us/en/article-index.json:/us/en/article-index.json",
+ "/content/danaher.resource/fragments/header/master.plain.html:/fragments/header/master.plain.html",
+ "/content/danaher.resource/fragments/footer.html:/fragments/footer.html"
],
"includes": [
"/content/danaher/ls/us/en",
diff --git a/scripts/lib-franklin-dev.js b/scripts/lib-franklin-dev.js
index 9e2146fed..5d99d804c 100644
--- a/scripts/lib-franklin-dev.js
+++ b/scripts/lib-franklin-dev.js
@@ -175,7 +175,7 @@ export async function loadScript(src, attrs) {
*/
export function getMetadata(name) {
const attr = name && name.includes(':') ? 'property' : 'name';
- const meta = [...document.head.querySelectorAll(`meta[${attr}="${name}"]`)].map((m) => m.content).join(', ');
+ const meta = [...document.head.querySelectorAll(`meta[${attr}="${name}" i]`)].map((m) => m.content).join(', ');
return meta || '';
}
@@ -862,7 +862,12 @@ export function setup() {
const scriptEl = document.querySelector('script[src$="/scripts/scripts.js"]');
if (scriptEl) {
try {
- [window.hlx.codeBasePath] = new URL(scriptEl.src).pathname.split('/scripts/scripts.js');
+ const scriptURL = new URL(scriptEl.src, window.location);
+ if (scriptURL.host === window.location.host) {
+ [window.hlx.codeBasePath] = scriptURL.pathname.split('/scripts/scripts.js');
+ } else {
+ [window.hlx.codeBasePath] = scriptURL.href.split('/scripts/scripts.js');
+ }
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js
index 39e172cab..1f8994b3b 100644
--- a/scripts/lib-franklin.js
+++ b/scripts/lib-franklin.js
@@ -1 +1 @@
-export function sampleRUM(checkpoint,data={}){sampleRUM.defer=sampleRUM.defer||[];const defer=fnname=>{sampleRUM[fnname]=sampleRUM[fnname]||((...args)=>sampleRUM.defer.push({fnname:fnname,args:args}))};sampleRUM.drain=sampleRUM.drain||((dfnname,fn)=>{sampleRUM[dfnname]=fn;sampleRUM.defer.filter((({fnname:fnname})=>dfnname===fnname)).forEach((({fnname:fnname,args:args})=>sampleRUM[fnname](...args)))});sampleRUM.always=sampleRUM.always||[];sampleRUM.always.on=(chkpnt,fn)=>{sampleRUM.always[chkpnt]=fn};sampleRUM.on=(chkpnt,fn)=>{sampleRUM.cases[chkpnt]=fn};defer("observe");defer("cwv");try{window.hlx=window.hlx||{};if(!window.hlx.rum){const usp=new URLSearchParams(window.location.search);const weight=usp.get("rum")==="on"?1:20;const id=Array.from({length:75},((_,i)=>String.fromCharCode(48+i))).filter((a=>/\d|[A-Z]/i.test(a))).filter((()=>Math.random()*75>70)).join("");const random=Math.random();const isSelected=random*weight<1;const firstReadTime=Date.now();const urlSanitizers={full:()=>window.location.href,origin:()=>window.location.origin,path:()=>window.location.href.replace(/\?.*$/,"")};window.hlx.rum={weight:weight,id:id,random:random,isSelected:isSelected,firstReadTime:firstReadTime,sampleRUM:sampleRUM,sanitizeURL:urlSanitizers[window.hlx.RUM_MASK_URL||"path"]}}const{weight:weight,id:id,firstReadTime:firstReadTime}=window.hlx.rum;if(window.hlx&&window.hlx.rum&&window.hlx.rum.isSelected){const knownProperties=["weight","id","referer","checkpoint","t","source","target","cwv","CLS","FID","LCP","INP"];const sendPing=(pdata=data)=>{const body=JSON.stringify({weight:weight,id:id,referer:window.hlx.rum.sanitizeURL(),checkpoint:checkpoint,t:Date.now()-firstReadTime,...data},knownProperties);const url=`https://rum.hlx.page/.rum/${weight}`;navigator.sendBeacon(url,body);console.debug(`ping:${checkpoint}`,pdata)};sampleRUM.cases=sampleRUM.cases||{cwv:()=>sampleRUM.cwv(data)||true,lazy:()=>{const script=document.createElement("script");script.src="https://rum.hlx.page/.rum/@adobe/helix-rum-enhancer@^1/src/index.js";document.head.appendChild(script);return true}};sendPing(data);if(sampleRUM.cases[checkpoint]){sampleRUM.cases[checkpoint]()}}if(sampleRUM.always[checkpoint]){sampleRUM.always[checkpoint](data)}}catch(error){}}export async function loadCSS(href){return new Promise(((resolve,reject)=>{if(!document.querySelector(`head > link[href="${href}"]`)){const link=document.createElement("link");link.rel="stylesheet";link.href=href;link.onload=resolve;link.onerror=reject;document.head.append(link)}else{resolve()}}))}export async function loadScript(src,attrs){return new Promise(((resolve,reject)=>{if(!document.querySelector(`head > script[src="${src}"]`)){const script=document.createElement("script");script.src=src;if(attrs){for(const attr in attrs){script.setAttribute(attr,attrs[attr])}}script.onload=resolve;script.onerror=reject;document.head.append(script)}else{resolve()}}))}export function getMetadata(name){const attr=name&&name.includes(":")?"property":"name";const meta=[...document.head.querySelectorAll(`meta[${attr}="${name}"]`)].map((m=>m.content)).join(", ");return meta||""}export function toClassName(name){return typeof name==="string"?name.toLowerCase().replace(/[^0-9a-z]/gi,"-").replace(/-+/g,"-").replace(/^-|-$/g,""):""}export function toCamelCase(name){return toClassName(name).replace(/-([a-z])/g,(g=>g[1].toUpperCase()))}export function getAllMetadata(scope){return[...document.head.querySelectorAll(`meta[property^="${scope}:"],meta[name^="${scope}-"]`)].reduce(((res,meta)=>{const id=toClassName(meta.name?meta.name.substring(scope.length+1):meta.getAttribute("property").split(":")[1]);res[id]=meta.getAttribute("content");return res}),{})}const ICONS_CACHE={};export async function decorateIcons(element){let svgSprite=document.getElementById("franklin-svg-sprite");if(!svgSprite){const div=document.createElement("div");div.innerHTML='';svgSprite=div.firstElementChild;document.body.append(div.firstElementChild)}const icons=[...element.querySelectorAll("span.icon")];await Promise.all(icons.map((async span=>{const iconName=Array.from(span.classList).find((c=>c.startsWith("icon-"))).substring(5);if(!ICONS_CACHE[iconName]){ICONS_CACHE[iconName]=true;try{let iconSource=`${window.hlx.codeBasePath}/icons/${iconName}.svg`;if(iconName.startsWith("dam-")){const isPublicDomain=window.location.hostname.includes("lifesciences.danaher.com");iconSource=isPublicDomain?"":"https://lifesciences.danaher.com";iconSource+=`/content/dam/danaher/system/icons/${iconName.substring(4).replace("_"," ")}.svg`}const response=await fetch(iconSource);if(!response.ok){ICONS_CACHE[iconName]=false;return}const svg=await response.text();if(svg.match(/(