diff --git a/blocks/footer/footer.js b/blocks/footer/footer.js
index fa00938199..4629414117 100644
--- a/blocks/footer/footer.js
+++ b/blocks/footer/footer.js
@@ -1,11 +1,11 @@
-import { readBlockConfig, decorateIcons } from '../../scripts/lib-franklin.js';
+import { readBlockConfig } from '../../scripts/lib-franklin.js';
/**
* loads and decorates the footer
* @param {Element} block The header block element
*/
-export default async function decorate(block) {
+export default async function decorate(block, plugins) {
const cfg = readBlockConfig(block);
block.textContent = '';
@@ -14,6 +14,6 @@ export default async function decorate(block) {
const html = await resp.text();
const footer = document.createElement('div');
footer.innerHTML = html;
- await decorateIcons(footer);
+ await plugins.decorator.decorateIcons(footer);
block.append(footer);
}
diff --git a/blocks/header/header.js b/blocks/header/header.js
index bce97e9c6d..738b81ec9b 100644
--- a/blocks/header/header.js
+++ b/blocks/header/header.js
@@ -1,4 +1,4 @@
-import { readBlockConfig, decorateIcons } from '../../scripts/lib-franklin.js';
+import { readBlockConfig } from '../../scripts/lib-franklin.js';
/**
* collapses all open nav sections
@@ -16,7 +16,7 @@ function collapseAllNavSections(sections) {
* @param {Element} block The header block element
*/
-export default async function decorate(block) {
+export default async function decorate(block, plugins) {
const cfg = readBlockConfig(block);
block.textContent = '';
@@ -29,7 +29,7 @@ export default async function decorate(block) {
// decorate nav DOM
const nav = document.createElement('nav');
nav.innerHTML = html;
- decorateIcons(nav);
+ plugins.decorator.decorateIcons(nav);
const classes = ['brand', 'sections', 'tools'];
classes.forEach((e, j) => {
@@ -60,7 +60,7 @@ export default async function decorate(block) {
});
nav.prepend(hamburger);
nav.setAttribute('aria-expanded', 'false');
- decorateIcons(nav);
+ plugins.decorator.decorateIcons(nav);
block.append(nav);
}
}
diff --git a/head.html b/head.html
index e34c617b56..ff8390ffa9 100644
--- a/head.html
+++ b/head.html
@@ -1,4 +1,5 @@
+
diff --git a/scripts/delayed.js b/scripts/delayed.js
index 920b4ad839..06c1c41222 100644
--- a/scripts/delayed.js
+++ b/scripts/delayed.js
@@ -1,7 +1 @@
-// eslint-disable-next-line import/no-cycle
-import { sampleRUM } from './lib-franklin.js';
-
-// Core Web Vitals RUM collection
-sampleRUM('cwv');
-
-// add more delayed functionality here
+// Execute anything that can be postponed to the latest here
diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js
index bd06357799..33de7da167 100644
--- a/scripts/lib-franklin.js
+++ b/scripts/lib-franklin.js
@@ -10,68 +10,102 @@
* governing permissions and limitations under the License.
*/
-/**
- * log RUM if part of the sample.
- * @param {string} checkpoint identifies the checkpoint in funnel
- * @param {Object} data additional data for RUM sample
- */
-export function sampleRUM(checkpoint, data = {}) {
- sampleRUM.defer = sampleRUM.defer || [];
- const defer = (fnname) => {
- sampleRUM[fnname] = sampleRUM[fnname]
- || ((...args) => sampleRUM.defer.push({ fnname, args }));
- };
- sampleRUM.drain = sampleRUM.drain
- || ((dfnname, fn) => {
- sampleRUM[dfnname] = fn;
- sampleRUM.defer
- .filter(({ fnname }) => dfnname === fnname)
- .forEach(({ fnname, args }) => sampleRUM[fnname](...args));
- });
- 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 : 100; // with parameter, weight is 1. Defaults to 100.
- // eslint-disable-next-line no-bitwise
- const hashCode = (s) => s.split('').reduce((a, b) => (((a << 5) - a) + b.charCodeAt(0)) | 0, 0);
- const id = `${hashCode(window.location.href)}-${new Date().getTime()}-${Math.random().toString(16).substr(2, 14)}`;
- const random = Math.random();
- const isSelected = (random * weight < 1);
- // eslint-disable-next-line object-curly-newline
- window.hlx.rum = { weight, id, random, isSelected, sampleRUM };
- }
- const { weight, id } = window.hlx.rum;
- if (window.hlx && window.hlx.rum && window.hlx.rum.isSelected) {
- const sendPing = (pdata = data) => {
- // eslint-disable-next-line object-curly-newline, max-len, no-use-before-define
- const body = JSON.stringify({ weight, id, referer: window.location.href, generation: window.hlx.RUM_GENERATION, checkpoint, ...data });
- const url = `https://rum.hlx.page/.rum/${weight}`;
- // eslint-disable-next-line no-unused-expressions
- navigator.sendBeacon(url, body);
- // eslint-disable-next-line no-console
- console.debug(`ping:${checkpoint}`, pdata);
- };
- sampleRUM.cases = sampleRUM.cases || {
- cwv: () => sampleRUM.cwv(data) || true,
- lazy: () => {
- // use classic script to avoid CORS issues
- 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](); }
+export const RumPlugin = () => {
+ /**
+ * log RUM if part of the sample.
+ * @param {string} checkpoint identifies the checkpoint in funnel
+ * @param {Object} data additional data for RUM sample
+ */
+ function sampleRUM(checkpoint, data = {}) {
+ sampleRUM.defer = sampleRUM.defer || [];
+ const defer = (fnname) => {
+ sampleRUM[fnname] = sampleRUM[fnname]
+ || ((...args) => sampleRUM.defer.push({ fnname, args }));
+ };
+ sampleRUM.drain = sampleRUM.drain
+ || ((dfnname, fn) => {
+ sampleRUM[dfnname] = fn;
+ sampleRUM.defer
+ .filter(({ fnname }) => dfnname === fnname)
+ .forEach(({ fnname, args }) => sampleRUM[fnname](...args));
+ });
+ 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 : 100; // with parameter, weight is 1. Defaults to 100.
+ // eslint-disable-next-line no-bitwise
+ const hashCode = (s) => s.split('').reduce((a, b) => (((a << 5) - a) + b.charCodeAt(0)) | 0, 0);
+ const id = `${hashCode(window.location.href)}-${new Date().getTime()}-${Math.random().toString(16).substr(2, 14)}`;
+ const random = Math.random();
+ const isSelected = (random * weight < 1);
+ // eslint-disable-next-line object-curly-newline
+ window.hlx.rum = { weight, id, random, isSelected, sampleRUM };
+ }
+ const { weight, id } = window.hlx.rum;
+ if (window.hlx && window.hlx.rum && window.hlx.rum.isSelected) {
+ const sendPing = (pdata = data) => {
+ // eslint-disable-next-line object-curly-newline, max-len, no-use-before-define
+ const body = JSON.stringify({ weight, id, referer: window.location.href, generation: window.hlx.RUM_GENERATION, checkpoint, ...data });
+ const url = `https://rum.hlx.page/.rum/${weight}`;
+ // eslint-disable-next-line no-unused-expressions
+ navigator.sendBeacon(url, body);
+ // eslint-disable-next-line no-console
+ console.debug(`ping:${checkpoint}`, pdata);
+ };
+ sampleRUM.cases = sampleRUM.cases || {
+ cwv: () => sampleRUM.cwv(data) || true,
+ lazy: () => {
+ // use classic script to avoid CORS issues
+ 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](); }
+ }
+ } catch (error) {
+ // something went wrong
}
- } catch (error) {
- // something went wrong
}
-}
+
+ sampleRUM('top');
+
+ window.addEventListener('load', () => sampleRUM('load'));
+
+ return {
+ name: 'rum',
+
+ api: {
+ sampleRUM,
+ },
+
+ preEager: async () => {
+ window.addEventListener('unhandledrejection', (event) => {
+ sampleRUM('error', { source: event.reason.sourceURL, target: event.reason.line });
+ });
+ window.addEventListener('error', (event) => {
+ sampleRUM('error', { source: event.filename, target: event.lineno });
+ });
+ },
+
+ postLazy: async () => {
+ const main = document.querySelector('main');
+ sampleRUM('lazy');
+ sampleRUM.observe(main.querySelectorAll('div[data-block-name]'));
+ sampleRUM.observe(main.querySelectorAll('picture > img'));
+ },
+
+ preDelayed: async () => {
+ sampleRUM('cwv');
+ },
+ };
+};
/**
* Loads a CSS file.
@@ -123,77 +157,56 @@ export function toCamelCase(name) {
return toClassName(name).replace(/-([a-z])/g, (g) => g[1].toUpperCase());
}
-/**
- * Replace icons with inline SVG and prefix with codeBasePath.
- * @param {Element} element
- */
-export function decorateIcons(element = document) {
- element.querySelectorAll('span.icon').forEach(async (span) => {
- if (span.classList.length < 2 || !span.classList[1].startsWith('icon-')) {
- return;
- }
- const icon = span.classList[1].substring(5);
- // eslint-disable-next-line no-use-before-define
- const resp = await fetch(`${window.hlx.codeBasePath}/icons/${icon}.svg`);
- if (resp.ok) {
- const iconHTML = await resp.text();
- if (iconHTML.match(/