Skip to content
This repository was archived by the owner on Aug 31, 2023. It is now read-only.

Commit 73bb759

Browse files
committed
Allow to set styles depending on the browser's version
1 parent 46d6352 commit 73bb759

File tree

2 files changed

+171
-13
lines changed

2 files changed

+171
-13
lines changed

css/options.css

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55

66
/* Dark theme styles override */
77
@media (prefers-color-scheme: dark) {
8-
body.is-firefox {
8+
body.is-firefox.lt-89 {
99
background-color: #202023;
1010
color: rgb(249, 249, 250);
1111
}
1212

13-
body.is-firefox.gte-89 {
13+
body.is-firefox.ge-89 {
1414
background-color: rgb(35, 34, 43);
1515
color: rgb(251,251,254);
1616
}

js/options.js

Lines changed: 169 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,22 @@
22
'use strict';
33

44
class OptionsPage {
5+
/**
6+
* Returns the list of browsers versions for which to compare the current browser version from. This allows to
7+
* set CSS classes which can be used to target version-specific styles.
8+
*/
9+
get browser_versions_to_compare() {
10+
return {
11+
'firefox': ['89']
12+
};
13+
}
14+
515
/**
616
* Class which handles everything related to the options page of the extension. Preferences are persisted in
717
* the browser's local storage.
818
*/
919
constructor() {
10-
this.addBrowserDiscriminatingClassToBody();
20+
this.addBrowserDiscriminatingClassesToBody();
1121

1222
this.preferencesManager = new globals.Gmrle.PreferencesManager();
1323

@@ -188,29 +198,41 @@
188198
}
189199

190200
/**
191-
* Returns the browser name the extension is currently running on.
201+
* Returns the browser name the extension is currently running on, as well as its version.
202+
*
203+
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent
192204
*/
193-
getCurrentBrowserName() {
205+
getCurrentBrowserNameAndVersion() {
206+
let browserName = null;
207+
let browserVersion = null;
208+
194209
let ua = navigator.userAgent;
195210

196211
if (ua.includes('Firefox/') && !ua.includes('Seamonkey/')) {
197-
return 'firefox';
212+
browserName = 'firefox';
213+
214+
let firefoxVersionRegex = new RegExp('Firefox\/([0-9.]+)');
215+
let results = firefoxVersionRegex.exec(ua);
216+
217+
if (results) {
218+
browserVersion = results[1];
219+
}
198220
} else if (ua.includes('Chrome/') && !ua.includes('Chromium/')) {
199-
return 'chrome';
221+
browserName = 'chrome';
200222
} else if (ua.includes('Edg/')) {
201-
return 'edge';
223+
browserName = 'edge';
202224
} else if (ua.includes('OPR/')) {
203-
return 'opera';
225+
browserName = 'opera';
204226
}
205227

206-
return null;
228+
return [browserName, browserVersion];
207229
}
208230

209231
/**
210-
* Adds a CSS class name to the <body> tag identifying the browser the extension is currently running on.
232+
* Adds CSS classes names to the <body> tag identifying the browser the extension is currently running on, as well as its version.
211233
*/
212-
addBrowserDiscriminatingClassToBody() {
213-
let currentBrowserName = this.getCurrentBrowserName();
234+
addBrowserDiscriminatingClassesToBody() {
235+
let [currentBrowserName, currentBrowserVersion] = this.getCurrentBrowserNameAndVersion();
214236

215237
if (!currentBrowserName) {
216238
return;
@@ -223,6 +245,22 @@
223245
}
224246

225247
body.classList.add('is-' + currentBrowserName);
248+
249+
this.addBrowserVersionsComparisonsClasses(currentBrowserName, currentBrowserVersion, body);
250+
}
251+
252+
addBrowserVersionsComparisonsClasses(currentBrowserName, currentBrowserVersion, el) {
253+
if (!currentBrowserName || !currentBrowserVersion || !(currentBrowserName in this.browser_versions_to_compare)) {
254+
return;
255+
}
256+
257+
this.browser_versions_to_compare[currentBrowserName].forEach(function(targetBrowserVersion) {
258+
['gt', 'ge', 'lt', 'le', 'eq', 'ne'].forEach(function(operator) {
259+
if (this.versionCompare(currentBrowserVersion, targetBrowserVersion, operator)) {
260+
el.classList.add(operator + '-' + targetBrowserVersion.replace(new RegExp('\.'), '-'));
261+
}
262+
}, this);
263+
}, this);
226264
}
227265

228266
/**
@@ -277,6 +315,126 @@
277315

278316
delete this.submitButtonInOptionsForm.dataset.originalTextContent;
279317
}
318+
319+
/**
320+
* This function has been stolen from https://github.com/locutusjs/locutus/blob/master/src/php/info/version_compare.js
321+
*/
322+
versionCompare(v1, v2, operator) { // eslint-disable-line camelcase
323+
// discuss at: https://locutus.io/php/version_compare/
324+
// original by: Philippe Jausions (https://pear.php.net/user/jausions)
325+
// original by: Aidan Lister (https://aidanlister.com/)
326+
// reimplemented by: Kankrelune (https://www.webfaktory.info/)
327+
// improved by: Brett Zamir (https://brett-zamir.me)
328+
// improved by: Scott Baker
329+
// improved by: Theriault (https://github.com/Theriault)
330+
// example 1: version_compare('8.2.5rc', '8.2.5a')
331+
// returns 1: 1
332+
// example 2: version_compare('8.2.50', '8.2.52', '<')
333+
// returns 2: true
334+
// example 3: version_compare('5.3.0-dev', '5.3.0')
335+
// returns 3: -1
336+
// example 4: version_compare('4.1.0.52','4.01.0.51')
337+
// returns 4: 1
338+
339+
// Important: compare must be initialized at 0.
340+
let i
341+
let x
342+
let compare = 0
343+
344+
// vm maps textual PHP versions to negatives so they're less than 0.
345+
// PHP currently defines these as CASE-SENSITIVE. It is important to
346+
// leave these as negatives so that they can come before numerical versions
347+
// and as if no letters were there to begin with.
348+
// (1alpha is < 1 and < 1.1 but > 1dev1)
349+
// If a non-numerical value can't be mapped to this table, it receives
350+
// -7 as its value.
351+
const vm = {
352+
dev: -6,
353+
alpha: -5,
354+
a: -5,
355+
beta: -4,
356+
b: -4,
357+
RC: -3,
358+
rc: -3,
359+
'#': -2,
360+
p: 1,
361+
pl: 1
362+
}
363+
364+
// This function will be called to prepare each version argument.
365+
// It replaces every _, -, and + with a dot.
366+
// It surrounds any nonsequence of numbers/dots with dots.
367+
// It replaces sequences of dots with a single dot.
368+
// version_compare('4..0', '4.0') === 0
369+
// Important: A string of 0 length needs to be converted into a value
370+
// even less than an unexisting value in vm (-7), hence [-8].
371+
// It's also important to not strip spaces because of this.
372+
// version_compare('', ' ') === 1
373+
const _prepVersion = function (v) {
374+
v = ('' + v).replace(/[_\-+]/g, '.')
375+
v = v.replace(/([^.\d]+)/g, '.$1.').replace(/\.{2,}/g, '.')
376+
return (!v.length ? [-8] : v.split('.'))
377+
}
378+
// This converts a version component to a number.
379+
// Empty component becomes 0.
380+
// Non-numerical component becomes a negative number.
381+
// Numerical component becomes itself as an integer.
382+
const _numVersion = function (v) {
383+
return !v ? 0 : (isNaN(v) ? vm[v] || -7 : parseInt(v, 10))
384+
}
385+
386+
v1 = _prepVersion(v1)
387+
v2 = _prepVersion(v2)
388+
x = Math.max(v1.length, v2.length)
389+
390+
for (i = 0; i < x; i++) {
391+
if (v1[i] === v2[i]) {
392+
continue
393+
}
394+
v1[i] = _numVersion(v1[i])
395+
v2[i] = _numVersion(v2[i])
396+
if (v1[i] < v2[i]) {
397+
compare = -1
398+
break
399+
} else if (v1[i] > v2[i]) {
400+
compare = 1
401+
break
402+
}
403+
}
404+
405+
if (!operator) {
406+
return compare
407+
}
408+
409+
// Important: operator is CASE-SENSITIVE.
410+
// "No operator" seems to be treated as "<."
411+
// Any other values seem to make the function return null.
412+
switch (operator) {
413+
case '>':
414+
case 'gt':
415+
return (compare > 0)
416+
case '>=':
417+
case 'ge':
418+
return (compare >= 0)
419+
case '<=':
420+
case 'le':
421+
return (compare <= 0)
422+
case '===':
423+
case '=':
424+
case 'eq':
425+
return (compare === 0)
426+
case '<>':
427+
case '!==':
428+
case 'ne':
429+
return (compare !== 0)
430+
case '':
431+
case '<':
432+
case 'lt':
433+
return (compare < 0)
434+
default:
435+
return null
436+
}
437+
}
280438
}
281439

282440
document.addEventListener('DOMContentLoaded', function() {

0 commit comments

Comments
 (0)