diff --git a/src/popup/app/accounts/accountsHintController.js b/src/popup/app/accounts/accountsHintController.js index f81c8e736b..b686e04adf 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, platformUtilsService, + .controller('accountsHintController', function ($scope, $state, apiService, toastr, $q, popupUtilsService, $analytics, i18nService, $timeout) { $timeout(function () { - platformUtilsService.initListSectionItemListeners(document, angular); + popupUtilsService.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 ed98a791ec..5ad41060e2 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, - platformUtilsService, $analytics, i18nService, $timeout) { + popupUtilsService, $analytics, i18nService, $timeout) { $timeout(function () { - platformUtilsService.initListSectionItemListeners(document, angular); + popupUtilsService.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 4f384a3009..dd7a5dcfe2 100644 --- a/src/popup/app/accounts/accountsLoginTwoFactorController.js +++ b/src/popup/app/accounts/accountsLoginTwoFactorController.js @@ -3,9 +3,9 @@ angular .controller('accountsLoginTwoFactorController', function ($scope, $state, authService, toastr, platformUtilsService, $analytics, i18nService, $stateParams, $filter, constantsService, $timeout, $window, cryptoService, apiService, - environmentService, SweetAlert) { + environmentService, SweetAlert, popupUtilsService) { $timeout(function () { - platformUtilsService.initListSectionItemListeners(document, angular); + popupUtilsService.initListSectionItemListeners(document, angular); }, 500); $scope.i18n = i18nService; diff --git a/src/popup/app/accounts/accountsRegisterController.js b/src/popup/app/accounts/accountsRegisterController.js index 89943b84d8..902ddb41c8 100644 --- a/src/popup/app/accounts/accountsRegisterController.js +++ b/src/popup/app/accounts/accountsRegisterController.js @@ -3,10 +3,10 @@ angular .controller( 'accountsRegisterController', - function ($scope, $state, cryptoService, toastr, $q, apiService, platformUtilsService, $analytics, + function ($scope, $state, cryptoService, toastr, $q, apiService, popupUtilsService, $analytics, i18nService, $timeout) { $timeout(function () { - platformUtilsService.initListSectionItemListeners(document, angular); + popupUtilsService.initListSectionItemListeners(document, angular); document.getElementById('email').focus(); }, 500); diff --git a/src/popup/app/lock/lock.component.ts b/src/popup/app/lock/lock.component.ts index ca90e8bcfa..e6f6036b9f 100644 --- a/src/popup/app/lock/lock.component.ts +++ b/src/popup/app/lock/lock.component.ts @@ -3,15 +3,15 @@ import * as template from './lock.component.html'; import { CryptoService } from 'jslib/abstractions/crypto.service'; import { MessagingService } from 'jslib/abstractions/messaging.service'; -import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service'; import { UserService } from 'jslib/abstractions/user.service'; +import PopupUtilsService from '../services/popupUtils.service'; + export class LockController { i18n: any; masterPassword: string; constructor(public $state: any, public i18nService: any, private $timeout: ng.ITimeoutService, - private platformUtilsService: PlatformUtilsService, public cryptoService: CryptoService, public toastr: any, public userService: UserService, public messagingService: MessagingService, public SweetAlert: any) { this.i18n = i18nService; @@ -19,7 +19,7 @@ export class LockController { $onInit() { this.$timeout(() => { - this.platformUtilsService.initListSectionItemListeners(document, angular); + PopupUtilsService.initListSectionItemListeners(document, angular); document.getElementById('master-password').focus(); }, 500); } diff --git a/src/popup/app/services/popupUtils.service.ts b/src/popup/app/services/popupUtils.service.ts new file mode 100644 index 0000000000..dc6175d752 --- /dev/null +++ b/src/popup/app/services/popupUtils.service.ts @@ -0,0 +1,87 @@ +export default class PopupUtilsService { + static 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); + }); + } + + initListSectionItemListeners(doc: Document, angular: any): void { + PopupUtilsService.initListSectionItemListeners(doc, angular); + } +} diff --git a/src/popup/app/services/services.module.ts b/src/popup/app/services/services.module.ts index 3395db0d65..696ac8414f 100644 --- a/src/popup/app/services/services.module.ts +++ b/src/popup/app/services/services.module.ts @@ -1,6 +1,7 @@ import * as angular from 'angular'; import AuthService from './auth.service'; import * as backgroundServices from './background.service'; +import PopupUtilsService from './popupUtils.service'; import StateService from './state.service'; import { ValidationService } from './validation.service'; @@ -13,6 +14,7 @@ export default angular .service('stateService', StateService) .service('validationService', ValidationService) .service('authService', AuthService) + .service('popupUtilsService', PopupUtilsService) .factory('messagingService', () => messagingService) .factory('storageService', backgroundServices.storageService) diff --git a/src/popup/app/settings/environment.component.ts b/src/popup/app/settings/environment.component.ts index 62d7a592a8..e5e07c1150 100644 --- a/src/popup/app/settings/environment.component.ts +++ b/src/popup/app/settings/environment.component.ts @@ -2,7 +2,8 @@ import * as angular from 'angular'; import * as template from './environment.component.html'; import { EnvironmentService } from 'jslib/abstractions/environment.service'; -import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service'; + +import PopupUtilsService from '../services/popupUtils.service'; export class EnvironmentController { iconsUrl: string; @@ -12,12 +13,12 @@ export class EnvironmentController { baseUrl: string; i18n: any; - constructor(private i18nService: any, private $analytics: any, private platformUtilsService: PlatformUtilsService, + constructor(private i18nService: any, private $analytics: any, private environmentService: EnvironmentService, private toastr: any, private $timeout: ng.ITimeoutService) { this.i18n = i18nService; $timeout(() => { - platformUtilsService.initListSectionItemListeners(document, angular); + PopupUtilsService.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 547a5dc902..123c5db461 100644 --- a/src/popup/app/settings/folders/add-folder.component.ts +++ b/src/popup/app/settings/folders/add-folder.component.ts @@ -4,7 +4,8 @@ import * as template from './add-folder.component.html'; import { Folder } from 'jslib/models/domain/folder'; import { FolderService } from 'jslib/abstractions/folder.service'; -import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service'; + +import PopupUtilsService from '../../services/popupUtils.service'; export class AddFolderController { savePromise: any; @@ -12,10 +13,9 @@ export class AddFolderController { i18n: any; constructor(private folderService: FolderService, private $state: any, private toastr: any, - platformUtilsService: PlatformUtilsService, private $analytics: any, private i18nService: any, - $timeout: ng.ITimeoutService) { + private $analytics: any, private i18nService: any, $timeout: ng.ITimeoutService) { $timeout(() => { - platformUtilsService.initListSectionItemListeners(document, angular); + PopupUtilsService.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 8763e64be6..7084f7e711 100644 --- a/src/popup/app/settings/folders/edit-folder.component.ts +++ b/src/popup/app/settings/folders/edit-folder.component.ts @@ -4,7 +4,8 @@ import * as template from './edit-folder.component.html'; import { Folder } from 'jslib/models/domain/folder'; import { FolderService } from 'jslib/abstractions/folder.service'; -import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service'; + +import PopupUtilsService from '../../services/popupUtils.service'; export class EditFolderController { $transition$: any; @@ -14,12 +15,12 @@ export class EditFolderController { folder: Folder; constructor($scope: any, $stateParams: any, private folderService: FolderService, private toastr: any, - private $state: any, private SweetAlert: any, platformUtilsService: PlatformUtilsService, - private $analytics: any, private i18nService: any, $timeout: ng.ITimeoutService) { + private $state: any, private SweetAlert: any, private $analytics: any, private i18nService: any, + $timeout: ng.ITimeoutService) { this.i18n = i18nService; $timeout(() => { - platformUtilsService.initListSectionItemListeners(document, angular); + PopupUtilsService.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 a9833c51cc..ea8f64a1cc 100644 --- a/src/popup/app/settings/options.component.ts +++ b/src/popup/app/settings/options.component.ts @@ -8,6 +8,7 @@ import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service'; import { StorageService } from 'jslib/abstractions/storage.service'; import { TotpService } from 'jslib/abstractions/totp.service'; +import PopupUtilsService from '../services/popupUtils.service'; import StateService from '../services/state.service'; export class OptionsController { @@ -26,7 +27,7 @@ export class OptionsController { this.i18n = i18nService; $timeout(() => { - platformUtilsService.initListSectionItemListeners(document, angular); + PopupUtilsService.initListSectionItemListeners(document, angular); }, 500); this.loadSettings(); diff --git a/src/popup/app/settings/settings.component.ts b/src/popup/app/settings/settings.component.ts index 08edd149ec..24a389b537 100644 --- a/src/popup/app/settings/settings.component.ts +++ b/src/popup/app/settings/settings.component.ts @@ -1,4 +1,5 @@ import * as angular from 'angular'; +import * as template from './settings.component.html'; import { DeviceType } from 'jslib/enums/deviceType'; @@ -10,7 +11,7 @@ import { MessagingService } from 'jslib/abstractions/messaging.service'; import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service'; import { StorageService } from 'jslib/abstractions/storage.service'; -import * as template from './settings.component.html'; +import PopupUtilsService from '../services/popupUtils.service'; const RateUrls = { [DeviceType.Chrome]: @@ -41,7 +42,7 @@ export class SettingsController { this.i18n = i18nService; $timeout(() => { - platformUtilsService.initListSectionItemListeners(document, angular); + PopupUtilsService.initListSectionItemListeners(document, angular); }, 500); this.showOnLocked = !platformUtilsService.isFirefox() && !platformUtilsService.isEdge(); diff --git a/src/popup/app/tools/password-generator.component.ts b/src/popup/app/tools/password-generator.component.ts index e4a32b1958..08a5212f7b 100644 --- a/src/popup/app/tools/password-generator.component.ts +++ b/src/popup/app/tools/password-generator.component.ts @@ -2,7 +2,8 @@ import * as angular from 'angular'; import * as template from './password-generator.component.html'; import { PasswordGenerationService } from 'jslib/abstractions/passwordGeneration.service'; -import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service'; + +import PopupUtilsService from '../services/popupUtils.service'; export class PasswordGeneratorController { $transition$: any; @@ -14,8 +15,8 @@ export class PasswordGeneratorController { i18n: any; constructor(private $state: any, private passwordGenerationService: PasswordGenerationService, - private toastr: any, private platformUtilsService: PlatformUtilsService, - private $analytics: any, private i18nService: any, private $timeout: ng.ITimeoutService) { + private toastr: any, private $analytics: any, private i18nService: any, + private $timeout: ng.ITimeoutService) { this.i18n = i18nService; passwordGenerationService.getOptions().then((options: any) => { @@ -43,7 +44,7 @@ export class PasswordGeneratorController { this.showSelect = this.addState || this.editState; this.$timeout(() => { - this.platformUtilsService.initListSectionItemListeners(document, angular); + PopupUtilsService.initListSectionItemListeners(document, angular); }, 500); } diff --git a/src/popup/app/vault/vaultAddCipherController.js b/src/popup/app/vault/vaultAddCipherController.js index d2bb310978..101c44d106 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, platformUtilsService, $analytics, i18nService, constantsService, $timeout) { + cryptoService, toastr, popupUtilsService, $analytics, i18nService, constantsService, $timeout) { $scope.i18n = i18nService; $scope.constants = constantsService; $scope.addFieldType = constantsService.fieldType.text.toString(); @@ -31,7 +31,7 @@ angular } $timeout(function () { - platformUtilsService.initListSectionItemListeners(document, angular); + popupUtilsService.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 () { - platformUtilsService.initListSectionItemListeners(document, angular); + popupUtilsService.initListSectionItemListeners(document, angular); }, 500); }; @@ -106,7 +106,7 @@ angular }); $timeout(function () { - platformUtilsService.initListSectionItemListeners(document, angular); + popupUtilsService.initListSectionItemListeners(document, angular); }, 500); }; diff --git a/src/popup/app/vault/vaultAttachmentsController.js b/src/popup/app/vault/vaultAttachmentsController.js index ea196fcb00..ad09a25c83 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, platformUtilsService, $analytics, i18nService, cryptoService, tokenService, $timeout) { + SweetAlert, popupUtilsService, $analytics, i18nService, cryptoService, tokenService, $timeout) { $timeout(function () { - platformUtilsService.initListSectionItemListeners(document, angular); + popupUtilsService.initListSectionItemListeners(document, angular); }, 500); $scope.i18n = i18nService; diff --git a/src/popup/app/vault/vaultEditCipherController.js b/src/popup/app/vault/vaultEditCipherController.js index d8b6e83de3..d9c4016aac 100644 --- a/src/popup/app/vault/vaultEditCipherController.js +++ b/src/popup/app/vault/vaultEditCipherController.js @@ -2,9 +2,10 @@ angular .module('bit.vault') .controller('vaultEditCipherController', function ($scope, $state, $stateParams, cipherService, folderService, - cryptoService, toastr, SweetAlert, platformUtilsService, $analytics, i18nService, constantsService, $timeout) { + cryptoService, toastr, SweetAlert, platformUtilsService, $analytics, i18nService, constantsService, $timeout, + popupUtilsService) { $timeout(function () { - platformUtilsService.initListSectionItemListeners(document, angular); + popupUtilsService.initListSectionItemListeners(document, angular); document.getElementById('name').focus(); }, 500); @@ -40,7 +41,7 @@ angular $scope.cipher.type = parseInt($scope.selectedType); $timeout(function () { - platformUtilsService.initListSectionItemListeners(document, angular); + popupUtilsService.initListSectionItemListeners(document, angular); }, 500); }; @@ -123,7 +124,7 @@ angular }); $timeout(function () { - platformUtilsService.initListSectionItemListeners(document, angular); + popupUtilsService.initListSectionItemListeners(document, angular); }, 500); }; diff --git a/src/services/browserPlatformUtils.service.ts b/src/services/browserPlatformUtils.service.ts index e96d776b0c..8bdf75ee1b 100644 --- a/src/services/browserPlatformUtils.service.ts +++ b/src/services/browserPlatformUtils.service.ts @@ -112,88 +112,6 @@ export default class BrowserPlatformUtilsService implements PlatformUtilsService 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 BrowserPlatformUtilsService.getDomain(uriString); }