|
2 | 2 | 'use strict'; |
3 | 3 |
|
4 | 4 | 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 | + |
5 | 15 | /** |
6 | 16 | * Class which handles everything related to the options page of the extension. Preferences are persisted in |
7 | 17 | * the browser's local storage. |
8 | 18 | */ |
9 | 19 | constructor() { |
10 | | - this.addBrowserDiscriminatingClassToBody(); |
| 20 | + this.addBrowserDiscriminatingClassesToBody(); |
11 | 21 |
|
12 | 22 | this.preferencesManager = new globals.Gmrle.PreferencesManager(); |
13 | 23 |
|
|
188 | 198 | } |
189 | 199 |
|
190 | 200 | /** |
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 |
192 | 204 | */ |
193 | | - getCurrentBrowserName() { |
| 205 | + getCurrentBrowserNameAndVersion() { |
| 206 | + let browserName = null; |
| 207 | + let browserVersion = null; |
| 208 | + |
194 | 209 | let ua = navigator.userAgent; |
195 | 210 |
|
196 | 211 | 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 | + } |
198 | 220 | } else if (ua.includes('Chrome/') && !ua.includes('Chromium/')) { |
199 | | - return 'chrome'; |
| 221 | + browserName = 'chrome'; |
200 | 222 | } else if (ua.includes('Edg/')) { |
201 | | - return 'edge'; |
| 223 | + browserName = 'edge'; |
202 | 224 | } else if (ua.includes('OPR/')) { |
203 | | - return 'opera'; |
| 225 | + browserName = 'opera'; |
204 | 226 | } |
205 | 227 |
|
206 | | - return null; |
| 228 | + return [browserName, browserVersion]; |
207 | 229 | } |
208 | 230 |
|
209 | 231 | /** |
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. |
211 | 233 | */ |
212 | | - addBrowserDiscriminatingClassToBody() { |
213 | | - let currentBrowserName = this.getCurrentBrowserName(); |
| 234 | + addBrowserDiscriminatingClassesToBody() { |
| 235 | + let [currentBrowserName, currentBrowserVersion] = this.getCurrentBrowserNameAndVersion(); |
214 | 236 |
|
215 | 237 | if (!currentBrowserName) { |
216 | 238 | return; |
|
223 | 245 | } |
224 | 246 |
|
225 | 247 | 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); |
226 | 264 | } |
227 | 265 |
|
228 | 266 | /** |
|
277 | 315 |
|
278 | 316 | delete this.submitButtonInOptionsForm.dataset.originalTextContent; |
279 | 317 | } |
| 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 | + } |
280 | 438 | } |
281 | 439 |
|
282 | 440 | document.addEventListener('DOMContentLoaded', function() { |
|
0 commit comments