From 0dd711471baebf07825f94580b03e3ece4c54f5f Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Thu, 4 Jan 2018 12:32:10 -0500 Subject: [PATCH] separated BrowserUtils from generic Utils --- src/background/main.background.ts | 25 +- src/background/runtime.background.ts | 7 +- src/background/webRequest.background.ts | 8 +- src/models/domain/cipher.ts | 12 +- src/models/request/deviceRequest.ts | 8 +- .../app/accounts/accountsHintController.js | 4 +- .../app/accounts/accountsLoginController.js | 4 +- .../accountsLoginTwoFactorController.js | 10 +- .../accounts/accountsRegisterController.js | 4 +- .../accountsTwoFactorMethodsController.js | 5 +- .../components/action-buttons.component.ts | 6 +- src/popup/app/components/pop-out.component.ts | 8 +- src/popup/app/current/current.component.ts | 18 +- src/popup/app/lock/lock.component.ts | 10 +- src/popup/app/services/auth.service.ts | 8 +- src/popup/app/services/background.service.ts | 2 + src/popup/app/services/services.module.ts | 1 + .../app/settings/environment.component.ts | 6 +- .../settings/folders/add-folder.component.ts | 9 +- .../settings/folders/edit-folder.component.ts | 9 +- src/popup/app/settings/options.component.ts | 8 +- src/popup/app/settings/settings.component.ts | 10 +- .../app/tools/password-generator.component.ts | 7 +- src/popup/app/tools/tools.component.ts | 6 +- .../app/vault/vaultAddCipherController.js | 8 +- .../app/vault/vaultAttachmentsController.js | 4 +- src/popup/app/vault/vaultController.js | 6 +- .../app/vault/vaultEditCipherController.js | 10 +- .../app/vault/vaultViewCipherController.js | 6 +- .../app/vault/vaultViewGroupingController.js | 4 +- src/scripts/analytics.js | 4 +- .../abstractions/browserUtils.service.ts | 17 ++ src/services/abstractions/utils.service.ts | 15 -- src/services/api.service.ts | 36 +-- src/services/autofill.service.ts | 8 +- src/services/browserUtils.service.spec.ts | 84 +++++++ src/services/browserUtils.service.ts | 215 ++++++++++++++++++ src/services/constants.service.ts | 6 +- src/services/i18n.service.ts | 6 +- src/services/lock.service.ts | 8 +- src/services/utils.service.spec.ts | 81 ------- src/services/utils.service.ts | 212 ----------------- 42 files changed, 474 insertions(+), 451 deletions(-) create mode 100644 src/services/abstractions/browserUtils.service.ts create mode 100644 src/services/browserUtils.service.spec.ts create mode 100644 src/services/browserUtils.service.ts diff --git a/src/background/main.background.ts b/src/background/main.background.ts index 0df78b318a..768d1058fc 100644 --- a/src/background/main.background.ts +++ b/src/background/main.background.ts @@ -14,6 +14,7 @@ import WindowsBackground from './windows.background'; import ApiService from '../services/api.service'; import AppIdService from '../services/appId.service'; import AutofillService from '../services/autofill.service'; +import BrowserUtilsService from '../services/browserUtils.service'; import ChromeStorageService from '../services/chromeStorage.service'; import CipherService from '../services/cipher.service'; import CollectionService from '../services/collection.service'; @@ -36,6 +37,7 @@ import { StorageService } from '../services/abstractions/storage.service'; export default class MainBackground { storageService: StorageService; i18nService: any; + browserUtilsService: BrowserUtilsService; utilsService: UtilsService; constantsService: ConstantsService; cryptoService: CryptoService; @@ -75,12 +77,13 @@ export default class MainBackground { // Services this.storageService = new ChromeStorageService(); this.utilsService = new UtilsService(); - this.i18nService = i18nService(this.utilsService); - this.constantsService = new ConstantsService(this.i18nService, this.utilsService); + this.browserUtilsService = new BrowserUtilsService(); + this.i18nService = i18nService(this.browserUtilsService); + this.constantsService = new ConstantsService(this.i18nService, this.browserUtilsService); this.cryptoService = new CryptoService(this.storageService, this.storageService); this.tokenService = new TokenService(this.storageService); this.appIdService = new AppIdService(this.storageService); - this.apiService = new ApiService(this.tokenService, this.utilsService, + this.apiService = new ApiService(this.tokenService, this.browserUtilsService, (expired: boolean) => this.logout(expired)); this.environmentService = new EnvironmentService(this.apiService, this.storageService); this.userService = new UserService(this.tokenService, this.storageService); @@ -91,7 +94,7 @@ export default class MainBackground { this.apiService, this.storageService); this.collectionService = new CollectionService(this.cryptoService, this.userService, this.storageService); this.lockService = new LockService(this.cipherService, this.folderService, this.collectionService, - this.cryptoService, this.utilsService, this.storageService, + this.cryptoService, this.browserUtilsService, this.storageService, () => this.setIcon(), () => this.refreshBadgeAndMenu()); this.syncService = new SyncService(this.userService, this.apiService, this.settingsService, this.folderService, this.cipherService, this.cryptoService, this.collectionService, @@ -99,7 +102,7 @@ export default class MainBackground { this.passwordGenerationService = new PasswordGenerationService(this.cryptoService, this.storageService); this.totpService = new TotpService(this.storageService); this.autofillService = new AutofillService(this.cipherService, this.tokenService, - this.totpService, this.utilsService); + this.totpService, this.utilsService, this.browserUtilsService); // Other fields this.sidebarAction = (typeof opr !== 'undefined') && opr.sidebarAction ? @@ -111,7 +114,7 @@ export default class MainBackground { this.passwordGenerationService); this.runtimeBackground = new RuntimeBackground(this, this.autofillService, this.cipherService); this.tabsBackground = new TabsBackground(this); - this.webRequestBackground = new WebRequestBackground(this.utilsService, this.cipherService); + this.webRequestBackground = new WebRequestBackground(this.browserUtilsService, this.cipherService); this.windowsBackground = new WindowsBackground(this); } @@ -251,7 +254,7 @@ export default class MainBackground { }); // Firefox & Edge do not support writing to the clipboard from background - if (!this.utilsService.isFirefox() && !this.utilsService.isEdge()) { + if (!this.browserUtilsService.isFirefox() && !this.browserUtilsService.isEdge()) { await this.contextMenusCreate({ type: 'normal', id: 'copy-username', @@ -295,7 +298,7 @@ export default class MainBackground { return; } - const tabDomain = UtilsService.getDomain(url); + const tabDomain = BrowserUtilsService.getDomain(url); if (tabDomain == null) { return; } @@ -370,7 +373,7 @@ export default class MainBackground { }); } - if (this.utilsService.isFirefox()) { + if (this.browserUtilsService.isFirefox()) { // Firefox does not support writing to the clipboard from background return; } @@ -411,7 +414,7 @@ export default class MainBackground { return; } - const tabDomain = UtilsService.getDomain(tab.url); + const tabDomain = BrowserUtilsService.getDomain(tab.url); if (tabDomain == null) { return; } @@ -489,7 +492,7 @@ export default class MainBackground { }, }; - if (this.utilsService.isFirefox()) { + if (this.browserUtilsService.isFirefox()) { await theAction.setIcon(options); } else { return new Promise((resolve) => { diff --git a/src/background/runtime.background.ts b/src/background/runtime.background.ts index 66366f158d..36a660ed69 100644 --- a/src/background/runtime.background.ts +++ b/src/background/runtime.background.ts @@ -5,6 +5,7 @@ import BrowserApi from '../browser/browserApi'; import MainBackground from './main.background'; import AutofillService from '../services/autofill.service'; +import BrowserUtilsService from '../services/browserUtils.service'; import CipherService from '../services/cipher.service'; import UtilsService from '../services/utils.service'; @@ -134,7 +135,7 @@ export default class RuntimeBackground { } const loginInfo = this.main.loginsToAdd[i]; - const tabDomain = UtilsService.getDomain(tab.url); + const tabDomain = BrowserUtilsService.getDomain(tab.url); if (tabDomain != null && tabDomain !== loginInfo.domain) { continue; } @@ -172,7 +173,7 @@ export default class RuntimeBackground { } const loginInfo = this.main.loginsToAdd[i]; - const tabDomain = UtilsService.getDomain(tab.url); + const tabDomain = BrowserUtilsService.getDomain(tab.url); if (tabDomain != null && tabDomain !== loginInfo.domain) { continue; } @@ -185,7 +186,7 @@ export default class RuntimeBackground { } private async addLogin(loginInfo: any, tab: any) { - const loginDomain = UtilsService.getDomain(loginInfo.url); + const loginDomain = BrowserUtilsService.getDomain(loginInfo.url); if (loginDomain == null) { return; } diff --git a/src/background/webRequest.background.ts b/src/background/webRequest.background.ts index a8b310846b..ebf4d32dce 100644 --- a/src/background/webRequest.background.ts +++ b/src/background/webRequest.background.ts @@ -1,14 +1,14 @@ +import BrowserUtilsService from '../services/browserUtils.service'; import CipherService from '../services/cipher.service'; -import UtilsService from '../services/utils.service'; export default class WebRequestBackground { private pendingAuthRequests: any[] = []; private webRequest: any; private isFirefox: boolean; - constructor(utilsService: UtilsService, private cipherService: CipherService) { + constructor(browserUtilsService: BrowserUtilsService, private cipherService: CipherService) { this.webRequest = (window as any).chrome.webRequest; - this.isFirefox = utilsService.isFirefox(); + this.isFirefox = browserUtilsService.isFirefox(); } async init() { @@ -24,7 +24,7 @@ export default class WebRequestBackground { return; } - const domain = UtilsService.getDomain(details.url); + const domain = BrowserUtilsService.getDomain(details.url); if (domain == null) { if (callback) { callback(); diff --git a/src/models/domain/cipher.ts b/src/models/domain/cipher.ts index 5d51c82828..3c18f7a666 100644 --- a/src/models/domain/cipher.ts +++ b/src/models/domain/cipher.ts @@ -11,7 +11,7 @@ import { Identity } from './identity'; import { Login } from './login'; import { SecureNote } from './secureNote'; -import { UtilsService } from '../../services/abstractions/utils.service'; +import { BrowserUtilsService } from '../../services/abstractions/browserUtils.service'; class Cipher extends Domain { id: string; @@ -32,7 +32,7 @@ class Cipher extends Domain { fields: Field[]; collectionIds: string[]; - private utilsService: UtilsService; + private browserUtilsService: BrowserUtilsService; constructor(obj?: CipherData, alreadyEncrypted: boolean = false, localData: any = null) { super(); @@ -119,12 +119,12 @@ class Cipher extends Domain { model.login = await this.login.decrypt(this.organizationId); model.subTitle = model.login.username; if (model.login.uri) { - if (this.utilsService == null) { - this.utilsService = chrome.extension.getBackgroundPage() - .bitwardenMain.utilsService as UtilsService; + if (this.browserUtilsService == null) { + this.browserUtilsService = chrome.extension.getBackgroundPage() + .bitwardenMain.browserUtilsService as BrowserUtilsService; } - model.login.domain = this.utilsService.getDomain(model.login.uri); + model.login.domain = this.browserUtilsService.getDomain(model.login.uri); } break; case CipherType.SecureNote: diff --git a/src/models/request/deviceRequest.ts b/src/models/request/deviceRequest.ts index 928a46557f..0869ba25c3 100644 --- a/src/models/request/deviceRequest.ts +++ b/src/models/request/deviceRequest.ts @@ -1,5 +1,5 @@ import { BrowserType } from '../../enums/browserType.enum'; -import { UtilsService } from '../../services/abstractions/utils.service'; +import { BrowserUtilsService } from '../../services/abstractions/browserUtils.service'; class DeviceRequest { type: BrowserType; @@ -7,9 +7,9 @@ class DeviceRequest { identifier: string; pushToken?: string; - constructor(appId: string, utilsService: UtilsService) { - this.type = utilsService.getBrowser(); - this.name = utilsService.getBrowserString(); + constructor(appId: string, browserUtilsService: BrowserUtilsService) { + this.type = browserUtilsService.getBrowser(); + this.name = browserUtilsService.getBrowserString(); this.identifier = appId; this.pushToken = null; } diff --git a/src/popup/app/accounts/accountsHintController.js b/src/popup/app/accounts/accountsHintController.js index 1cab6befa9..62b2bb3204 100644 --- a/src/popup/app/accounts/accountsHintController.js +++ b/src/popup/app/accounts/accountsHintController.js @@ -1,10 +1,10 @@ angular .module('bit.accounts') - .controller('accountsHintController', function ($scope, $state, apiService, toastr, $q, utilsService, + .controller('accountsHintController', function ($scope, $state, apiService, toastr, $q, browserUtilsService, $analytics, i18nService, $timeout) { $timeout(function () { - utilsService.initListSectionItemListeners(document, angular); + browserUtilsService.initListSectionItemListeners(document, angular); document.getElementById('email').focus(); }, 500); diff --git a/src/popup/app/accounts/accountsLoginController.js b/src/popup/app/accounts/accountsLoginController.js index 767abe6aaf..c9a54cbba9 100644 --- a/src/popup/app/accounts/accountsLoginController.js +++ b/src/popup/app/accounts/accountsLoginController.js @@ -2,9 +2,9 @@ angular .module('bit.accounts') .controller('accountsLoginController', function ($scope, $state, $stateParams, authService, userService, toastr, - utilsService, $analytics, i18nService, $timeout) { + browserUtilsService, $analytics, i18nService, $timeout) { $timeout(function () { - utilsService.initListSectionItemListeners(document, angular); + browserUtilsService.initListSectionItemListeners(document, angular); if ($stateParams.email) { document.getElementById('master-password').focus(); } diff --git a/src/popup/app/accounts/accountsLoginTwoFactorController.js b/src/popup/app/accounts/accountsLoginTwoFactorController.js index 7b04270d0b..9ef4b92f44 100644 --- a/src/popup/app/accounts/accountsLoginTwoFactorController.js +++ b/src/popup/app/accounts/accountsLoginTwoFactorController.js @@ -1,11 +1,11 @@ angular .module('bit.accounts') - .controller('accountsLoginTwoFactorController', function ($scope, $state, authService, toastr, utilsService, SweetAlert, + .controller('accountsLoginTwoFactorController', function ($scope, $state, authService, toastr, browserUtilsService, SweetAlert, $analytics, i18nService, $stateParams, $filter, constantsService, $timeout, $window, cryptoService, apiService, environmentService) { $timeout(function () { - utilsService.initListSectionItemListeners(document, angular); + browserUtilsService.initListSectionItemListeners(document, angular); }, 500); $scope.i18n = i18nService; @@ -130,7 +130,7 @@ angular var provider = $filter('filter')(constants.twoFactorProviderInfo, { type: keys[i], active: true }); if (provider.length && provider[0].priority > providerPriority) { if (provider[0].type == constants.twoFactorProvider.u2f && (typeof $window.u2f === 'undefined') && - !utilsService.isChrome() && !utilsService.isOpera()) { + !browserUtilsService.isChrome() && !browserUtilsService.isOpera()) { continue; } @@ -183,8 +183,8 @@ angular params = providers[constants.twoFactorProvider.email]; $scope.twoFactorEmail = params.Email; - if (chrome.extension.getViews({ type: 'popup' }).length > 0 && !utilsService.inSidebar($window) && - !utilsService.inTab($window) && !utilsService.inPopout($window)) { + if (chrome.extension.getViews({ type: 'popup' }).length > 0 && !browserUtilsService.inSidebar($window) && + !browserUtilsService.inTab($window) && !browserUtilsService.inPopout($window)) { SweetAlert.swal({ title: i18nService.twoStepLogin, text: i18nService.popup2faCloseMessage, diff --git a/src/popup/app/accounts/accountsRegisterController.js b/src/popup/app/accounts/accountsRegisterController.js index b3ab1f4cdf..52c2e7faeb 100644 --- a/src/popup/app/accounts/accountsRegisterController.js +++ b/src/popup/app/accounts/accountsRegisterController.js @@ -3,9 +3,9 @@ angular .controller( 'accountsRegisterController', - function ($scope, $state, cryptoService, toastr, $q, apiService, utilsService, $analytics, i18nService, $timeout) { + function ($scope, $state, cryptoService, toastr, $q, apiService, browserUtilsService, $analytics, i18nService, $timeout) { $timeout(function () { - utilsService.initListSectionItemListeners(document, angular); + browserUtilsService.initListSectionItemListeners(document, angular); document.getElementById('email').focus(); }, 500); diff --git a/src/popup/app/accounts/accountsTwoFactorMethodsController.js b/src/popup/app/accounts/accountsTwoFactorMethodsController.js index 0e69338e21..296bbb158e 100644 --- a/src/popup/app/accounts/accountsTwoFactorMethodsController.js +++ b/src/popup/app/accounts/accountsTwoFactorMethodsController.js @@ -1,4 +1,4 @@ -angular +angular .module('bit.accounts') .controller('accountsTwoFactorMethodsController', function ($scope, $state, $stateParams, constantsService, @@ -25,7 +25,8 @@ if (providers.hasOwnProperty(constants.twoFactorProvider.duo)) { add(constants.twoFactorProvider.duo); } - if (providers.hasOwnProperty(constants.twoFactorProvider.u2f) && (utilsService.isChrome() || utilsService.isOpera())) { + if (providers.hasOwnProperty(constants.twoFactorProvider.u2f) && + (browserUtilsService.isChrome() || browserUtilsService.isOpera())) { add(constants.twoFactorProvider.u2f); } diff --git a/src/popup/app/components/action-buttons.component.ts b/src/popup/app/components/action-buttons.component.ts index a04ff170f6..7ba92243cb 100644 --- a/src/popup/app/components/action-buttons.component.ts +++ b/src/popup/app/components/action-buttons.component.ts @@ -1,6 +1,6 @@ import * as template from './action-buttons.component.html'; -import { UtilsService } from '../../../services/abstractions/utils.service'; +import { BrowserUtilsService } from '../../../services/abstractions/browserUtils.service'; export class ActionButtonsController implements ng.IController { onView: Function; @@ -11,7 +11,7 @@ export class ActionButtonsController implements ng.IController { constants: any; constructor(private i18nService: any, private $analytics: any, private constantsService: any, private toastr: any, - private $timeout: any, private $window: any, private utilsService: UtilsService) { + private $timeout: any, private $window: any, private browserUtilsService: BrowserUtilsService) { this.i18n = i18nService; this.constants = constantsService; } @@ -22,7 +22,7 @@ export class ActionButtonsController implements ng.IController { if (self.cipher.login.uri.startsWith('http://') || self.cipher.login.uri.startsWith('https://')) { self.$analytics.eventTrack('Launched Website From Listing'); chrome.tabs.create({ url: self.cipher.login.uri }); - if (self.utilsService.inPopup(self.$window)) { + if (self.browserUtilsService.inPopup(self.$window)) { self.$window.close(); } } diff --git a/src/popup/app/components/pop-out.component.ts b/src/popup/app/components/pop-out.component.ts index 5af16e1691..2d623e8183 100644 --- a/src/popup/app/components/pop-out.component.ts +++ b/src/popup/app/components/pop-out.component.ts @@ -1,11 +1,11 @@ import * as template from './pop-out.component.html'; -import { UtilsService } from '../../../services/abstractions/utils.service'; +import { BrowserUtilsService } from '../../../services/abstractions/browserUtils.service'; export class PopOutController implements ng.IController { i18n: any; - constructor(private $analytics: any, private $window: any, private utilsService: UtilsService, + constructor(private $analytics: any, private $window: any, private browserUtilsService: BrowserUtilsService, private i18nService: any) { this.i18n = i18nService; } @@ -14,7 +14,7 @@ export class PopOutController implements ng.IController { this.$analytics.eventTrack('Expand Vault'); let href = this.$window.location.href; - if (this.utilsService.isEdge()) { + if (this.browserUtilsService.isEdge()) { const popupIndex = href.indexOf('/popup/'); if (popupIndex > -1) { href = href.substring(popupIndex); @@ -39,7 +39,7 @@ export class PopOutController implements ng.IController { height: bodyRect.height, }); - if (this.utilsService.inPopup(this.$window)) { + if (this.browserUtilsService.inPopup(this.$window)) { this.$window.close(); } } else { diff --git a/src/popup/app/current/current.component.ts b/src/popup/app/current/current.component.ts index e21656a7b5..3e1a93b4fd 100644 --- a/src/popup/app/current/current.component.ts +++ b/src/popup/app/current/current.component.ts @@ -1,5 +1,6 @@ import { CipherType } from '../../../enums/cipherType.enum'; +import { BrowserUtilsService } from '../../../services/abstractions/browserUtils.service'; import { UtilsService } from '../../../services/abstractions/utils.service'; import * as template from './current.component.html'; @@ -17,12 +18,13 @@ export class CurrentController { inSidebar: boolean = false; disableSearch: boolean = false; - constructor($scope: any, private cipherService: any, private utilsService: UtilsService, private toastr: any, - private $window: any, private $state: any, private $timeout: any, private autofillService: any, - private $analytics: any, private i18nService: any, private $filter: any) { + constructor($scope: any, private cipherService: any, private browserUtilsService: BrowserUtilsService, + private utilsService: UtilsService, private toastr: any, private $window: any, private $state: any, + private $timeout: any, private autofillService: any, private $analytics: any, private i18nService: any, + private $filter: any) { this.i18n = i18nService; - this.inSidebar = utilsService.inSidebar($window); - this.disableSearch = utilsService.isEdge(); + this.inSidebar = browserUtilsService.inSidebar($window); + this.disableSearch = browserUtilsService.isEdge(); $scope.$on('syncCompleted', (event: any, successfully: boolean) => { if (this.loaded) { @@ -76,10 +78,10 @@ export class CurrentController { fromBackground: false, }).then((totpCode: string) => { this.$analytics.eventTrack('Autofilled'); - if (totpCode && this.utilsService.isFirefox()) { + if (totpCode && this.browserUtilsService.isFirefox()) { this.utilsService.copyToClipboard(totpCode, document); } - if (this.utilsService.inPopup(this.$window)) { + if (this.browserUtilsService.inPopup(this.$window)) { this.$window.close(); } }).catch(() => { @@ -105,7 +107,7 @@ export class CurrentController { return; } - this.domain = this.utilsService.getDomain(this.url); + this.domain = this.browserUtilsService.getDomain(this.url); chrome.tabs.sendMessage(tabs[0].id, { command: 'collectPageDetails', diff --git a/src/popup/app/lock/lock.component.ts b/src/popup/app/lock/lock.component.ts index 50b4047220..3016dfe9d2 100644 --- a/src/popup/app/lock/lock.component.ts +++ b/src/popup/app/lock/lock.component.ts @@ -1,22 +1,22 @@ import * as angular from 'angular'; import * as template from './lock.component.html'; +import { BrowserUtilsService } from '../../../services/abstractions/browserUtils.service'; import { CryptoService } from '../../../services/abstractions/crypto.service'; -import { UtilsService } from '../../../services/abstractions/utils.service'; export class LockController { i18n: any; masterPassword: string; - constructor(public $state: any, public i18nService: any, private $timeout: any, private utilsService: UtilsService, - public cryptoService: CryptoService, public toastr: any, public userService: any, - public SweetAlert: any) { + constructor(public $state: any, public i18nService: any, private $timeout: any, + private browserUtilsService: BrowserUtilsService, public cryptoService: CryptoService, public toastr: any, + public userService: any, public SweetAlert: any) { this.i18n = i18nService; } $onInit() { this.$timeout(() => { - this.utilsService.initListSectionItemListeners(document, angular); + this.browserUtilsService.initListSectionItemListeners(document, angular); document.getElementById('master-password').focus(); }, 500); } diff --git a/src/popup/app/services/auth.service.ts b/src/popup/app/services/auth.service.ts index 83441a6f19..907bcf5b8e 100644 --- a/src/popup/app/services/auth.service.ts +++ b/src/popup/app/services/auth.service.ts @@ -1,13 +1,13 @@ import { DeviceRequest } from '../../../models/request/deviceRequest'; import { TokenRequest } from '../../../models/request/tokenRequest'; +import { BrowserUtilsService } from '../../../services/abstractions/browserUtils.service'; import { CryptoService } from '../../../services/abstractions/crypto.service'; -import { UtilsService } from '../../../services/abstractions/utils.service'; class AuthService { constructor(public cryptoService: CryptoService, public apiService: any, public userService: any, - public tokenService: any, public $rootScope: any, public appIdService: any, public utilsService: UtilsService, - public constantsService: any) { + public tokenService: any, public $rootScope: any, public appIdService: any, + public browserUtilsService: BrowserUtilsService, public constantsService: any) { } async logIn(email: string, masterPassword: string, twoFactorProvider?: number, @@ -19,7 +19,7 @@ class AuthService { const storedTwoFactorToken = await this.tokenService.getTwoFactorToken(email); const hashedPassword = await this.cryptoService.hashPassword(masterPassword, key); - const deviceRequest = new DeviceRequest(appId, this.utilsService); + const deviceRequest = new DeviceRequest(appId, this.browserUtilsService); let request: TokenRequest; diff --git a/src/popup/app/services/background.service.ts b/src/popup/app/services/background.service.ts index 4e0aee42bb..74f0850090 100644 --- a/src/popup/app/services/background.service.ts +++ b/src/popup/app/services/background.service.ts @@ -1,3 +1,4 @@ +import { BrowserUtilsService } from '../../../services/abstractions/browserUtils.service'; import { CryptoService } from '../../../services/abstractions/crypto.service'; import { StorageService } from '../../../services/abstractions/storage.service'; import { UtilsService } from '../../../services/abstractions/utils.service'; @@ -19,6 +20,7 @@ export const cipherService = getBackgroundService('cipherService' export const syncService = getBackgroundService('syncService'); export const autofillService = getBackgroundService('autofillService'); export const passwordGenerationService = getBackgroundService('passwordGenerationService'); +export const browserUtilsService = getBackgroundService('browserUtilsService'); export const utilsService = getBackgroundService('utilsService'); export const appIdService = getBackgroundService('appIdService'); export const i18nService = getBackgroundService('i18nService'); diff --git a/src/popup/app/services/services.module.ts b/src/popup/app/services/services.module.ts index 79626cb24e..760f4d6f89 100644 --- a/src/popup/app/services/services.module.ts +++ b/src/popup/app/services/services.module.ts @@ -20,6 +20,7 @@ export default angular .factory('syncService', backgroundServices.syncService) .factory('autofillService', backgroundServices.autofillService) .factory('passwordGenerationService', backgroundServices.passwordGenerationService) + .factory('browserUtilsService', backgroundServices.browserUtilsService) .factory('utilsService', backgroundServices.utilsService) .factory('appIdService', backgroundServices.appIdService) .factory('i18nService', backgroundServices.i18nService) diff --git a/src/popup/app/settings/environment.component.ts b/src/popup/app/settings/environment.component.ts index be886e5863..55073b927f 100644 --- a/src/popup/app/settings/environment.component.ts +++ b/src/popup/app/settings/environment.component.ts @@ -1,5 +1,5 @@ import * as angular from 'angular'; -import UtilsService from '../../../services/utils.service'; +import BrowserUtilsService from '../../../services/browserUtils.service'; import * as template from './environment.component.html'; export class EnvironmentController { @@ -10,12 +10,12 @@ export class EnvironmentController { baseUrl: string; i18n: any; - constructor(private i18nService: any, private $analytics: any, utilsService: UtilsService, + constructor(private i18nService: any, private $analytics: any, browserUtilsService: BrowserUtilsService, private environmentService: any, private toastr: any, private $timeout: ng.ITimeoutService) { this.i18n = i18nService; $timeout(() => { - utilsService.initListSectionItemListeners(document, angular); + browserUtilsService.initListSectionItemListeners(document, angular); }, 500); this.baseUrl = environmentService.baseUrl || ''; diff --git a/src/popup/app/settings/folders/add-folder.component.ts b/src/popup/app/settings/folders/add-folder.component.ts index 14382d181d..e1552df3c8 100644 --- a/src/popup/app/settings/folders/add-folder.component.ts +++ b/src/popup/app/settings/folders/add-folder.component.ts @@ -1,17 +1,18 @@ import * as angular from 'angular'; import { Folder } from '../../../../models/domain/folder'; -import { UtilsService } from '../../../../services/abstractions/utils.service'; import * as template from './add-folder.component.html'; +import { BrowserUtilsService } from '../../../../services/abstractions/browserUtils.service'; + export class AddFolderController { savePromise: any; folder: {}; i18n: any; - constructor(private folderService: any, private $state: any, private toastr: any, utilsService: UtilsService, - private $analytics: any, private i18nService: any, $timeout: any) { + constructor(private folderService: any, private $state: any, private toastr: any, + browserUtilsService: BrowserUtilsService, private $analytics: any, private i18nService: any, $timeout: any) { $timeout(() => { - utilsService.initListSectionItemListeners(document, angular); + browserUtilsService.initListSectionItemListeners(document, angular); document.getElementById('name').focus(); }, 500); diff --git a/src/popup/app/settings/folders/edit-folder.component.ts b/src/popup/app/settings/folders/edit-folder.component.ts index f923a99cb3..2c9dc8cf33 100644 --- a/src/popup/app/settings/folders/edit-folder.component.ts +++ b/src/popup/app/settings/folders/edit-folder.component.ts @@ -1,8 +1,9 @@ import * as angular from 'angular'; import { Folder } from '../../../../models/domain/folder'; -import UtilsService from '../../../../services/utils.service'; import * as template from './edit-folder.component.html'; +import { BrowserUtilsService } from '../../../../services/abstractions/browserUtils.service'; + export class EditFolderController { $transition$: any; folderId: any; @@ -11,12 +12,12 @@ export class EditFolderController { folder: Folder; constructor($scope: any, $stateParams: any, private folderService: any, private toastr: any, private $state: any, - private SweetAlert: any, utilsService: UtilsService, private $analytics: any, private i18nService: any, - $timeout: any) { + private SweetAlert: any, browserUtilsService: BrowserUtilsService, private $analytics: any, + private i18nService: any, $timeout: any) { this.i18n = i18nService; $timeout(() => { - utilsService.initListSectionItemListeners(document, angular); + browserUtilsService.initListSectionItemListeners(document, angular); document.getElementById('name').focus(); }, 500); diff --git a/src/popup/app/settings/options.component.ts b/src/popup/app/settings/options.component.ts index 653ea99221..4dd7cf2f19 100644 --- a/src/popup/app/settings/options.component.ts +++ b/src/popup/app/settings/options.component.ts @@ -1,6 +1,6 @@ import * as angular from 'angular'; +import { BrowserUtilsService } from '../../../services/abstractions/browserUtils.service'; import { StorageService } from '../../../services/abstractions/storage.service'; -import { UtilsService } from '../../../services/abstractions/utils.service'; import StateService from '../services/state.service'; import * as template from './options.component.html'; @@ -14,12 +14,12 @@ export class OptionsController { i18n: any; constructor(private i18nService: any, private $analytics: any, private constantsService: any, - private utilsService: UtilsService, private totpService: any, private stateService: StateService, + private browserUtilsService: BrowserUtilsService, private totpService: any, private stateService: StateService, private storageService: StorageService, private $timeout: ng.ITimeoutService) { this.i18n = i18nService; $timeout(() => { - utilsService.initListSectionItemListeners(document, angular); + browserUtilsService.initListSectionItemListeners(document, angular); }, 500); this.loadSettings(); @@ -31,7 +31,7 @@ export class OptionsController { const disableGa = await this.storageService.get( this.constantsService.disableGaKey); - this.disableGa = disableGa || (this.utilsService.isFirefox() && disableGa === undefined); + this.disableGa = disableGa || (this.browserUtilsService.isFirefox() && disableGa === undefined); this.disableAddLoginNotification = await this.storageService.get( this.constantsService.disableAddLoginNotificationKey); diff --git a/src/popup/app/settings/settings.component.ts b/src/popup/app/settings/settings.component.ts index 7a6dae9539..5b24df06f0 100644 --- a/src/popup/app/settings/settings.component.ts +++ b/src/popup/app/settings/settings.component.ts @@ -1,8 +1,8 @@ import * as angular from 'angular'; import { BrowserType } from '../../../enums/browserType.enum'; +import { BrowserUtilsService } from '../../../services/abstractions/browserUtils.service'; import { CryptoService } from '../../../services/abstractions/crypto.service'; import { StorageService } from '../../../services/abstractions/storage.service'; -import { UtilsService } from '../../../services/abstractions/utils.service'; import ConstantsService from '../../../services/constants.service'; import * as template from './settings.component.html'; @@ -27,17 +27,17 @@ export class SettingsController { i18n: any; showOnLocked: boolean; - constructor(private $state: any, private SweetAlert: any, private utilsService: UtilsService, + constructor(private $state: any, private SweetAlert: any, private browserUtilsService: BrowserUtilsService, private $analytics: any, private i18nService: any, private constantsService: ConstantsService, private cryptoService: CryptoService, private lockService: any, private storageService: StorageService, private $timeout: ng.ITimeoutService) { this.i18n = i18nService; $timeout(() => { - utilsService.initListSectionItemListeners(document, angular); + browserUtilsService.initListSectionItemListeners(document, angular); }, 500); - this.showOnLocked = !utilsService.isFirefox() && !utilsService.isEdge(); + this.showOnLocked = !browserUtilsService.isFirefox() && !browserUtilsService.isEdge(); this.storageService.get(constantsService.lockOptionKey).then((lockOption: number) => { if (lockOption != null) { let option = lockOption.toString(); @@ -146,7 +146,7 @@ export class SettingsController { rate() { this.$analytics.eventTrack('Rate Extension'); chrome.tabs.create({ - url: RateUrls[this.utilsService.getBrowser()], + url: RateUrls[this.browserUtilsService.getBrowser()], }); } } diff --git a/src/popup/app/tools/password-generator.component.ts b/src/popup/app/tools/password-generator.component.ts index fcccbee4aa..36bacf0833 100644 --- a/src/popup/app/tools/password-generator.component.ts +++ b/src/popup/app/tools/password-generator.component.ts @@ -1,7 +1,7 @@ import * as angular from 'angular'; import * as template from './password-generator.component.html'; -import { UtilsService } from '../../../services/abstractions/utils.service'; +import { BrowserUtilsService } from '../../../services/abstractions/browserUtils.service'; export class PasswordGeneratorController { $transition$: any; @@ -13,7 +13,7 @@ export class PasswordGeneratorController { i18n: any; constructor(private $state: any, private passwordGenerationService: any, - private toastr: any, private utilsService: UtilsService, private $analytics: any, + private toastr: any, private browserUtilsService: BrowserUtilsService, private $analytics: any, private i18nService: any, private $timeout: any) { this.i18n = i18nService; @@ -41,9 +41,8 @@ export class PasswordGeneratorController { this.showSelect = this.addState || this.editState; - const self = this; this.$timeout(() => { - self.utilsService.initListSectionItemListeners(document, angular); + this.browserUtilsService.initListSectionItemListeners(document, angular); }, 500); } diff --git a/src/popup/app/tools/tools.component.ts b/src/popup/app/tools/tools.component.ts index 900c3170c9..425f202852 100644 --- a/src/popup/app/tools/tools.component.ts +++ b/src/popup/app/tools/tools.component.ts @@ -1,6 +1,6 @@ import * as template from './tools.component.html'; -import { UtilsService } from '../../../services/abstractions/utils.service'; +import { BrowserUtilsService } from '../../../services/abstractions/browserUtils.service'; export class ToolsController { showExport: boolean; @@ -8,10 +8,10 @@ export class ToolsController { private webVaultBaseUrl: string = 'https://vault.bitwarden.com'; constructor(private SweetAlert: any, private i18nService: any, - private $analytics: any, private utilsService: UtilsService, + private $analytics: any, private browserUtilsService: BrowserUtilsService, private environmentService: any) { this.i18n = i18nService; - this.showExport = !utilsService.isEdge(); + this.showExport = !browserUtilsService.isEdge(); if (environmentService.baseUrl) { this.webVaultBaseUrl = environmentService.baseUrl; } else if (environmentService.webVaultUrl) { diff --git a/src/popup/app/vault/vaultAddCipherController.js b/src/popup/app/vault/vaultAddCipherController.js index 7e77895a06..dab0c62a63 100644 --- a/src/popup/app/vault/vaultAddCipherController.js +++ b/src/popup/app/vault/vaultAddCipherController.js @@ -2,7 +2,7 @@ angular .module('bit.vault') .controller('vaultAddCipherController', function ($scope, $state, $stateParams, cipherService, folderService, - cryptoService, toastr, utilsService, $analytics, i18nService, constantsService, $timeout) { + cryptoService, toastr, browserUtilsService, $analytics, i18nService, constantsService, $timeout) { $scope.i18n = i18nService; $scope.constants = constantsService; $scope.addFieldType = constantsService.fieldType.text.toString(); @@ -31,7 +31,7 @@ angular } $timeout(function () { - utilsService.initListSectionItemListeners(document, angular); + browserUtilsService.initListSectionItemListeners(document, angular); if (!$stateParams.cipher && $scope.cipher.name && $scope.cipher.login && $scope.cipher.login.uri) { document.getElementById('loginUsername').focus(); @@ -49,7 +49,7 @@ angular $scope.cipher.type = parseInt($scope.selectedType); $timeout(function () { - utilsService.initListSectionItemListeners(document, angular); + browserUtilsService.initListSectionItemListeners(document, angular); }, 500); }; @@ -106,7 +106,7 @@ angular }); $timeout(function () { - utilsService.initListSectionItemListeners(document, angular); + browserUtilsService.initListSectionItemListeners(document, angular); }, 500); }; diff --git a/src/popup/app/vault/vaultAttachmentsController.js b/src/popup/app/vault/vaultAttachmentsController.js index 116ac492ae..f4265ea8dc 100644 --- a/src/popup/app/vault/vaultAttachmentsController.js +++ b/src/popup/app/vault/vaultAttachmentsController.js @@ -2,9 +2,9 @@ angular .module('bit.vault') .controller('vaultAttachmentsController', function ($scope, $state, $stateParams, cipherService, toastr, - SweetAlert, utilsService, $analytics, i18nService, cryptoService, tokenService, $timeout) { + SweetAlert, browserUtilsService, $analytics, i18nService, cryptoService, tokenService, $timeout) { $timeout(function () { - utilsService.initListSectionItemListeners(document, angular); + browserUtilsService.initListSectionItemListeners(document, angular); }, 500); $scope.i18n = i18nService; diff --git a/src/popup/app/vault/vaultController.js b/src/popup/app/vault/vaultController.js index 2df6b038a1..37a3c8541d 100644 --- a/src/popup/app/vault/vaultController.js +++ b/src/popup/app/vault/vaultController.js @@ -2,14 +2,14 @@ angular .module('bit.vault') .controller('vaultController', function ($scope, $rootScope, cipherService, folderService, $q, $state, $stateParams, toastr, - syncService, utilsService, $analytics, i18nService, stateService, $timeout, $window, collectionService, $filter) { + syncService, browserUtilsService, $analytics, i18nService, stateService, $timeout, $window, collectionService, $filter) { var stateKey = 'vault', state = stateService.getState(stateKey) || {}; stateService.removeState('viewGrouping'); $scope.i18n = i18nService; - $scope.showGroupingCounts = !utilsService.isEdge(); - $scope.disableSearch = utilsService.isEdge(); + $scope.showGroupingCounts = !browserUtilsService.isEdge(); + $scope.disableSearch = browserUtilsService.isEdge(); document.getElementById('search').focus(); var syncOnLoad = $stateParams.syncOnLoad; diff --git a/src/popup/app/vault/vaultEditCipherController.js b/src/popup/app/vault/vaultEditCipherController.js index 80cbf7c200..64a8b1d28c 100644 --- a/src/popup/app/vault/vaultEditCipherController.js +++ b/src/popup/app/vault/vaultEditCipherController.js @@ -2,15 +2,15 @@ angular .module('bit.vault') .controller('vaultEditCipherController', function ($scope, $state, $stateParams, cipherService, folderService, - cryptoService, toastr, SweetAlert, utilsService, $analytics, i18nService, constantsService, $timeout) { + cryptoService, toastr, SweetAlert, browserUtilsService, $analytics, i18nService, constantsService, $timeout) { $timeout(function () { - utilsService.initListSectionItemListeners(document, angular); + browserUtilsService.initListSectionItemListeners(document, angular); document.getElementById('name').focus(); }, 500); $scope.i18n = i18nService; $scope.constants = constantsService; - $scope.showAttachments = !utilsService.isEdge(); + $scope.showAttachments = !browserUtilsService.isEdge(); $scope.addFieldType = constantsService.fieldType.text.toString(); $scope.selectedType = constantsService.cipherType.login.toString(); var cipherId = $stateParams.cipherId; @@ -40,7 +40,7 @@ angular $scope.cipher.type = parseInt($scope.selectedType); $timeout(function () { - utilsService.initListSectionItemListeners(document, angular); + browserUtilsService.initListSectionItemListeners(document, angular); }, 500); }; @@ -123,7 +123,7 @@ angular }); $timeout(function () { - utilsService.initListSectionItemListeners(document, angular); + browserUtilsService.initListSectionItemListeners(document, angular); }, 500); }; diff --git a/src/popup/app/vault/vaultViewCipherController.js b/src/popup/app/vault/vaultViewCipherController.js index cc01df3bef..f0d6fef4df 100644 --- a/src/popup/app/vault/vaultViewCipherController.js +++ b/src/popup/app/vault/vaultViewCipherController.js @@ -2,11 +2,11 @@ angular .module('bit.vault') .controller('vaultViewCipherController', function ($scope, $state, $stateParams, cipherService, toastr, - $analytics, i18nService, utilsService, totpService, $timeout, tokenService, $window, cryptoService, SweetAlert, + $analytics, i18nService, browserUtilsService, totpService, $timeout, tokenService, $window, cryptoService, SweetAlert, constantsService) { $scope.constants = constantsService; $scope.i18n = i18nService; - $scope.showAttachments = !utilsService.isEdge(); + $scope.showAttachments = !browserUtilsService.isEdge(); var from = $stateParams.from, totpInterval = null; @@ -30,7 +30,7 @@ angular if (model.login.uri) { $scope.cipher.showLaunch = model.login.uri.startsWith('http://') || model.login.uri.startsWith('https://'); - var domain = utilsService.getDomain(model.login.uri); + var domain = browserUtilsService.getDomain(model.login.uri); if (domain) { $scope.cipher.login.website = domain; } diff --git a/src/popup/app/vault/vaultViewGroupingController.js b/src/popup/app/vault/vaultViewGroupingController.js index 961a7d43b7..afe2604746 100644 --- a/src/popup/app/vault/vaultViewGroupingController.js +++ b/src/popup/app/vault/vaultViewGroupingController.js @@ -2,7 +2,7 @@ angular .module('bit.vault') .controller('vaultViewGroupingController', function ($scope, cipherService, folderService, $q, $state, $stateParams, toastr, - syncService, $analytics, i18nService, stateService, utilsService, $timeout, $window, collectionService) { + syncService, $analytics, i18nService, stateService, browserUtilsService, $timeout, $window, collectionService) { var stateKey = 'viewGrouping', state = stateService.getState(stateKey) || {}; @@ -60,7 +60,7 @@ angular var cipherPromise = cipherService.getAllDecryptedForGrouping($scope.grouping.id, $scope.folderGrouping) .then(function (ciphers) { - if (utilsService.isEdge()) { + if (browserUtilsService.isEdge()) { // Edge is super slow at sorting decCiphers = ciphers; } diff --git a/src/scripts/analytics.js b/src/scripts/analytics.js index 868ccea55f..b23164c793 100644 --- a/src/scripts/analytics.js +++ b/src/scripts/analytics.js @@ -9,9 +9,9 @@ return; } - var gaTrackingId = bgMain.utilsService.analyticsId(); + var gaTrackingId = bgMain.browserUtilsService.analyticsId(); var gaFunc = null; - var isFirefox = bgMain.utilsService.isFirefox(); + var isFirefox = bgMain.browserUtilsService.isFirefox(); window.GoogleAnalyticsObject = 'ga'; window[window.GoogleAnalyticsObject] = function (action, param1, param2, param3, param4) { diff --git a/src/services/abstractions/browserUtils.service.ts b/src/services/abstractions/browserUtils.service.ts new file mode 100644 index 0000000000..d23b76e057 --- /dev/null +++ b/src/services/abstractions/browserUtils.service.ts @@ -0,0 +1,17 @@ +import { BrowserType } from '../../enums/browserType.enum'; + +export interface BrowserUtilsService { + getBrowser(): BrowserType; + getBrowserString(): string; + isFirefox(): boolean; + isChrome(): boolean; + isEdge(): boolean; + isOpera(): boolean; + analyticsId(): string; + initListSectionItemListeners(doc: Document, angular: any): void; + getDomain(uriString: string): string; + inSidebar(theWindow: Window): boolean; + inTab(theWindow: Window): boolean; + inPopout(theWindow: Window): boolean; + inPopup(theWindow: Window): boolean; +} diff --git a/src/services/abstractions/utils.service.ts b/src/services/abstractions/utils.service.ts index 348849c604..a251eedeaa 100644 --- a/src/services/abstractions/utils.service.ts +++ b/src/services/abstractions/utils.service.ts @@ -1,19 +1,4 @@ -import { BrowserType } from '../../enums/browserType.enum'; - export interface UtilsService { - getBrowser(): BrowserType; - getBrowserString(): string; - isFirefox(): boolean; - isChrome(): boolean; - isEdge(): boolean; - isOpera(): boolean; - analyticsId(): string; - initListSectionItemListeners(doc: Document, angular: any): void; copyToClipboard(text: string, doc?: Document): void; - getDomain(uriString: string): string; getHostname(uriString: string): string; - inSidebar(theWindow: Window): boolean; - inTab(theWindow: Window): boolean; - inPopout(theWindow: Window): boolean; - inPopup(theWindow: Window): boolean; } diff --git a/src/services/api.service.ts b/src/services/api.service.ts index d97ec6c47a..494eef78b2 100644 --- a/src/services/api.service.ts +++ b/src/services/api.service.ts @@ -1,7 +1,7 @@ import AppIdService from './appId.service'; +import BrowserUtilsService from './browserUtils.service'; import ConstantsService from './constants.service'; import TokenService from './token.service'; -import UtilsService from './utils.service'; import EnvironmentUrls from '../models/domain/environmentUrls'; @@ -32,11 +32,13 @@ export default class ApiService { urlsSet: boolean = false; baseUrl: string; identityBaseUrl: string; + deviceType: string; logoutCallback: Function; - constructor(private tokenService: TokenService, private utilsService: UtilsService, + constructor(private tokenService: TokenService, browserUtilsService: BrowserUtilsService, logoutCallback: Function) { this.logoutCallback = logoutCallback; + this.deviceType = browserUtilsService.getBrowser().toString(); } setUrls(urls: EnvironmentUrls) { @@ -86,7 +88,7 @@ export default class ApiService { headers: new Headers({ 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8', 'Accept': 'application/json', - 'Device-Type': this.utilsService.getBrowser().toString(), + 'Device-Type': this.deviceType, }), method: 'POST', })); @@ -126,7 +128,7 @@ export default class ApiService { cache: 'no-cache', headers: new Headers({ 'Content-Type': 'application/json; charset=utf-8', - 'Device-Type': this.utilsService.getBrowser().toString(), + 'Device-Type': this.deviceType, }), method: 'POST', })); @@ -146,7 +148,7 @@ export default class ApiService { headers: new Headers({ 'Accept': 'application/json', 'Authorization': authHeader, - 'Device-Type': this.utilsService.getBrowser().toString(), + 'Device-Type': this.deviceType, }), })); @@ -164,7 +166,7 @@ export default class ApiService { cache: 'no-cache', headers: new Headers({ 'Content-Type': 'application/json; charset=utf-8', - 'Device-Type': this.utilsService.getBrowser().toString(), + 'Device-Type': this.deviceType, }), method: 'POST', })); @@ -181,7 +183,7 @@ export default class ApiService { cache: 'no-cache', headers: new Headers({ 'Content-Type': 'application/json; charset=utf-8', - 'Device-Type': this.utilsService.getBrowser().toString(), + 'Device-Type': this.deviceType, }), method: 'POST', })); @@ -203,7 +205,7 @@ export default class ApiService { 'Accept': 'application/json', 'Authorization': authHeader, 'Content-Type': 'application/json; charset=utf-8', - 'Device-Type': this.utilsService.getBrowser().toString(), + 'Device-Type': this.deviceType, }), method: 'POST', })); @@ -226,7 +228,7 @@ export default class ApiService { 'Accept': 'application/json', 'Authorization': authHeader, 'Content-Type': 'application/json; charset=utf-8', - 'Device-Type': this.utilsService.getBrowser().toString(), + 'Device-Type': this.deviceType, }), method: 'PUT', })); @@ -246,7 +248,7 @@ export default class ApiService { cache: 'no-cache', headers: new Headers({ 'Authorization': authHeader, - 'Device-Type': this.utilsService.getBrowser().toString(), + 'Device-Type': this.deviceType, }), method: 'DELETE', })); @@ -268,7 +270,7 @@ export default class ApiService { 'Accept': 'application/json', 'Authorization': authHeader, 'Content-Type': 'application/json; charset=utf-8', - 'Device-Type': this.utilsService.getBrowser().toString(), + 'Device-Type': this.deviceType, }), method: 'POST', })); @@ -291,7 +293,7 @@ export default class ApiService { 'Accept': 'application/json', 'Authorization': authHeader, 'Content-Type': 'application/json; charset=utf-8', - 'Device-Type': this.utilsService.getBrowser().toString(), + 'Device-Type': this.deviceType, }), method: 'PUT', })); @@ -311,7 +313,7 @@ export default class ApiService { cache: 'no-cache', headers: new Headers({ 'Authorization': authHeader, - 'Device-Type': this.utilsService.getBrowser().toString(), + 'Device-Type': this.deviceType, }), method: 'DELETE', })); @@ -332,7 +334,7 @@ export default class ApiService { headers: new Headers({ 'Accept': 'application/json', 'Authorization': authHeader, - 'Device-Type': this.utilsService.getBrowser().toString(), + 'Device-Type': this.deviceType, }), method: 'POST', })); @@ -352,7 +354,7 @@ export default class ApiService { cache: 'no-cache', headers: new Headers({ 'Authorization': authHeader, - 'Device-Type': this.utilsService.getBrowser().toString(), + 'Device-Type': this.deviceType, }), method: 'DELETE', })); @@ -372,7 +374,7 @@ export default class ApiService { headers: new Headers({ 'Accept': 'application/json', 'Authorization': authHeader, - 'Device-Type': this.utilsService.getBrowser().toString(), + 'Device-Type': this.deviceType, }), })); @@ -430,7 +432,7 @@ export default class ApiService { headers: new Headers({ 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8', 'Accept': 'application/json', - 'Device-Type': this.utilsService.getBrowser().toString(), + 'Device-Type': this.deviceType, }), method: 'POST', })); diff --git a/src/services/autofill.service.ts b/src/services/autofill.service.ts index 2b2593bffe..945a7a9891 100644 --- a/src/services/autofill.service.ts +++ b/src/services/autofill.service.ts @@ -5,6 +5,7 @@ import AutofillField from '../models/domain/autofillField'; import AutofillPageDetails from '../models/domain/autofillPageDetails'; import AutofillScript from '../models/domain/autofillScript'; +import BrowserUtilsService from './browserUtils.service'; import CipherService from './cipher.service'; import TokenService from './token.service'; import TotpService from './totp.service'; @@ -92,7 +93,8 @@ var IsoProvinces: { [id: string]: string; } = { export default class AutofillService { constructor(public cipherService: CipherService, public tokenService: TokenService, - public totpService: TotpService, public utilsService: UtilsService) { + public totpService: TotpService, public utilsService: UtilsService, + public browserUtilsService: BrowserUtilsService) { } getFormsWithPasswordFields(pageDetails: AutofillPageDetails): any[] { @@ -167,7 +169,7 @@ export default class AutofillService { }, { frameId: pd.frameId }); if (options.cipher.type !== CipherType.Login || totpPromise || - (options.fromBackground && this.utilsService.isFirefox()) || options.skipTotp || + (options.fromBackground && this.browserUtilsService.isFirefox()) || options.skipTotp || !options.cipher.login.totp || !this.tokenService.getPremium()) { return; } @@ -205,7 +207,7 @@ export default class AutofillService { return; } - const tabDomain = UtilsService.getDomain(tab.url); + const tabDomain = BrowserUtilsService.getDomain(tab.url); if (tabDomain == null) { return; } diff --git a/src/services/browserUtils.service.spec.ts b/src/services/browserUtils.service.spec.ts new file mode 100644 index 0000000000..957b1dbc2a --- /dev/null +++ b/src/services/browserUtils.service.spec.ts @@ -0,0 +1,84 @@ +import BrowserUtilsService from './browserUtils.service'; +import { BrowserType } from '../enums/browserType.enum'; + +describe('Browser Utils Service', () => { + describe('getDomain', () => { + it('should fail for invalid urls', () => { + expect(BrowserUtilsService.getDomain(null)).toBeNull(); + expect(BrowserUtilsService.getDomain(undefined)).toBeNull(); + expect(BrowserUtilsService.getDomain(' ')).toBeNull(); + expect(BrowserUtilsService.getDomain('https://bit!:"_&ward.com')).toBeNull(); + expect(BrowserUtilsService.getDomain('bitwarden')).toBeNull(); + }); + + it('should handle urls without protocol', () => { + expect(BrowserUtilsService.getDomain('bitwarden.com')).toBe('bitwarden.com'); + expect(BrowserUtilsService.getDomain('wrong://bitwarden.com')).toBe('bitwarden.com'); + }); + + it('should handle valid urls', () => { + expect(BrowserUtilsService.getDomain('https://bitwarden')).toBe('bitwarden'); + expect(BrowserUtilsService.getDomain('https://bitwarden.com')).toBe('bitwarden.com'); + expect(BrowserUtilsService.getDomain('http://bitwarden.com')).toBe('bitwarden.com'); + expect(BrowserUtilsService.getDomain('http://vault.bitwarden.com')).toBe('bitwarden.com'); + expect(BrowserUtilsService.getDomain('https://user:password@bitwarden.com:8080/password/sites?and&query#hash')).toBe('bitwarden.com'); + expect(BrowserUtilsService.getDomain('https://bitwarden.unknown')).toBe('bitwarden.unknown'); + }); + + it('should support localhost and IP', () => { + expect(BrowserUtilsService.getDomain('https://localhost')).toBe('localhost'); + expect(BrowserUtilsService.getDomain('https://192.168.1.1')).toBe('192.168.1.1'); + }); + }); + + describe('getBrowser', () => { + const original = navigator.userAgent; + + // Reset the userAgent. + afterAll(() => { + Object.defineProperty(navigator, 'userAgent', { + value: original + }); + }); + + it('should detect chrome', () => { + Object.defineProperty(navigator, 'userAgent', { + configurable: true, + value: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36' + }); + + const browserUtilsService = new BrowserUtilsService(); + expect(browserUtilsService.getBrowser()).toBe(BrowserType.Chrome); + }); + + it('should detect firefox', () => { + Object.defineProperty(navigator, 'userAgent', { + configurable: true, + value: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0' + }); + + const browserUtilsService = new BrowserUtilsService(); + expect(browserUtilsService.getBrowser()).toBe(BrowserType.Firefox); + }); + + it('should detect opera', () => { + Object.defineProperty(navigator, 'userAgent', { + configurable: true, + value: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3175.3 Safari/537.36 OPR/49.0.2695.0 (Edition developer)' + }); + + const browserUtilsService = new BrowserUtilsService(); + expect(browserUtilsService.getBrowser()).toBe(BrowserType.Opera); + }); + + it('should detect edge', () => { + Object.defineProperty(navigator, 'userAgent', { + configurable: true, + value: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; ServiceUI 9) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36 Edge/15.15063' + }); + + const browserUtilsService = new BrowserUtilsService(); + expect(browserUtilsService.getBrowser()).toBe(BrowserType.Edge); + }); + }); +}); diff --git a/src/services/browserUtils.service.ts b/src/services/browserUtils.service.ts new file mode 100644 index 0000000000..aa18ac6a88 --- /dev/null +++ b/src/services/browserUtils.service.ts @@ -0,0 +1,215 @@ +import * as tldjs from 'tldjs'; +import { BrowserType } from '../enums/browserType.enum'; +import { BrowserUtilsService as BrowserUtilsServiceInterface } from './abstractions/browserUtils.service'; + +const AnalyticsIds = { + [BrowserType.Chrome]: 'UA-81915606-6', + [BrowserType.Firefox]: 'UA-81915606-7', + [BrowserType.Opera]: 'UA-81915606-8', + [BrowserType.Edge]: 'UA-81915606-9', + [BrowserType.Vivaldi]: 'UA-81915606-15', + [BrowserType.Safari]: 'UA-81915606-16', +}; + +export default class BrowserUtilsService implements BrowserUtilsServiceInterface { + static getDomain(uriString: string): string { + if (uriString == null) { + return null; + } + + uriString = uriString.trim(); + if (uriString === '') { + return null; + } + + if (uriString.startsWith('http://') || uriString.startsWith('https://')) { + try { + const url = new URL(uriString); + + if (url.hostname === 'localhost' || BrowserUtilsService.validIpAddress(url.hostname)) { + return url.hostname; + } + + const urlDomain = tldjs.getDomain(url.hostname); + return urlDomain != null ? urlDomain : url.hostname; + } catch (e) { } + } + + const domain = tldjs.getDomain(uriString); + if (domain != null) { + return domain; + } + + return null; + } + + private static validIpAddress(ipString: string): boolean { + // tslint:disable-next-line + const ipRegex = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; + return ipRegex.test(ipString); + } + + private browserCache: BrowserType = null; + private analyticsIdCache: string = null; + + getBrowser(): BrowserType { + if (this.browserCache) { + return this.browserCache; + } + + if (navigator.userAgent.indexOf('Firefox') !== -1 || navigator.userAgent.indexOf('Gecko/') !== -1) { + this.browserCache = BrowserType.Firefox; + } else if ((!!(window as any).opr && !!opr.addons) || !!(window as any).opera || + navigator.userAgent.indexOf(' OPR/') >= 0) { + this.browserCache = BrowserType.Opera; + } else if (navigator.userAgent.indexOf(' Edge/') !== -1) { + this.browserCache = BrowserType.Edge; + } else if (navigator.userAgent.indexOf(' Vivaldi/') !== -1) { + this.browserCache = BrowserType.Vivaldi; + } else if ((window as any).chrome) { + this.browserCache = BrowserType.Chrome; + } + + return this.browserCache; + } + + getBrowserString(): string { + return BrowserType[this.getBrowser()].toLowerCase(); + } + + isFirefox(): boolean { + return this.getBrowser() === BrowserType.Firefox; + } + + isChrome(): boolean { + return this.getBrowser() === BrowserType.Chrome; + } + + isEdge(): boolean { + return this.getBrowser() === BrowserType.Edge; + } + + isOpera(): boolean { + return this.getBrowser() === BrowserType.Opera; + } + + isVivaldi(): boolean { + return this.getBrowser() === BrowserType.Vivaldi; + } + + isSafari(): boolean { + return this.getBrowser() === BrowserType.Safari; + } + + analyticsId(): string { + if (this.analyticsIdCache) { + return this.analyticsIdCache; + } + + this.analyticsIdCache = AnalyticsIds[this.getBrowser()]; + return this.analyticsIdCache; + } + + initListSectionItemListeners(doc: Document, angular: any): void { + if (!doc) { + throw new Error('doc parameter required'); + } + + const sectionItems = doc.querySelectorAll( + '.list-section-item:not([data-bw-events="1"])'); + const sectionFormItems = doc.querySelectorAll( + '.list-section-item:not([data-bw-events="1"]) input, ' + + '.list-section-item:not([data-bw-events="1"]) select, ' + + '.list-section-item:not([data-bw-events="1"]) textarea'); + + sectionItems.forEach((item) => { + (item as HTMLElement).dataset.bwEvents = '1'; + + item.addEventListener('click', (e) => { + if (e.defaultPrevented) { + return; + } + + const el = e.target as HTMLElement; + + // Some elements will already focus properly + if (el.tagName != null) { + switch (el.tagName.toLowerCase()) { + case 'label': case 'input': case 'textarea': case 'select': + return; + default: + break; + } + } + + const cell = el.closest('.list-section-item'); + if (!cell) { + return; + } + + const textFilter = 'input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"])'; + const text = cell.querySelectorAll(textFilter + ', textarea'); + const checkbox = cell.querySelectorAll('input[type="checkbox"]'); + const select = cell.querySelectorAll('select'); + + if (text.length > 0) { + (text[0] as HTMLElement).focus(); + } else if (select.length > 0) { + (select[0] as HTMLElement).focus(); + } else if (checkbox.length > 0) { + const cb = checkbox[0] as HTMLInputElement; + cb.checked = !cb.checked; + if (angular) { + angular.element(checkbox[0]).triggerHandler('click'); + } + } + }, false); + }); + + sectionFormItems.forEach((item) => { + const itemCell = item.closest('.list-section-item'); + (itemCell as HTMLElement).dataset.bwEvents = '1'; + + item.addEventListener('focus', (e: Event) => { + const el = e.target as HTMLElement; + const cell = el.closest('.list-section-item'); + if (!cell) { + return; + } + + cell.classList.add('active'); + }, false); + + item.addEventListener('blur', (e: Event) => { + const el = e.target as HTMLElement; + const cell = el.closest('.list-section-item'); + if (!cell) { + return; + } + + cell.classList.remove('active'); + }, false); + }); + } + + getDomain(uriString: string): string { + return BrowserUtilsService.getDomain(uriString); + } + + inSidebar(theWindow: Window): boolean { + return theWindow.location.search !== '' && theWindow.location.search.indexOf('uilocation=sidebar') > -1; + } + + inTab(theWindow: Window): boolean { + return theWindow.location.search !== '' && theWindow.location.search.indexOf('uilocation=tab') > -1; + } + + inPopout(theWindow: Window): boolean { + return theWindow.location.search !== '' && theWindow.location.search.indexOf('uilocation=popout') > -1; + } + + inPopup(theWindow: Window): boolean { + return theWindow.location.search === '' || theWindow.location.search.indexOf('uilocation=') === -1 || + theWindow.location.search.indexOf('uilocation=popup') > -1; + } +} diff --git a/src/services/constants.service.ts b/src/services/constants.service.ts index 337c6174a4..a5e49bd250 100644 --- a/src/services/constants.service.ts +++ b/src/services/constants.service.ts @@ -1,4 +1,4 @@ -import UtilsService from './utils.service'; +import BrowserUtilsService from './browserUtils.service'; export default class ConstantsService { static readonly environmentUrlsKey: string = 'environmentUrls'; @@ -57,8 +57,8 @@ export default class ConstantsService { twoFactorProviderInfo: any[]; - constructor(i18nService: any, utilsService: UtilsService) { - if (utilsService.isEdge()) { + constructor(i18nService: any, browserUtilsService: BrowserUtilsService) { + if (browserUtilsService.isEdge()) { // delay for i18n fetch setTimeout(() => { this.bootstrap(i18nService); diff --git a/src/services/i18n.service.ts b/src/services/i18n.service.ts index a294015f3c..931490f4a7 100644 --- a/src/services/i18n.service.ts +++ b/src/services/i18n.service.ts @@ -1,9 +1,9 @@ -import UtilsService from '../services/utils.service'; +import BrowserUtilsService from '../services/browserUtils.service'; -export default function i18nService(utilsService: UtilsService) { +export default function i18nService(browserUtilsService: BrowserUtilsService) { const edgeMessages: any = {}; - if (utilsService.isEdge()) { + if (browserUtilsService.isEdge()) { fetch('../_locales/en/messages.json').then((file) => { return file.json(); }).then((locales) => { diff --git a/src/services/lock.service.ts b/src/services/lock.service.ts index b528b2c136..f65d1d5a8d 100644 --- a/src/services/lock.service.ts +++ b/src/services/lock.service.ts @@ -1,16 +1,16 @@ +import BrowserUtilsService from './browserUtils.service'; import CipherService from './cipher.service'; import CollectionService from './collection.service'; import ConstantsService from './constants.service'; import CryptoService from './crypto.service'; import FolderService from './folder.service'; -import UtilsService from './utils.service'; import { StorageService } from './abstractions/storage.service'; export default class LockService { constructor(private cipherService: CipherService, private folderService: FolderService, private collectionService: CollectionService, private cryptoService: CryptoService, - private utilsService: UtilsService, private storageService: StorageService, + private browserUtilsService: BrowserUtilsService, private storageService: StorageService, private setIcon: Function, private refreshBadgeAndMenu: Function) { this.checkLock(); setInterval(() => this.checkLock(), 10 * 1000); // check every 10 seconds @@ -81,9 +81,9 @@ export default class LockService { // Helpers private sidebarViewName(): string { - if ((window as any).chrome.sidebarAction && this.utilsService.isFirefox()) { + if ((window as any).chrome.sidebarAction && this.browserUtilsService.isFirefox()) { return 'sidebar'; - } else if (this.utilsService.isOpera() && (typeof opr !== 'undefined') && opr.sidebarAction) { + } else if (this.browserUtilsService.isOpera() && (typeof opr !== 'undefined') && opr.sidebarAction) { return 'sidebar_panel'; } diff --git a/src/services/utils.service.spec.ts b/src/services/utils.service.spec.ts index c976888de8..fe3843e1fd 100644 --- a/src/services/utils.service.spec.ts +++ b/src/services/utils.service.spec.ts @@ -1,36 +1,6 @@ import UtilsService from './utils.service'; -import { BrowserType } from '../enums/browserType.enum'; describe('Utils Service', () => { - describe('getDomain', () => { - it('should fail for invalid urls', () => { - expect(UtilsService.getDomain(null)).toBeNull(); - expect(UtilsService.getDomain(undefined)).toBeNull(); - expect(UtilsService.getDomain(' ')).toBeNull(); - expect(UtilsService.getDomain('https://bit!:"_&ward.com')).toBeNull(); - expect(UtilsService.getDomain('bitwarden')).toBeNull(); - }); - - it('should handle urls without protocol', () => { - expect(UtilsService.getDomain('bitwarden.com')).toBe('bitwarden.com'); - expect(UtilsService.getDomain('wrong://bitwarden.com')).toBe('bitwarden.com'); - }); - - it('should handle valid urls', () => { - expect(UtilsService.getDomain('https://bitwarden')).toBe('bitwarden'); - expect(UtilsService.getDomain('https://bitwarden.com')).toBe('bitwarden.com'); - expect(UtilsService.getDomain('http://bitwarden.com')).toBe('bitwarden.com'); - expect(UtilsService.getDomain('http://vault.bitwarden.com')).toBe('bitwarden.com'); - expect(UtilsService.getDomain('https://user:password@bitwarden.com:8080/password/sites?and&query#hash')).toBe('bitwarden.com'); - expect(UtilsService.getDomain('https://bitwarden.unknown')).toBe('bitwarden.unknown'); - }); - - it('should support localhost and IP', () => { - expect(UtilsService.getDomain('https://localhost')).toBe('localhost'); - expect(UtilsService.getDomain('https://192.168.1.1')).toBe('192.168.1.1'); - }); - }); - describe('getHostname', () => { it('should fail for invalid urls', () => { expect(UtilsService.getHostname(null)).toBeNull(); @@ -59,55 +29,4 @@ describe('Utils Service', () => { expect(UtilsService.newGuid()).toMatch(validGuid); }); }); - - describe('getBrowser', () => { - const original = navigator.userAgent; - - // Reset the userAgent. - afterAll(() => { - Object.defineProperty(navigator, 'userAgent', { - value: original - }); - }); - - it('should detect chrome', () => { - Object.defineProperty(navigator, 'userAgent', { - configurable: true, - value: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36' - }); - - const utilsService = new UtilsService(); - expect(utilsService.getBrowser()).toBe(BrowserType.Chrome); - }); - - it('should detect firefox', () => { - Object.defineProperty(navigator, 'userAgent', { - configurable: true, - value: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0' - }); - - const utilsService = new UtilsService(); - expect(utilsService.getBrowser()).toBe(BrowserType.Firefox); - }); - - it('should detect opera', () => { - Object.defineProperty(navigator, 'userAgent', { - configurable: true, - value: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3175.3 Safari/537.36 OPR/49.0.2695.0 (Edition developer)' - }); - - const utilsService = new UtilsService(); - expect(utilsService.getBrowser()).toBe(BrowserType.Opera); - }); - - it('should detect edge', () => { - Object.defineProperty(navigator, 'userAgent', { - configurable: true, - value: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; ServiceUI 9) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36 Edge/15.15063' - }); - - const utilsService = new UtilsService(); - expect(utilsService.getBrowser()).toBe(BrowserType.Edge); - }); - }); }); diff --git a/src/services/utils.service.ts b/src/services/utils.service.ts index 53e5df7cfd..d16438924d 100644 --- a/src/services/utils.service.ts +++ b/src/services/utils.service.ts @@ -1,16 +1,5 @@ -import * as tldjs from 'tldjs'; -import { BrowserType } from '../enums/browserType.enum'; import { UtilsService as UtilsServiceInterface } from './abstractions/utils.service'; -const AnalyticsIds = { - [BrowserType.Chrome]: 'UA-81915606-6', - [BrowserType.Firefox]: 'UA-81915606-7', - [BrowserType.Opera]: 'UA-81915606-8', - [BrowserType.Edge]: 'UA-81915606-9', - [BrowserType.Vivaldi]: 'UA-81915606-15', - [BrowserType.Safari]: 'UA-81915606-16', -}; - export default class UtilsService implements UtilsServiceInterface { static copyToClipboard(text: string, doc?: Document): void { doc = doc || document; @@ -136,37 +125,6 @@ export default class UtilsService implements UtilsServiceInterface { return decodeURIComponent(escape(encodedString)); } - static getDomain(uriString: string): string { - if (uriString == null) { - return null; - } - - uriString = uriString.trim(); - if (uriString === '') { - return null; - } - - if (uriString.startsWith('http://') || uriString.startsWith('https://')) { - try { - const url = new URL(uriString); - - if (url.hostname === 'localhost' || UtilsService.validIpAddress(url.hostname)) { - return url.hostname; - } - - const urlDomain = tldjs.getDomain(url.hostname); - return urlDomain != null ? urlDomain : url.hostname; - } catch (e) { } - } - - const domain = tldjs.getDomain(uriString); - if (domain != null) { - return domain; - } - - return null; - } - static getHostname(uriString: string): string { if (uriString == null) { return null; @@ -187,159 +145,6 @@ export default class UtilsService implements UtilsServiceInterface { return null; } - private static validIpAddress(ipString: string): boolean { - // tslint:disable-next-line - const ipRegex = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; - return ipRegex.test(ipString); - } - - private browserCache: BrowserType = null; - private analyticsIdCache: string = null; - - getBrowser(): BrowserType { - if (this.browserCache) { - return this.browserCache; - } - - if (navigator.userAgent.indexOf('Firefox') !== -1 || navigator.userAgent.indexOf('Gecko/') !== -1) { - this.browserCache = BrowserType.Firefox; - } else if ((!!(window as any).opr && !!opr.addons) || !!(window as any).opera || - navigator.userAgent.indexOf(' OPR/') >= 0) { - this.browserCache = BrowserType.Opera; - } else if (navigator.userAgent.indexOf(' Edge/') !== -1) { - this.browserCache = BrowserType.Edge; - } else if (navigator.userAgent.indexOf(' Vivaldi/') !== -1) { - this.browserCache = BrowserType.Vivaldi; - } else if ((window as any).chrome) { - this.browserCache = BrowserType.Chrome; - } - - return this.browserCache; - } - - getBrowserString(): string { - return BrowserType[this.getBrowser()].toLowerCase(); - } - - isFirefox(): boolean { - return this.getBrowser() === BrowserType.Firefox; - } - - isChrome(): boolean { - return this.getBrowser() === BrowserType.Chrome; - } - - isEdge(): boolean { - return this.getBrowser() === BrowserType.Edge; - } - - isOpera(): boolean { - return this.getBrowser() === BrowserType.Opera; - } - - isVivaldi(): boolean { - return this.getBrowser() === BrowserType.Vivaldi; - } - - isSafari(): boolean { - return this.getBrowser() === BrowserType.Safari; - } - - analyticsId(): string { - if (this.analyticsIdCache) { - return this.analyticsIdCache; - } - - this.analyticsIdCache = AnalyticsIds[this.getBrowser()]; - return this.analyticsIdCache; - } - - initListSectionItemListeners(doc: Document, angular: any): void { - if (!doc) { - throw new Error('doc parameter required'); - } - - const sectionItems = doc.querySelectorAll( - '.list-section-item:not([data-bw-events="1"])'); - const sectionFormItems = doc.querySelectorAll( - '.list-section-item:not([data-bw-events="1"]) input, ' + - '.list-section-item:not([data-bw-events="1"]) select, ' + - '.list-section-item:not([data-bw-events="1"]) textarea'); - - sectionItems.forEach((item) => { - (item as HTMLElement).dataset.bwEvents = '1'; - - item.addEventListener('click', (e) => { - if (e.defaultPrevented) { - return; - } - - const el = e.target as HTMLElement; - - // Some elements will already focus properly - if (el.tagName != null) { - switch (el.tagName.toLowerCase()) { - case 'label': case 'input': case 'textarea': case 'select': - return; - default: - break; - } - } - - const cell = el.closest('.list-section-item'); - if (!cell) { - return; - } - - const textFilter = 'input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"])'; - const text = cell.querySelectorAll(textFilter + ', textarea'); - const checkbox = cell.querySelectorAll('input[type="checkbox"]'); - const select = cell.querySelectorAll('select'); - - if (text.length > 0) { - (text[0] as HTMLElement).focus(); - } else if (select.length > 0) { - (select[0] as HTMLElement).focus(); - } else if (checkbox.length > 0) { - const cb = checkbox[0] as HTMLInputElement; - cb.checked = !cb.checked; - if (angular) { - angular.element(checkbox[0]).triggerHandler('click'); - } - } - }, false); - }); - - sectionFormItems.forEach((item) => { - const itemCell = item.closest('.list-section-item'); - (itemCell as HTMLElement).dataset.bwEvents = '1'; - - item.addEventListener('focus', (e: Event) => { - const el = e.target as HTMLElement; - const cell = el.closest('.list-section-item'); - if (!cell) { - return; - } - - cell.classList.add('active'); - }, false); - - item.addEventListener('blur', (e: Event) => { - const el = e.target as HTMLElement; - const cell = el.closest('.list-section-item'); - if (!cell) { - return; - } - - cell.classList.remove('active'); - }, false); - }); - } - - getDomain(uriString: string): string { - return UtilsService.getDomain(uriString); - } - getHostname(uriString: string): string { return UtilsService.getHostname(uriString); } @@ -347,21 +152,4 @@ export default class UtilsService implements UtilsServiceInterface { copyToClipboard(text: string, doc?: Document) { UtilsService.copyToClipboard(text, doc); } - - inSidebar(theWindow: Window): boolean { - return theWindow.location.search !== '' && theWindow.location.search.indexOf('uilocation=sidebar') > -1; - } - - inTab(theWindow: Window): boolean { - return theWindow.location.search !== '' && theWindow.location.search.indexOf('uilocation=tab') > -1; - } - - inPopout(theWindow: Window): boolean { - return theWindow.location.search !== '' && theWindow.location.search.indexOf('uilocation=popout') > -1; - } - - inPopup(theWindow: Window): boolean { - return theWindow.location.search === '' || theWindow.location.search.indexOf('uilocation=') === -1 || - theWindow.location.search.indexOf('uilocation=popup') > -1; - } }