Skip to content

Commit

Permalink
Detect some things with window.matchMedia
Browse files Browse the repository at this point in the history
(re: #1620)

Also removed some things from `utilDetect` that aren't worth
detecting anymore now that we don't support very old browsers.

Also cleaned up the utilDetect tests, I'm not sure they were
actually working correctly because of caching.
  • Loading branch information
bhousel committed Nov 21, 2024
1 parent d30bb66 commit ceba2f3
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 84 deletions.
4 changes: 2 additions & 2 deletions modules/core/LocalizationSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,8 @@ export class LocalizationSystem extends AbstractSystem {
// 4. English (always fallback)
const requestedLocales = (this._preferredLocaleCodes || [])
.concat(urlLocaleCodes)
.concat(utilDetect().browserLocales) // Locales preferred by the browser in priority order.
.concat(['en']); // Fallback to English since it's the only guaranteed complete language
.concat(utilDetect().locales) // Locales preferred by the browser in priority order.
.concat(['en']); // Fallback to English since it's the only guaranteed complete language

this._currLocaleCodes = this._getSupportedLocales(requestedLocales);
this._currLocaleCode = this._currLocaleCodes[0]; // First is highest priority locale; the rest are fallbacks
Expand Down
113 changes: 57 additions & 56 deletions modules/util/detect.js
Original file line number Diff line number Diff line change
@@ -1,94 +1,90 @@

let _detected;

let _cached;

/**
* `utilDetect` detects things from the user's browser.
* It returns an object with the following:
* {
* support: true, // Is Rapid supported? (basically - not Internet Explorer)
* browser: "Chrome", // e.g. 'Edge','msie','Opera','Chrome','Safari','Firefox'
* version: "133.0", // reported browser version
* languages: ['en-US'], // Array sourced from `navigator.languages`
* host: "http://127.0.0.1:8080/",
* os: "mac",
* platform: "Macintosh",
* prefersColorScheme: 'light', // 'light' or 'dark'
* prefersContrast: null, // 'more', 'less', or `null`
* prefersReducedMotion: false, // `true` or `false`
* prefersReducedTransparency: false // `true` or `false`
* }
*/
export function utilDetect(refresh) {
if (_detected && !refresh) return _detected;
_detected = {};
if (_cached && !refresh) return _cached;
_cached = {};

const ua = navigator.userAgent;
let m = null;

/* Browser */
m = ua.match(/(edg)\/?\s*(\.?\d+(\.\d+)*)/i); // Edge
if (m !== null) {
_detected.browser = 'Edge';
_detected.version = m[2];
_cached.browser = 'Edge';
_cached.version = m[2];
}
if (!_detected.browser) {
if (!_cached.browser) {
m = ua.match(/Trident\/.*rv:([0-9]{1,}[\.0-9]{0,})/i); // IE11
if (m !== null) {
_detected.browser = 'msie';
_detected.version = m[1];
_cached.browser = 'msie';
_cached.version = m[1];
}
}
if (!_detected.browser) {
if (!_cached.browser) {
m = ua.match(/(opr)\/?\s*(\.?\d+(\.\d+)*)/i); // Opera 15+
if (m !== null) {
_detected.browser = 'Opera';
_detected.version = m[2];
_cached.browser = 'Opera';
_cached.version = m[2];
}
}
if (!_detected.browser) {
if (!_cached.browser) {
m = ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
if (m !== null) {
_detected.browser = m[1];
_detected.version = m[2];
_cached.browser = m[1];
_cached.version = m[2];
m = ua.match(/version\/([\.\d]+)/i);
if (m !== null) _detected.version = m[1];
if (m !== null) _cached.version = m[1];
}
}
if (!_detected.browser) {
_detected.browser = navigator.appName;
_detected.version = navigator.appVersion;
if (!_cached.browser) {
_cached.browser = navigator.appName;
_cached.version = navigator.appVersion;
}

// keep major.minor version only..
_detected.version = _detected.version.split(/\W/).slice(0,2).join('.');

// detect other browser capabilities
// Legacy Opera has incomplete svg style support. See #715
_detected.opera = (_detected.browser.toLowerCase() === 'opera' && parseFloat(_detected.version) < 15 );
// Keep major.minor version only..
_cached.version = _cached.version.split(/\W/).slice(0,2).join('.');

if (_detected.browser.toLowerCase() === 'msie') {
_detected.support = false;
if (_cached.browser.toLowerCase() === 'msie') {
_cached.support = false;
} else {
_detected.support = true;
_cached.support = true;
}

_detected.filedrop = (window.FileReader && 'ondrop' in window);
_detected.download = !(_detected.ie || _detected.browser.toLowerCase() === 'edge');
_detected.cssfilters = !(_detected.ie || _detected.browser.toLowerCase() === 'edge');


/* Platform */
if (/Win/.test(ua)) {
_detected.os = 'win';
_detected.platform = 'Windows';
_cached.os = 'win';
_cached.platform = 'Windows';
} else if (/Mac/.test(ua)) {
_detected.os = 'mac';
_detected.platform = 'Macintosh';
_cached.os = 'mac';
_cached.platform = 'Macintosh';
} else if (/X11/.test(ua) || /Linux/.test(ua)) {
_detected.os = 'linux';
_detected.platform = 'Linux';
_cached.os = 'linux';
_cached.platform = 'Linux';
} else {
_detected.os = 'win';
_detected.platform = 'Unknown';
_cached.os = 'win';
_cached.platform = 'Unknown';
}


/* Locale */
// An array of locales requested by the browser in priority order.
_detected.browserLocales = Array.from(new Set( // remove duplicates
[navigator.language]
.concat(navigator.languages || [])
.concat([
// old property for backwards compatibility
navigator.userLanguage
])
// remove any undefined values
.filter(Boolean)
));

_cached.locales = navigator.languages.slice(); // shallow copy

/* Host */
const loc = window.top.location;
Expand All @@ -97,8 +93,13 @@ export function utilDetect(refresh) {
origin = loc.protocol + '//' + loc.hostname + (loc.port ? ':' + loc.port: '');
}

_detected.host = origin + loc.pathname;
_cached.host = origin + loc.pathname;

_cached.prefersColorScheme = window.matchMedia?.('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
_cached.prefersContrast = window.matchMedia?.('(prefers-contrast: more)').matches ? 'more'
: window.matchMedia?.('(prefers-contrast: less)').matches ? 'less' : null;
_cached.prefersReducedMotion = window.matchMedia?.('(prefers-reduced-motion: reduce)').matches;
_cached.prefersReducedTransparency = window.matchMedia?.('(prefers-reduced-transparency: reduce)').matches;

return _detected;
return _cached;
}
52 changes: 26 additions & 26 deletions test/unit/util/detect.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { afterEach, beforeEach, describe, it } from 'node:test';
import { before, after, beforeEach, describe, it } from 'node:test';
import { strict as assert } from 'node:assert';
import * as Rapid from '../../../modules/headless.js';

Expand All @@ -15,45 +15,45 @@ if (!global.window) { // mock window for Node
}

describe('utilDetect', () => {
let navigator;
let originalNavigator = global.navigator;
let origNavigator;

before(() => {
origNavigator = global.navigator;
});

after(() => {
global.navigator = origNavigator; // restore original
});

beforeEach(() => {
// Create a mock navigator object with a languages and platform property
navigator = {
languages: ['en-US'],
platform: 'Windows'
const mock = {
languages: ['en-US', 'en'],
platform: 'MacIntel',
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36'
};
// Set the mock navigator object as the global navigator object
global.navigator = navigator;
});
afterEach(() => {
// Reset the global navigator object to its original value
global.navigator = originalNavigator;
// Copy the original navigator, so we can safely change things.
global.navigator = Object.assign(origNavigator || mock);
});

it('should detect the browser and version', () => {
const ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3';
global.navigator = { userAgent: ua };
const detected = Rapid.utilDetect();
global.navigator.userAgent = ua;
const detected = Rapid.utilDetect(true);
assert.strictEqual(detected.browser, 'Chrome');
assert.strictEqual(detected.version, '58.0');
});

it('should detect the platform', () => {
it('should detect the os and platform', () => {
const ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3';
global.navigator = { userAgent: ua };
const detected = Rapid.utilDetect();
global.navigator.userAgent = ua;
const detected = Rapid.utilDetect(true);
assert.strictEqual(detected.os, 'win');
assert.strictEqual(detected.platform, 'Windows');
});

it('should detect the locale', () => {
const detected = Rapid.utilDetect();
assert.ok(!detected.browserLocales.includes('en-US'));
});

it('should detect the platform', () => {
const detected = Rapid.utilDetect();
assert.strictEqual(detected.platform, 'Windows');
global.navigator.languages = ['es'];
const detected = Rapid.utilDetect(true);
assert.ok(detected.locales.includes('es'));
});
});
});

0 comments on commit ceba2f3

Please sign in to comment.