version 1.0
This commit is contained in:
175
src/main/frontend/generated/jar-resources/theme-util.js
Normal file
175
src/main/frontend/generated/jar-resources/theme-util.js
Normal file
@@ -0,0 +1,175 @@
|
||||
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 = [];
|
||||
|
||||
const webcomponentGlobalCss = {
|
||||
css: [],
|
||||
importers: []
|
||||
};
|
||||
|
||||
export const injectGlobalWebcomponentCss = (css) => {
|
||||
webcomponentGlobalCss.css.push(css);
|
||||
webcomponentGlobalCss.importers.forEach(registrar => {
|
||||
registrar(css);
|
||||
});
|
||||
};
|
||||
|
||||
export const webcomponentGlobalCssInjector = (registrar) => {
|
||||
const registeredCss = [];
|
||||
const wrapper = (css) => {
|
||||
const hash = getHash(css);
|
||||
if (!registeredCss.includes(hash)) {
|
||||
registeredCss.push(hash);
|
||||
registrar(css);
|
||||
}
|
||||
};
|
||||
webcomponentGlobalCss.importers.push(wrapper);
|
||||
webcomponentGlobalCss.css.forEach(wrapper);
|
||||
};
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
Reference in New Issue
Block a user