Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | 1x 1x 11x 11x 80x 80x 80x 80x 80x 11x 11x 2x 11x 11x 11x 99x 99x 43x 86x 86x 86x 86x 43x 43x 43x 43x 43x 6x 43x 6x 43x 21x 22x | import { Injectable } from '@angular/core'; import { coerce, gte, satisfies } from 'semver'; import { ResolvedUserAgent, resolveUserAgent } from 'browserslist-useragent'; import UAParser from 'ua-parser-js'; // eslint-disable-next-line import/no-relative-packages import browsersJson from '../../../../../../definitions/browsers.json'; @Injectable({ providedIn: 'root' }) export class UserAgentService { // see https://github.com/ai/browserslist#browsers static browserNameMap: { [browserlistId: string]: string } = { bb: 'BlackBerry', and_chr: 'Chrome', ChromeAndroid: 'Chrome', FirefoxAndroid: 'Firefox', ff: 'Firefox', ie_mob: 'ExplorerMobile', ie: 'Explorer', and_ff: 'Firefox', ios_saf: 'iOS', op_mini: 'OperaMini', op_mob: 'OperaMobile', and_qq: 'QQAndroid', and_uc: 'UCAndroid' }; // we can't use matchesUA from browserslist-useragent because it expects a set of browserslist-queries not an already // parsed list of supported browsers. We parse our list once every release to make it more efficient. // Apart from that, this function is very similar and inspired by matchesUA. // TODO wrap put every usage of userAgent and UAparser into this service static userAgentMatches( userAgent: ResolvedUserAgent, browsersList: string[] = browsersJson.browsers, allowHigherVersions: boolean = true ): boolean { const parsedBrowsers = UserAgentService.parseBrowsersList(browsersList); return parsedBrowsers.some(browser => { Iif (!userAgent.family) return false; Iif (!userAgent.version) return false; Iif (!browser.version) return false; const allowHigher = allowHigherVersions ? 'major' : null; return (browser.family.toLowerCase() === userAgent.family.toLocaleLowerCase() && UserAgentService.versionSatisfies(userAgent.version, browser.version, allowHigher) ); }); } // inspired by resolveUserAgent from browserslist-useragent which is not publicly exported unfortunately static resolveUserAgent(UAstring: string = window.navigator.userAgent): ResolvedUserAgent { const parsedUA = UAParser(UAstring).browser; // https://bugzilla.mozilla.org/show_bug.cgi?id=1805967 if ((parsedUA.name === 'Firefox') && UAstring.match(/rv:109/)) { // eslint-disable-next-line no-param-reassign UAstring = UAstring.replace(/rv:109/, `rv:${parsedUA.version}`); } // https://news.ycombinator.com/item?id=20030340 Iif ((parsedUA.name === 'Edge') && UAstring.match(/Edg\//)) { // eslint-disable-next-line no-param-reassign UAstring = UAstring.replace(/Edg\//, 'Edge/'); } return resolveUserAgent(UAstring); } static parseBrowsersList(simpleBrowserList: string[]): { family: string; version: string }[] { return simpleBrowserList .map(browser => { const [family, version] = browser.split(' '); return { family: UserAgentService.browserNameMap[family] ?? family, version }; }); } // inspired by compareBrowserSemvers from browserslist-useragent which is not publicly exported unfortunately static versionSatisfies( testSemver: string, constraintSemver: string, allowHigher: 'patch' | 'minor' | 'major' | null = null ): boolean { const semverify = (version: string) => { Iif (!version) { return null; } const coerced = coerce(version, { loose: true }); Iif (!coerced) { return null; } return coerced.version; }; const semverifiedA = semverify(testSemver); const semverifiedB = semverify(constraintSemver); Iif (!semverifiedA || !semverifiedB) { return false; } let referenceVersion = semverifiedB; if (allowHigher === 'patch') { referenceVersion = `~${semverifiedB}`; } if (allowHigher === 'minor') { referenceVersion = `^${semverifiedB}`; } if (allowHigher === 'major') { return gte(semverifiedA, semverifiedB); } return satisfies(semverifiedA, referenceVersion); } static outputWithOs(UAstring: string = window.navigator.userAgent): string { const browser = this.resolveUserAgent(UAstring); const os = UAParser(UAstring).os; return `${os.name}/${os.version} ${browser.family}/${browser.version}`; } } |