js/ui/password_picker/password_picker.js   F
last analyzed

Complexity

Total Complexity 82
Complexity/F 1.55

Size

Lines of Code 404
Function Count 53

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
cc 0
c 3
b 1
f 0
nc 256
dl 0
loc 404
rs 3.12
wmc 82
mnd 2
bc 81
fnc 53
bpm 1.5283
cpm 1.5471
noi 3

How to fix   Complexity   

Complexity

Complex classes like js/ui/password_picker/password_picker.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
$(document).ready(function () {
2
    var _this = this;
3
    var storage = new API.Storage();
4
    var runtimeSettings = {};
5
6
    API.runtime.sendMessage(API.runtime.id, {'method': 'getRuntimeSettings'}).then(function (settings) {
7
        var accounts = settings.accounts;
8
        runtimeSettings = settings;
9
        for(var i = 0; i < accounts.length; i++) {
10
            $('#savepw-vault').append('<option value=' + i + '>' + accounts[i].vault.name + '</option>');
11
        }
12
        storage.get('activeTab').then(function (name) {
13
            if (name && name !== '') {
14
                // makeTabActive(name);
15
                API.runtime.sendMessage(API.runtime.id, {method: "getActiveTab", args: {returnFn: "returnActiveTab"}});
16
            }
17
        });
18
19
    });
20
21
    $('[t]').each(function () {
22
        var string = $(this).attr('t');
23
        var startChar = string[0];
24
        var endChar = string[string.length - 1];
25
        var attribute;
26
        if (startChar === '[' && endChar === ']') {
27
            var data = string.replace('[', '').replace(']', '').split(',');
28
            attribute = data[1].trim();
29
            string = data[0].trim();
30
        }
31
        var translated = API.i18n.getMessage(string);
32
        if (attribute) {
33
            $(this).attr(attribute, translated);
34
        } else {
35
            $(this).text(translated);
36
        }
37
    });
38
39
    function fillLogin(login) {
40
        API.runtime.sendMessage(API.runtime.id, {
41
            method: 'passToParent',
42
            args: {
43
                injectMethod: 'enterLoginDetails',
44
                args: login
45
            }
46
        }).then(function () {
47
            removePasswordPicker();
48
        });
49
    }
50
51
    function removePasswordPicker(login) {
0 ignored issues
show
Unused Code introduced by
The parameter login is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
52
        API.runtime.sendMessage(API.runtime.id, {
53
            method: 'passToParent',
54
            args: {
55
                injectMethod: 'removePasswordPicker'
56
            }
57
        });
58
    }
59
60
    function copyTextToClipboard(text) {
61
        var copyFrom = document.createElement("textarea");
62
        copyFrom.textContent = text;
63
        var body = document.getElementsByTagName('body')[0];
64
        body.appendChild(copyFrom);
65
        copyFrom.select();
66
        document.execCommand('copy');
67
        body.removeChild(copyFrom);
68
    }
69
70
    _this.copyTextToClipboard = copyTextToClipboard;
71
72
73
    function setupAddCredentialFields() {
74
        var labelfield = $('#savepw-label');
75
        labelfield.val(document.title);
76
        var userfield = $('#savepw-username');
77
        var pwfield = $('#savepw-password');
78
        var vaultfield = $('#savepw-vault');
79
        $('.togglePw').click(function () {
80
            $('.togglePw').find('.fa').toggleClass('fa-eye').toggleClass('fa-eye-slash');
81
            if (pwfield.attr('type') === 'password') {
82
                pwfield.attr('type', 'text');
83
            } else {
84
                pwfield.attr('type', 'password');
85
            }
86
        });
87
88
        $('#savepw-save').click(function (e) {
89
            var fields = [labelfield, pwfield];
90
            var hasErrors = false;
91
            $.each(fields, function (k, field) {
92
               field.removeClass('error');
93
                if(!$(field).val()){
94
                   field.addClass('error');
95
                   hasErrors = true;
96
                }
97
            });
98
            e.preventDefault();
99
            if(hasErrors){
100
                return;
101
            }
102
            $(this).text(API.i18n.getMessage("saving"));
103
            $(this).attr('disabled', true);
104
            API.runtime.sendMessage(API.runtime.id, {
105
                method: "injectCreateCredential",
106
                args: {
107
                    label: labelfield.val(),
108
                    username: userfield.val(),
109
                    password: pwfield.val(),
110
                    vaultIndex: vaultfield.val()
111
                }
112
            }).then(removePasswordPicker);
113
        });
114
115
        $('#savepw-cancel').click(function () {
116
            labelfield.val(document.title);
117
            userfield.val('');
118
            pwfield.val('');
119
            removePasswordPicker();
120
        });
121
122
    }
123
124
    function toggleFieldType(field) {
125
        if ($(field).attr('type').toLowerCase() === 'text') {
126
            $(field).attr('type', 'password');
127
        } else {
128
            $(field).attr('type', 'text');
129
        }
130
    }
131
132
    function genPwd(settings) {
133
        /* jshint ignore:start */
134
        var password = generatePassword(settings['length'],
135
            settings.useUppercase,
136
            settings.useLowercase,
137
            settings.useDigits,
138
            settings.useSpecialChars,
139
            settings.minimumDigitCount,
140
            settings.avoidAmbiguousCharacters,
141
            settings.requireEveryCharType);
142
        /* jshint ignore:end */
143
        return password;
144
    }
145
146
    function getPasswordGenerationSettings(cb) {
147
        var default_settings = {
148
            'length': 12,
149
            'useUppercase': true,
150
            'useLowercase': true,
151
            'useDigits': true,
152
            'useSpecialChars': true,
153
            'minimumDigitCount': 3,
154
            'avoidAmbiguousCharacters': false,
155
            'requireEveryCharType': true
156
        };
157
        storage.get('password_generator_settings').then(function (_settings) {
158
            if (!_settings) {
159
                _settings = default_settings;
160
            }
161
162
            cb(_settings);
163
        }).error(function () {
164
            cb(default_settings);
165
        });
166
    }
167
168
    function setupPasswordGenerator() {
169
        //getPasswordGeneratorSettings
170
        getPasswordGenerationSettings(function (settings) {
171
            var round = 0;
172
173
            function generate_pass(inputId) {
174
                var new_password = genPwd(settings);
175
                $('#' + inputId).val(new_password);
176
                setTimeout(function () {
177
                    if (round < 10) {
178
                        generate_pass(inputId);
179
                        round++;
180
                    } else {
181
                        round = 0;
182
                    }
183
                }, 10);
184
            }
185
186
            $.each(settings, function (setting, val) {
187
                if (typeof(val) === "boolean") {
188
                    $('[name="' + setting + '"]').prop('checked', val);
189
                } else {
190
                    $('[name="' + setting + '"]').val(val);
191
                }
192
            });
193
194
            $('form[name="advancedSettings"]').change(function () {
195
                var pw_settings_form = $(this);
196
                settings = pw_settings_form.serializeObject();
197
                storage.set('password_generator_settings', settings);
198
            });
199
200
            $('.renewpw').click(function () {
201
                generate_pass('generated_password');
202
            });
203
            $('.renewpw_newac').click(function () {
204
                generate_pass('savepw-password');
205
206
            });
207
            $('.renewpw').click();
208
            $('.renewpw_newac').click();
209
210
            $('.usepwd').click(function () {
211
                $('#savepw-password').val($('#generated_password').val());
212
                $('.tab.add').click();
213
            });
214
215
            $('.togglePwVis').click(function () {
216
                toggleFieldType('#generated_password');
217
                $(this).find('.fa').toggleClass('fa-eye-slash').toggleClass('fa-eye');
218
            });
219
220
            $('.adv_opt').click(function () {
221
222
                var adv_settings = $('.pw-setting-advanced');
223
                $(this).find('i').toggleClass('fa-angle-right').toggleClass('fa-angle-down');
224
                if (adv_settings.is(':visible')) {
225
                    adv_settings.slideUp();
226
                } else {
227
                    adv_settings.slideDown();
228
                }
229
            });
230
        });
231
    }
232
233
    var picker = $('#password_picker');
234
    var makeTabActive = function (name) {
235
        picker.find('.tab').removeClass('active');
236
        picker.find('.tab-content').children().hide();
237
        picker.find('.tab-' + name + '-content').show();
238
        picker.find('.tab.' + name).addClass('active');
239
    };
240
241
    picker.find('.tab').click(function () {
242
        var name = $(this).attr('data-name');
243
        storage.set('activeTab', name).then(function (r) {
0 ignored issues
show
Unused Code introduced by
The parameter r is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
244
            makeTabActive(name);
245
            if(name === 'search'){
246
                $('#password_search').focus();
247
            }
248
        });
249
    });
250
    
251
252
253
    $('.tab.close').click(function () {
254
        removePasswordPicker();
255
    });
256
257
258
    function disablePassman(where, url){
259
        var whereFn = (where === 'site') ? 'Site' : 'URL';
260
        API.runtime.sendMessage(API.runtime.id, {
261
            method: "ignore"+ whereFn,
262
            args: url
263
        }).then(function () {
264
            var text = (where === 'site') ? 'site_ignored' : 'url_ignored';
265
            $('.tab-ignore-content').find('.text').text(API.i18n.getMessage(text));
266
            setTimeout(function () {
267
                removePasswordPicker();
268
            }, 2500);
269
        });
270
    }
271
272
273
274
    function returnActiveTab(tab) {
275
276
        $('.disable-site').on('click', function () {
277
            disablePassman('site', tab.url);
278
        });
279
280
        $('.disable-page').on('click', function () {
281
            disablePassman('url', tab.url);
282
        });
283
284
        API.runtime.sendMessage(API.runtime.id, {
285
            method: "getCredentialsByUrl",
286
            args: [tab.url]
287
        }).then(function (logins) {
288
            if (logins.length === 0) {
289
                API.runtime.sendMessage(API.runtime.id, {
290
                    'method': 'getSetting',
291
                    args: 'no_results_found_tab'
292
                }).then(function (value) {
293
                    makeTabActive(value);
294
                });
295
                return;
296
            }
297
            if (logins.length !== 0) {
298
                picker.find('.tab-list-content').html('');
299
                if(runtimeSettings.passwordPickerGotoList){
300
                    makeTabActive('list');
301
                }
302
            }
303
            for (var i = 0; i < logins.length; i++) {
304
                var login = logins[i];
305
                var div = $('<div>', {class: 'account', text: login.label});
306
                $('<br>').appendTo(div);
307
                var username = (login.username !== '' ) ? login.username : login.email;
308
                $('<small>').text(username).appendTo(div);
309
                /* jshint ignore:start */
310
                div.click((function (login) {
311
                    return function () {
312
                        //enterLoginDetails(login);
313
                        //API.runtime.sendMessage(API.runtime.id, {method: 'getMasterPasswordSet'})
314
                        fillLogin(login)
315
                    };
316
                })(login));
317
                /* jshint ignore:end*/
318
319
                picker.find('.tab-list-content').append(div);
320
            }
321
        });
322
    }
323
324
    _this.returnActiveTab = returnActiveTab;
325
326
327
    $('.no-credentials .save').on('click', function () {
328
        $('.tab.add').click();
329
    });
330
    $('.no-credentials .search').on('click', function () {
331
        $('.tab.search').click();
332
    });
333
    $('.no-credentials .gen').on('click', function () {
334
        $('.tab.generate').click();
335
    });
336
    setupAddCredentialFields();
337
    setupPasswordGenerator();
338
339
340
    API.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
0 ignored issues
show
Unused Code introduced by
The parameter sendResponse is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
341
        if (_this[msg.method]) {
342
            _this[msg.method](msg.args, sender);
343
        }
344
    });
345
346
347
    $('#password_search').keyup(function () {
348
        searchCredentials();
349
    });
350
351
    function url_domain(data) {
352
        if(!data){
353
            return '';
354
        }
355
        var matches = data.match(/^https?\:\/\/([^\/?#]+)(?:[\/?#]|$)/i);
356
        return matches && matches[1];  // domain will be null if no match is found
357
    }
358
359
360
    function searchCredentials() {
361
362
        var searchText = $('#password_search').val();
363
        if (searchText === '') {
364
            return;
365
        }
366
        API.runtime.sendMessage(API.runtime.id, {
367
            'method': 'searchCredential',
368
            args: searchText
369
        }).then(function (result) {
370
            if (result.length === 0 || !result) {
371
                $('#searchResults').html(API.i18n.getMessage('no_credentials_found'));
372
            }
373
            var html = '';
374
            for (var i = 0; i < result.length; i++) {
375
                var login = result[i];
376
                var div = $('<div>', {class: 'account', text: login.label});
377
                $('<br>').appendTo(div);
378
379
                var username = (login.username !== '' ) ? login.username : login.email;
380
                $('<small>').text(username).appendTo(div);
381
                $('<br>').appendTo(div);
382
                $('<small>').text(url_domain(login.url)).appendTo(div);
383
                /* jshint ignore:start */
384
                div.click((function (login) {
385
                    return function () {
386
                        //enterLoginDetails(login);
387
                        //API.runtime.sendMessage(API.runtime.id, {method: 'getMasterPasswordSet'})
388
                        fillLogin(login);
389
                        //@TODO Ask to update the url of the login
390
                        API.runtime.sendMessage(API.runtime.id, {
391
                            'method': 'updateCredentialUrlDoorhanger',
392
                            args: login
393
                        })
394
                    };
395
                })(login));
396
                /* jshint ignore:end*/
397
                html += $('<div />').html(div).html();
398
399
            }
400
            picker.find('#searchResults').html(html);
401
        });
402
    }
403
404
});
405