Skip to content
Snippets Groups Projects
Verified Commit 9eed9cea authored by Volker Schukai's avatar Volker Schukai :alien:
Browse files

feat: new function detectRuntimeEnvironment and convertToPixels

parent 3a68dd70
No related branches found
No related tags found
No related merge requests found
/**
* Copyright schukai GmbH and contributors 2023. All Rights Reserved.
* Node module: @schukai/monster
* This file is licensed under the AGPLv3 License.
* License text available at https://www.gnu.org/licenses/agpl-3.0.en.html
*/
import {getWindow} from './util.mjs';
export {convertToPixels, getDeviceDPI}
/**
* Stores the DPI of the device.
*
* @returns {number}
* @type {number}
*/
let CURRENT_DEVICE_DPI = function () {
let i = 0;
for (i = 56; i < 2000; i++) {
if (getWindow().matchMedia("(max-resolution: " + i + "dpi)").matches === true) {
return i;
}
}
return i;
};
/**
* Returns the DPI of the device.
*
* @returns {number}
*/
function getDeviceDPI() {
// only call the function once
if (typeof CURRENT_DEVICE_DPI === 'function') {
CURRENT_DEVICE_DPI = CURRENT_DEVICE_DPI();
}
return getWindow().devicePixelRatio * CURRENT_DEVICE_DPI;
}
/**
* Converts a CSS value to pixels.
*
* As Example:
*
* ```js
* convertToPixels('1em') // returns the current font size in pixels
* convertToPixels('1rem') // returns the current root font size in pixels
* convertToPixels('1px') // returns 1
* convertToPixels('100%') // returns the current width of the parent element in pixels
* ```
*
* Following units are supported:
* - px
* - em
* - rem
* - %
*
* @param value
* @param parentElement
* @param fontSizeElement
* @returns {number}
* @license AGPLv3
* @since 1.6.0
* @copyright schukai GmbH
* @memberOf Monster.DOM
* @throws {Error} Unsupported unit
*/
function convertToPixels(value, parentElement = document.documentElement, fontSizeElement = document.documentElement) {
const regex = /^([\d.]+)(.*)$/;
const [, num, unit] = value.match(regex);
const number = parseFloat(num);
const dpi = getDeviceDPI();
if (unit === 'px') {
return number;
} else if (unit === 'em') {
const fontSize = parseFloat(window.getComputedStyle(fontSizeElement).fontSize);
return number * fontSize;
} else if (unit === 'rem') {
const rootFontSize = parseFloat(window.getComputedStyle(parentElement).fontSize);
return number * rootFontSize;
} else if (unit === '%') {
const parentWidth = parseFloat(window.getComputedStyle(parentElement).width);
return (number * parentWidth) / 100;
} else if (unit === 'in') {
return number * dpi;
} else if (unit === 'cm') {
return (number * dpi) / 2.54;
} else if (unit === 'mm') {
return (number * dpi) / 25.4;
} else if (unit === 'pt') {
return (number * dpi) / 72;
} else if (unit === 'pc') {
return (number * dpi) / 6;
} else {
throw new Error(`Unsupported unit: ${unit}`);
}
}
/**
* Copyright schukai GmbH and contributors 2023. All Rights Reserved.
* Node module: @schukai/monster
* This file is licensed under the AGPLv3 License.
* License text available at https://www.gnu.org/licenses/agpl-3.0.en.html
*/
const ENV_AWS_LAMBDA = 'aws-lambda';
const ENV_GOOGLE_FUNCTIONS = 'google-functions';
const ENV_ELECTRON = 'electron';
const ENV_NODE = 'node';
const ENV_BROWSER = 'browser';
const ENV_WEB_WORKER = 'web-worker';
const ENV_DENO = 'deno';
const ENV_UNKNOWN = 'unknown';
/**
* Detects and returns the current runtime environment.
*
* - 'aws-lambda': AWS Lambda environment
* - 'google-functions': Google Cloud Functions environment
* - 'electron': Electron environment
* - 'node': Node.js environment
* - 'browser': Browser environment
* - 'web-worker': Web Worker environment
* - 'deno': Deno environment
* - 'react-native': React Native environment
* - 'unknown': Unknown environment
*
* @returns {string} The detected runtime environment. Possible values are:
*/
function detectRuntimeEnvironment() {
// AWS Lambda environment
if (
typeof process !== 'undefined' &&
process.env != null &&
process.env.AWS_LAMBDA_FUNCTION_NAME
) {
return ENV_AWS_LAMBDA;
}
// Google Cloud Functions environment
if (
typeof process !== 'undefined' &&
process.env != null &&
process.env.FUNCTION_NAME
) {
return ENV_GOOGLE_FUNCTIONS;
}
// Node.js environment
if (
typeof process !== 'undefined' &&
process.versions != null &&
process.versions.node != null
) {
// Electron environment
if (process.versions.electron != null) {
return ENV_ELECTRON;
}
return ENV_NODE;
}
// Browser environment
if (
typeof window !== 'undefined' &&
typeof window.document !== 'undefined' &&
typeof navigator !== 'undefined' &&
typeof navigator.userAgent === 'string'
) {
// Web Worker environment
if (typeof self === 'object' && typeof importScripts === 'function') {
return ENV_WEB_WORKER;
}
return ENV_BROWSER;
}
// Deno environment
if (typeof Deno !== 'undefined') {
return ENV_DENO;
}
// Unknown environment
return ENV_UNKNOWN;
}
export {
ENV_AWS_LAMBDA,
ENV_GOOGLE_FUNCTIONS,
ENV_ELECTRON,
ENV_NODE,
ENV_BROWSER,
ENV_WEB_WORKER,
ENV_DENO,
ENV_UNKNOWN,
detectRuntimeEnvironment}
\ No newline at end of file
'use strict';
import {expect} from "chai" import {expect} from "chai"
import {ATTRIBUTEPREFIX, Assembler} from "../../../../application/source/dom/assembler.mjs"; import {ATTRIBUTEPREFIX, Assembler} from "../../../../application/source/dom/assembler.mjs";
......
'use strict';
import {expect} from "chai" import {expect} from "chai"
......
import {expect} from 'chai';
import {convertToPixels, getDeviceDPI} from "../../../../application/source/dom/dimension.mjs";
import {getWindow} from "../../../../application/source/dom/util.mjs";
import {initJSDOM, isBrowser, JSDOMExport as JSDOM} from "../../util/jsdom.mjs";
import {getGlobal} from "../../../../application/source/types/global.mjs";
import {detectRuntimeEnvironment} from "../../../../application/source/util/runtime.mjs";
function getMockWindow(dpi) {
if(detectRuntimeEnvironment() === 'browser') {
return getWindow();
}
const dom = new JSDOM('', {
pretendToBeVisual: true,
resources: 'usable',
});
dom.window.matchMedia = (query) => {
const dpiRegex = /\(max-resolution: (\d+)dpi\)/;
const match = query.match(dpiRegex);
if (match) {
const maxDpi = parseInt(match[1], 10);
return {matches: dpi <= maxDpi};
}
return {matches: false};
};
return dom.window;
}
describe('dimension', () => {
let currentEnvironment;
before(function (done) {
initJSDOM().then(() => {
//chaiDom(getDocument());
done();
});
})
beforeEach(() => {
const testDpi = 96;
const testWindow = getMockWindow(testDpi);
getGlobal().window = testWindow;
});
afterEach(() => {
delete getGlobal().window;
});
describe('convertToPixels', () => {
it('should correctly convert px values', () => {
const result = convertToPixels('100px');
expect(result).to.equal(100);
});
it('should correctly convert em values', () => {
const testElement = document.createElement('div');
testElement.style.fontSize = '16px';
document.body.appendChild(testElement);
const result = convertToPixels('2em', testElement, testElement);
expect(result).to.equal(32);
document.body.removeChild(testElement);
});
it('should correctly convert rem values', () => {
const testElement = document.createElement('div');
testElement.style.fontSize = '16px';
document.documentElement.appendChild(testElement);
const result = convertToPixels('2rem', testElement);
expect(result).to.equal(32);
document.documentElement.removeChild(testElement);
});
it('should correctly convert percentage values', () => {
const testElement = document.createElement('div');
testElement.style.width = '500px';
document.body.appendChild(testElement);
const result = convertToPixels('50%', testElement);
expect(result).to.equal(250);
document.body.removeChild(testElement);
});
it('should throw an error for unsupported units', () => {
expect(() => convertToPixels('10unsupportedUnit')).to.throw('Unsupported unit: unsupportedUnit');
});
});
describe('getDeviceDPI', () => {
it('should return the correct device DPI', () => {
const testDpi = 96;
const testWindow = getMockWindow(testDpi);
getGlobal().window = testWindow;
const deviceDpi = getDeviceDPI();
expect(deviceDpi).to.equal(testDpi * testWindow.devicePixelRatio);
delete getGlobal().window;
});
it('should cache the result and return the same value', () => {
const testDpi = 96;
const testWindow = getMockWindow(testDpi);
getGlobal().window = testWindow;
const deviceDpi1 = getDeviceDPI();
const deviceDpi2 = getDeviceDPI();
expect(deviceDpi1).to.equal(deviceDpi2);
delete getGlobal().window;
});
});
});
\ No newline at end of file
...@@ -6,6 +6,7 @@ import {getGlobal} from "../../../application/source/types/global.mjs"; ...@@ -6,6 +6,7 @@ import {getGlobal} from "../../../application/source/types/global.mjs";
export const isBrowser = new Function("try {return this===window;}catch(e){ return false;}"); export const isBrowser = new Function("try {return this===window;}catch(e){ return false;}");
export const isNode = new Function("try {return this===global;}catch(e){return false;}"); export const isNode = new Function("try {return this===global;}catch(e){return false;}");
let JSDOMExport = null;
/** /**
* this helper function creates the dom stack in the node environment * this helper function creates the dom stack in the node environment
...@@ -27,6 +28,7 @@ function initJSDOM(options) { ...@@ -27,6 +28,7 @@ function initJSDOM(options) {
}, options || {}) }, options || {})
return import("jsdom").then(({JSDOM}) => { return import("jsdom").then(({JSDOM}) => {
JSDOMExport = JSDOM;
const {window} = new JSDOM(`<html> const {window} = new JSDOM(`<html>
<head> <head>
</head> </head>
...@@ -85,5 +87,5 @@ function initJSDOM(options) { ...@@ -85,5 +87,5 @@ function initJSDOM(options) {
}); });
} }
export {initJSDOM} export {initJSDOM,JSDOMExport}
...@@ -20,6 +20,7 @@ import "../cases/dom/resource.mjs"; ...@@ -20,6 +20,7 @@ import "../cases/dom/resource.mjs";
import "../cases/dom/resourcemanager.mjs"; import "../cases/dom/resourcemanager.mjs";
import "../cases/dom/util.mjs"; import "../cases/dom/util.mjs";
import "../cases/dom/find.mjs"; import "../cases/dom/find.mjs";
import "../cases/dom/dimension.mjs";
import "../cases/dom/customelement.mjs"; import "../cases/dom/customelement.mjs";
import "../cases/dom/attributes.mjs"; import "../cases/dom/attributes.mjs";
import "../cases/dom/events.mjs"; import "../cases/dom/events.mjs";
......
...@@ -14,8 +14,8 @@ ...@@ -14,8 +14,8 @@
</head> </head>
<body> <body>
<div id="headline" style="display: flex;align-items: center;justify-content: center;flex-direction: column;"> <div id="headline" style="display: flex;align-items: center;justify-content: center;flex-direction: column;">
<h1 style='margin-bottom: 0.1em;'>Monster 3.32.0</h1> <h1 style='margin-bottom: 0.1em;'>Monster 3.33.0</h1>
<div id="lastupdate" style='font-size:0.7em'>last update So 26. Mär 17:02:25 CEST 2023</div> <div id="lastupdate" style='font-size:0.7em'>last update Mo 27. Mär 18:10:51 CEST 2023</div>
</div> </div>
<div id="mocks"></div> <div id="mocks"></div>
<div id="mocha"></div> <div id="mocha"></div>
......
Source diff could not be displayed: it is too large. Options to address this: view the blob.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment