in scripts/lib-franklin.js [165:234]
export async function decorateIcons(element) {
// Prepare the inline sprite
let svgSprite = document.getElementById('franklin-svg-sprite');
if (!svgSprite) {
const div = document.createElement('div');
div.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" id="franklin-svg-sprite" style="display: none"></svg>';
svgSprite = div.firstElementChild;
document.body.append(div.firstElementChild);
}
// Download all new icons
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 {
const response = await fetch(`${window.hlx.codeBasePath}/icons/${iconName}.svg`);
if (!response.ok) {
ICONS_CACHE[iconName] = false;
return;
}
// Styled icons don't play nice with the sprite approach because of shadow dom isolation
// and same for internal references
const svg = await response.text();
if (svg.match(/(<style | class=|url\(#| xlink:href="#)/)) {
ICONS_CACHE[iconName] = {
styled: true,
html: svg
// rescope ids and references to avoid clashes across icons;
.replaceAll(/ id="([^"]+)"/g, (_, id) => ` id="${iconName}-${id}"`)
.replaceAll(/="url\(#([^)]+)\)"/g, (_, id) => `="url(#${iconName}-${id})"`)
.replaceAll(/ xlink:href="#([^"]+)"/g, (_, id) => ` xlink:href="#${iconName}-${id}"`),
};
} else {
ICONS_CACHE[iconName] = {
html: svg
.replace('<svg', `<symbol id="icons-sprite-${iconName}"`)
.replace(/ width=".*?"/, '')
.replace(/ height=".*?"/, '')
.replace('</svg>', '</symbol>'),
};
}
} catch (error) {
ICONS_CACHE[iconName] = false;
// eslint-disable-next-line no-console
console.error(error);
}
}
}));
const symbols = Object
.keys(ICONS_CACHE).filter((k) => !svgSprite.querySelector(`#icons-sprite-${k}`))
.map((k) => ICONS_CACHE[k])
.filter((v) => !v.styled)
.map((v) => v.html)
.join('\n');
svgSprite.innerHTML += symbols;
icons.forEach((span) => {
const iconName = Array.from(span.classList).find((c) => c.startsWith('icon-')).substring(5);
const parent = span.firstElementChild?.tagName === 'A' ? span.firstElementChild : span;
// Styled icons need to be inlined as-is, while unstyled ones can leverage the sprite
if (ICONS_CACHE[iconName].styled) {
parent.innerHTML = ICONS_CACHE[iconName].html;
} else {
parent.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg"><use href="#icons-sprite-${iconName}"/></svg>`;
}
});
}