scaler-js
Version:
Your best ally for easily building responsive web applications
199 lines (186 loc) • 8.52 kB
JavaScript
function isValidJsonString(value) {
try {
JSON.parse(value);
}
catch (e) {
return false;
}
return true;
}
const htmlTagBaseFontSize = 16;
const bypassScalerTransformationClassName = 'bypass-scaler-js-transformation';
const browserFontSizeDiffVarName = '--browser-font-size-diff';
function getPxToRemValue(value) {
return (value / htmlTagBaseFontSize).toFixed(4).replace(/[.,]0+$/, "");
}
var transformCss = (shouldTransformPixels, options, css) => {
const selector = css.selectorText;
if (!selector)
return '';
const isExcludedSelector = options.excludeSelectors.some(i => selector.includes(i));
const transformations = new Map();
// @ts-ignore
Array.from(css.styleMap).forEach((prop) => {
const propName = prop[0];
const cssUnitValueArr = prop[1];
if (cssUnitValueArr && Array.isArray(cssUnitValueArr) && cssUnitValueArr[0] instanceof CSSUnitValue) {
const cssUnitValue = cssUnitValueArr[0];
const isPixelUnit = cssUnitValue.unit === 'px';
const isExcludedAttr = options.excludeAttributes.some(i => propName.includes(i));
if (propName === 'font-size') {
const fontSizeValue = isPixelUnit
? `${getPxToRemValue(cssUnitValue.value)}rem`
: `${cssUnitValue.value}${cssUnitValue.unit}`;
transformations.set(propName, `calc(${fontSizeValue} + var(${browserFontSizeDiffVarName}))`);
}
else if (shouldTransformPixels && isPixelUnit && !isExcludedSelector && !isExcludedAttr) {
transformations.set(propName, `${getPxToRemValue(cssUnitValue.value)}rem`);
}
}
});
if (transformations.size) {
let transformedCss = `${selector}:not(${bypassScalerTransformationClassName}) {\n`;
transformations.forEach((value, key) => {
transformedCss += `${key}: ${value};\n`;
});
transformedCss += '}';
return transformedCss;
}
return '';
};
const transformPixelsDefault = {
excludeAttributes: [],
excludeSelectors: []
};
var scalerScript = () => {
return `
if (typeof window !== 'undefined') {
const baseFontSize = ${htmlTagBaseFontSize}
const segments = { width: 80, height: 45 }
const preciseBreakpoints = { width: 1320, height: 720 }
function getVirtualRemFontSize(width, height) {
const isLandscape = width > height
const widthSegment = isLandscape ? segments.width : segments.height
const heightSegment = isLandscape ? segments.height : segments.width
const preciseWidthBreakpoint = isLandscape ? preciseBreakpoints.width : preciseBreakpoints.height
const preciseHeightBreakpoint = isLandscape ? preciseBreakpoints.height : preciseBreakpoints.width
let X = width > preciseWidthBreakpoint ? widthSegment : widthSegment - Math.floor((preciseWidthBreakpoint - width) / (widthSegment / 2))
let Y = height > preciseHeightBreakpoint ? heightSegment : heightSegment - Math.floor((preciseHeightBreakpoint - height) / (heightSegment / 2))
return Math.round(((width / X) + (height / Y)) / 2)
}
const setBrowserFontSizeDiff = function(htmlElement) {
htmlElement.style.removeProperty('font-size');
const browserFontSize = window.getComputedStyle(htmlElement).getPropertyValue('font-size');
const browserDifference = Number(browserFontSize.replace('px', '')) - baseFontSize;
document.documentElement.style.setProperty('${browserFontSizeDiffVarName}', browserDifference + 'px')
}
const setVirtualRemFontSize = function(htmlElement) {
const vRem = getVirtualRemFontSize(window.innerWidth, window.innerHeight)
htmlElement.style.setProperty('font-size', vRem + 'px')
}
const updateHtmlFontSize = function() {
const htmlElement = document.querySelector('html');
setBrowserFontSizeDiff(htmlElement)
setVirtualRemFontSize(htmlElement)
}
const initHtmlFontSizeWatcher = function() {
window.addEventListener('resize', updateHtmlFontSize)
updateHtmlFontSize()
}
if (window.document.readyState !== 'loading') {
initHtmlFontSizeWatcher();
} else {
window.document.addEventListener('DOMContentLoaded', function() {
initHtmlFontSizeWatcher();
});
}
}`;
};
function transformExistingStyles(shouldTransformPixels, options) {
let transformations = '';
Array.from(document.styleSheets).forEach((styleSheet) => {
try {
const cssRules = styleSheet.cssRules;
Array.from(cssRules).filter((i) => i instanceof CSSStyleRule).forEach((rule) => {
const transformedRules = transformCss(shouldTransformPixels, options, rule);
if (transformedRules)
transformations += '\n' + transformedRules;
});
}
catch (error) {
console.warn('Scaler-js: Could not access a stylesheet rule. This might or might not affect your page responsiveness', error);
}
});
const style = document.createElement('style');
style.setAttribute('type', 'text/css');
style.setAttribute('data-scaler-js-transformations', 'true');
style.textContent = transformations.replace(/\n/g, '');
document.head.appendChild(style);
}
function observeNewlyAddedStyles(shouldTransformPixels, options) {
const cssObserver = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE && node.tagName.toLowerCase() === 'style') {
const styleEl = node;
if (styleEl.sheet) {
let transformations = '';
Array.from(styleEl.sheet.cssRules).filter((i) => i instanceof CSSStyleRule).forEach((rule) => {
const transformedRules = transformCss(shouldTransformPixels, options, rule);
if (transformedRules)
transformations += '\n' + transformedRules;
});
if (transformations) {
const css = styleEl.textContent ?? '';
styleEl.textContent = css + transformations;
}
}
}
});
}
}
});
cssObserver.observe(document.head, {
childList: true,
subtree: true,
});
}
function index (transformParams) {
function scaleUI() {
const htmlElem = document.querySelector('html');
const transformPixelsAttr = htmlElem?.getAttribute('data-scaler-js-transform-pixels');
const hasRuntimeOption = transformParams === 'runtime' && transformPixelsAttr != 'false';
const hasCustomOptions = typeof transformParams === 'object';
const shouldTransformPixels = hasRuntimeOption || hasCustomOptions || transformParams === true;
let transformPixelsOptions;
if (hasRuntimeOption && transformPixelsAttr && isValidJsonString(transformPixelsAttr)) {
transformPixelsOptions = Object.assign(transformPixelsDefault, JSON.parse(transformPixelsAttr));
}
else if (hasCustomOptions) {
transformPixelsOptions = Object.assign(transformPixelsDefault, { ...transformParams });
}
else {
transformPixelsOptions = transformPixelsDefault;
}
setTimeout(() => {
transformExistingStyles(shouldTransformPixels, transformPixelsOptions);
observeNewlyAddedStyles(shouldTransformPixels, transformPixelsOptions);
});
const script = scalerScript();
const scriptTag = document.createElement('script');
scriptTag.setAttribute('data-scaler-js-html-font-size-watcher', 'true');
scriptTag.textContent = script;
document.head.appendChild(scriptTag);
}
if (window.document.readyState !== 'loading') {
scaleUI();
}
else {
window.document.addEventListener('DOMContentLoaded', function () {
scaleUI();
});
}
}
export { index as default };
//# sourceMappingURL=index.esm.mjs.map