diff --git a/src/js/app.js b/src/js/app.js index 44050be..d54d92f 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -63,7 +63,6 @@ var app = angular.module('mail', [ 'read', 'contacts', 'login-new-device', - 'privatekey-upload', 'infinite-scroll' ]); diff --git a/src/js/controller/app/privatekey-upload.js b/src/js/controller/app/privatekey-upload.js index 779a101..eb70450 100644 --- a/src/js/controller/app/privatekey-upload.js +++ b/src/js/controller/app/privatekey-upload.js @@ -33,26 +33,6 @@ var PrivateKeyUploadCtrl = function($scope, keychain, pgp, dialog, auth) { } }; - $scope.handlePaste = function(event) { - var evt = event; - if (evt.originalEvent) { - evt = evt.originalEvent; - } - - var value = evt.clipboardData.getData('text/plain'); - if (!value) { - return; - } - - value = value.replace(/-/g, ''); - $scope.code0 = value.slice(0, 4); - $scope.code1 = value.slice(4, 8); - $scope.code2 = value.slice(8, 12); - $scope.code3 = value.slice(12, 16); - $scope.code4 = value.slice(16, 20); - $scope.code5 = value.slice(20, 24); - }; - $scope.checkServerForKey = function(callback) { var keyParams = pgp.getKeyParams(); keychain.hasPrivateKey({ @@ -80,14 +60,12 @@ var PrivateKeyUploadCtrl = function($scope, keychain, pgp, dialog, auth) { $scope.code = util.randomString(24); $scope.displayedCode = $scope.code.slice(0, 4) + '-' + $scope.code.slice(4, 8) + '-' + $scope.code.slice(8, 12) + '-' + $scope.code.slice(12, 16) + '-' + $scope.code.slice(16, 20) + '-' + $scope.code.slice(20, 24); - // clear input fields of any previous artifacts - $scope.code0 = $scope.code1 = $scope.code2 = $scope.code3 = $scope.code4 = $scope.code5 = ''; + // clear input field of any previous artifacts + $scope.inputCode = ''; }; $scope.verifyCode = function() { - var inputCode = '' + $scope.code0 + $scope.code1 + $scope.code2 + $scope.code3 + $scope.code4 + $scope.code5; - - if (inputCode.toUpperCase() !== $scope.code) { + if ($scope.inputCode.toUpperCase() !== $scope.code) { var err = new Error('The code does not match. Please go back and check the generated code.'); dialog.error(err); return false; @@ -171,26 +149,4 @@ var PrivateKeyUploadCtrl = function($scope, keychain, pgp, dialog, auth) { }; -// -// Directives -// - -var ngModule = angular.module('privatekey-upload', []); -ngModule.directive('focusNext', function() { - return { - link: function(scope, element, attr) { - var maxLen = element[0].maxLength; - - scope.$watch(attr.ngModel, function(val) { - if (val && val.length === maxLen) { - var nextinput = element.next('input'); - if (nextinput.length) { - nextinput[0].focus(); - } - } - }); - } - }; -}); - module.exports = PrivateKeyUploadCtrl; \ No newline at end of file diff --git a/src/js/controller/login/login-privatekey-download.js b/src/js/controller/login/login-privatekey-download.js index 2d9715b..5c3a58c 100644 --- a/src/js/controller/login/login-privatekey-download.js +++ b/src/js/controller/login/login-privatekey-download.js @@ -70,10 +70,8 @@ var LoginPrivateKeyDownloadCtrl = function($scope, $location, $routeParams, auth }; $scope.decryptAndStorePrivateKeyLocally = function() { - var inputCode = '' + $scope.code0 + $scope.code1 + $scope.code2 + $scope.code3 + $scope.code4 + $scope.code5; - var options = $scope.encryptedPrivateKey; - options.code = inputCode.toUpperCase(); + options.code = $scope.code.toUpperCase(); keychain.decryptAndStorePrivateKeyLocally(options, function(err, privateKey) { if (err) { @@ -108,26 +106,6 @@ var LoginPrivateKeyDownloadCtrl = function($scope, $location, $routeParams, auth }); }; - $scope.handlePaste = function(event) { - var evt = event; - if (evt.originalEvent) { - evt = evt.originalEvent; - } - - var value = evt.clipboardData.getData('text/plain'); - if (!value) { - return; - } - - value = value.replace(/-/g, ''); - $scope.code0 = value.slice(0, 4); - $scope.code1 = value.slice(4, 8); - $scope.code2 = value.slice(8, 12); - $scope.code3 = value.slice(12, 16); - $scope.code4 = value.slice(16, 20); - $scope.code5 = value.slice(20, 24); - }; - // // helper functions // diff --git a/src/js/directive/common.js b/src/js/directive/common.js index bc8a74d..fac43ce 100644 --- a/src/js/directive/common.js +++ b/src/js/directive/common.js @@ -173,4 +173,135 @@ ngModule.directive('woClickFileInput', function() { }; }); +ngModule.directive('woInputCode', function() { + var BLOCK_SIZE = 4; + var NUM_BLOCKS = 6; + var BLOCK_DIVIDER = '-'; + + // helpers + + function getBlockIndex(code, pos) { + return code.substr(0, pos).replace(new RegExp('[^' + BLOCK_DIVIDER + ']', 'g'), '').length; + } + + function getBlockDimensions(code, i) { + var start = 0; + var end = code.length; + var found = 0; + for(var j = 0; j < code.length; j++) { + if(code[j] === BLOCK_DIVIDER) { + found++; + if(found === i) { + start = j + 1; + } + if(found === i + 1) { + end = j - 1; + } + } + } + + return { + start: start, + end: end + }; + } + + function getBlock(code, i) { + var dims = getBlockDimensions(code, i); + return code.substring(dims.start, dims.end + 1); + } + + function setBlock(code, i, val) { + var dims = getBlockDimensions(code, i); + return code.substring(0, dims.start) + val + code.substring(dims.end + 1); + } + + return { + restrict: 'A', + require: 'ngModel', + link: function(scope, elm, attrs, ngModelCtrl) { + function format(val) { + var str = ''; + + // check if value exists + if (!val) { + return str; + } + + for(var i = 0; i < val.length; i++) { + if(i > 0 && i % BLOCK_SIZE === 0) { + str += BLOCK_DIVIDER; + } + str += val[i]; + } + + return str; + } + + function parse(val) { + var parsed = val.replace(new RegExp(BLOCK_DIVIDER, 'g'), '').trim(); + return parsed; + } + + ngModelCtrl.$parsers.push(parse); + ngModelCtrl.$formatters.push(format); + + var maxlength = NUM_BLOCKS * (BLOCK_SIZE + 1) - 1; + + elm.on('input', function() { + var start = this.selectionStart; + var end = this.selectionEnd; + var val = this.value; + + var blockIndex = getBlockIndex(val, start); + var blockDims = getBlockDimensions(val, blockIndex); + var block = getBlock(val, blockIndex); + + // add new block to the end + if(val.length < maxlength && start === val.length && block.length > BLOCK_SIZE) { + val = setBlock(val, blockIndex, block.substr(0, BLOCK_SIZE) + BLOCK_DIVIDER + block.substr(BLOCK_SIZE)); + start = val.length; + end = val.length; + } + // maxsize in last block + else if(start === val.length && block.length > BLOCK_SIZE) { + val = setBlock(val, blockIndex, block.substr(0, BLOCK_SIZE)); + start = val.length; + end = val.length; + } + // overwrite next char if block is full + else if(block.length > BLOCK_SIZE && start <= blockDims.end) { + val = val.substring(0, start) + val.substring(start + 1); + } + // jump to next block if cursor is at the end of the block and block is full + else if(block.length > BLOCK_SIZE && start === blockDims.end + 1) { + var overflow = block.substr(BLOCK_SIZE); + if(overflow.length > BLOCK_SIZE) { + overflow = overflow.substr(0, BLOCK_SIZE); + } + val = setBlock(val, blockIndex, block.substr(0, BLOCK_SIZE)); + + var nextBlock = getBlock(val, blockIndex + 1); + val = setBlock(val, blockIndex + 1, (overflow + nextBlock).substr(0, BLOCK_SIZE)); + + start++; + end++; + } + + ngModelCtrl.$setViewValue(val); + ngModelCtrl.$render(); + + this.setSelectionRange(start, end); + }); + + elm.on('click', function() { + var blockIndex = getBlockIndex(this.value, this.selectionStart); + var dims = getBlockDimensions(this.value, blockIndex); + + this.setSelectionRange(dims.start, dims.end + 1); + }); + } + }; +}); + module.exports = ngModule; \ No newline at end of file diff --git a/src/tpl/login-privatekey-download.html b/src/tpl/login-privatekey-download.html index 578bbc8..af4601e 100644 --- a/src/tpl/login-privatekey-download.html +++ b/src/tpl/login-privatekey-download.html @@ -43,14 +43,8 @@

{{errMsg}}

-
- - - - - - - - - - - -
+
diff --git a/src/tpl/privatekey-upload.html b/src/tpl/privatekey-upload.html index 764565a..986960b 100644 --- a/src/tpl/privatekey-upload.html +++ b/src/tpl/privatekey-upload.html @@ -25,14 +25,8 @@

Please confirm the keychain code you have written down.

-
- - - - - - - - - - - -
+
diff --git a/test/unit/controller/app/privatekey-upload-ctrl-test.js b/test/unit/controller/app/privatekey-upload-ctrl-test.js index 1ff65df..a7a28c7 100644 --- a/test/unit/controller/app/privatekey-upload-ctrl-test.js +++ b/test/unit/controller/app/privatekey-upload-ctrl-test.js @@ -78,70 +78,33 @@ describe('Private Key Upload Controller unit test', function() { }); }); - describe('handlePaste', function() { - it('should work', function() { - scope.handlePaste({ - clipboardData: { - getData: function(val) { - expect(val).to.equal('text/plain'); - return '1qaz-2wsx-3edc-4rfv-5tgb-6yhn'; - } - } - }); - - expect(scope.code0).to.equal('1qaz'); - expect(scope.code1).to.equal('2wsx'); - expect(scope.code2).to.equal('3edc'); - expect(scope.code3).to.equal('4rfv'); - expect(scope.code4).to.equal('5tgb'); - expect(scope.code5).to.equal('6yhn'); - }); - }); - describe('displayUploadUi', function() { it('should work', function() { // add some artifacts from a previous key input - scope.code0 = scope.code1 = scope.code2 = scope.code3 = scope.code4 = scope.code5 = 'asdasd'; + scope.inputCode = 'asdasd'; scope.displayUploadUi(); expect(scope.step).to.equal(1); expect(scope.code.length).to.equal(24); // artifacts should be cleared - expect(scope.code0).to.be.empty; - expect(scope.code1).to.be.empty; - expect(scope.code2).to.be.empty; - expect(scope.code3).to.be.empty; - expect(scope.code4).to.be.empty; - expect(scope.code5).to.be.empty; + expect(scope.inputCode).to.be.empty; }); }); describe('verifyCode', function() { it('should fail for wrong code', function() { - scope.code0 = 'b'; - scope.code1 = 'b'; - scope.code2 = 'b'; - scope.code3 = 'b'; - scope.code4 = 'b'; - scope.code5 = 'b'; - scope.code = 'aaaaaa'; + scope.inputCode = 'bbbbbb'; + scope.code = 'AAAAAA'; - scope.onError = function() {}; expect(scope.verifyCode()).to.be.false; }); it('should work', function() { - scope.code0 = 'a'; - scope.code1 = 'a'; - scope.code2 = 'a'; - scope.code3 = 'a'; - scope.code4 = 'a'; - scope.code5 = 'a'; - scope.code = 'aaaaaa'; + scope.inputCode = 'aaAaaa'; + scope.code = 'AAAAAA'; - scope.onError = function() {}; - expect(scope.verifyCode()).to.be.false; + expect(scope.verifyCode()).to.be.true; }); }); diff --git a/test/unit/controller/login/login-privatekey-download-ctrl-test.js b/test/unit/controller/login/login-privatekey-download-ctrl-test.js index e47ad60..f556a8e 100644 --- a/test/unit/controller/login/login-privatekey-download-ctrl-test.js +++ b/test/unit/controller/login/login-privatekey-download-ctrl-test.js @@ -113,34 +113,9 @@ describe('Login Private Key Download Controller unit test', function() { }); }); - describe('handlePaste', function() { - it('should work', function() { - scope.handlePaste({ - clipboardData: { - getData: function(val) { - expect(val).to.equal('text/plain'); - return '1qaz-2wsx-3edc-4rfv-5tgb-6yhn'; - } - } - }); - - expect(scope.code0).to.equal('1qaz'); - expect(scope.code1).to.equal('2wsx'); - expect(scope.code2).to.equal('3edc'); - expect(scope.code3).to.equal('4rfv'); - expect(scope.code4).to.equal('5tgb'); - expect(scope.code5).to.equal('6yhn'); - }); - }); - describe('decryptAndStorePrivateKeyLocally', function() { beforeEach(function() { - scope.code0 = '0'; - scope.code1 = '1'; - scope.code2 = '2'; - scope.code3 = '3'; - scope.code4 = '4'; - scope.code5 = '5'; + scope.code = '012345'; scope.encryptedPrivateKey = { encryptedPrivateKey: 'encryptedPrivateKey'