ModeratorElection/frontend/generated/jar-resources/theme-util.js
2024-06-06 17:45:46 +02:00

150 lines
4.6 KiB
JavaScript

import stripCssComments from 'strip-css-comments';
// Safari 15 - 16.3, polyfilled
const polyfilledSafari = CSSStyleSheet.toString().includes('document.createElement');
const createLinkReferences = (css, target) => {
// Unresolved urls are written as '@import url(text);' or '@import "text";' to the css
// media query can be present on @media tag or on @import directive after url
// Note that with Vite production build there is no space between @import and "text"
// [0] is the full match
// [1] matches the media query
// [2] matches the url
// [3] matches the quote char surrounding in '@import "..."'
// [4] matches the url in '@import "..."'
// [5] matches media query on @import statement
const importMatcher =
/(?:@media\s(.+?))?(?:\s{)?\@import\s*(?:url\(\s*['"]?(.+?)['"]?\s*\)|(["'])((?:\\.|[^\\])*?)\3)([^;]*);(?:})?/g;
// Only cleanup if comment exist
if (/\/\*(.|[\r\n])*?\*\//gm.exec(css) != null) {
// clean up comments
css = stripCssComments(css);
}
var match;
var styleCss = css;
// For each external url import add a link reference
while ((match = importMatcher.exec(css)) !== null) {
styleCss = styleCss.replace(match[0], '');
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = match[2] || match[4];
const media = match[1] || match[5];
if (media) {
link.media = media;
}
// For target document append to head else append to target
if (target === document) {
document.head.appendChild(link);
} else {
target.appendChild(link);
}
}
return styleCss;
};
const addAdoptedStyleSafariPolyfill = (sheet, target, first) => {
if (first) {
target.adoptedStyleSheets = [sheet, ...target.adoptedStyleSheets];
} else {
target.adoptedStyleSheets = [...target.adoptedStyleSheets, sheet];
}
return () => {
target.adoptedStyleSheets = target.adoptedStyleSheets.filter((ss) => ss !== sheet);
};
};
const addAdoptedStyle = (cssText, target, first) => {
const sheet = new CSSStyleSheet();
sheet.replaceSync(cssText);
if (polyfilledSafari) {
return addAdoptedStyleSafariPolyfill(sheet, target, first);
}
if (first) {
target.adoptedStyleSheets.splice(0, 0, sheet);
} else {
target.adoptedStyleSheets.push(sheet);
}
return () => {
target.adoptedStyleSheets.splice(target.adoptedStyleSheets.indexOf(sheet), 1);
};
};
const addStyleTag = (cssText, referenceComment) => {
const styleTag = document.createElement('style');
styleTag.type = 'text/css';
styleTag.textContent = cssText;
let beforeThis = undefined;
if (referenceComment) {
const comments = Array.from(document.head.childNodes).filter(elem => elem.nodeType === Node.COMMENT_NODE);
const container = comments.find(comment => comment.data.trim() === referenceComment);
if (container) {
beforeThis = container;
}
}
document.head.insertBefore(styleTag, beforeThis);
return () => {
styleTag.remove();
};
};
// target: Document | ShadowRoot
export const injectGlobalCss = (css, referenceComment, target, first) => {
if (target === document) {
const hash = getHash(css);
if (window.Vaadin.theme.injectedGlobalCss.indexOf(hash) !== -1) {
return;
}
window.Vaadin.theme.injectedGlobalCss.push(hash);
}
const cssText = createLinkReferences(css, target);
// We avoid mixing style tags and adoptedStyleSheets to make override order clear
if (target === document) {
return addStyleTag(cssText, referenceComment);
}
return addAdoptedStyle(cssText, target, first);
};
window.Vaadin = window.Vaadin || {};
window.Vaadin.theme = window.Vaadin.theme || {};
window.Vaadin.theme.injectedGlobalCss = [];
/**
* Calculate a 32 bit FNV-1a hash
* Found here: https://gist.github.com/vaiorabbit/5657561
* Ref.: http://isthe.com/chongo/tech/comp/fnv/
*
* @param {string} str the input value
* @returns {string} 32 bit (as 8 byte hex string)
*/
function hashFnv32a(str) {
/*jshint bitwise:false */
let i,
l,
hval = 0x811c9dc5;
for (i = 0, l = str.length; i < l; i++) {
hval ^= str.charCodeAt(i);
hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24);
}
// Convert to 8 digit hex string
return ('0000000' + (hval >>> 0).toString(16)).substr(-8);
}
/**
* Calculate a 64 bit hash for the given input.
* Double hash is used to significantly lower the collision probability.
*
* @param {string} input value to get hash for
* @returns {string} 64 bit (as 16 byte hex string)
*/
function getHash(input) {
let h1 = hashFnv32a(input); // returns 32 bit (as 8 byte hex string)
return h1 + hashFnv32a(h1 + input);
}