Completed
Pull Request — master (#50)
by Sander
01:31
created

js/background/inject/inject.js   C

Complexity

Total Complexity 58
Complexity/F 1.93

Size

Lines of Code 270
Function Count 30

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 7
Bugs 0 Features 0
Metric Value
cc 0
c 7
b 0
f 0
nc 192
dl 0
loc 270
rs 5.4871
wmc 58
mnd 3
bc 54
fnc 30
bpm 1.8
cpm 1.9333
noi 11

1 Function

Rating   Name   Duplication   Size   Complexity  
B $j.ready 0 268 1

How to fix   Complexity   

Complexity

Complex classes like js/background/inject/inject.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
/* global API */
2
var $j = jQuery.noConflict();
3
4
$j(document).ready(function () {
5
    var _this = this;
6
    Array.prototype.findUrl = function (match) {
0 ignored issues
show
Compatibility Best Practice introduced by
You are extending the built-in type Array. This may have unintended consequences on other objects using this built-in type. Consider subclassing instead.
Loading history...
7
        return this.filter(function (item) {
8
            return typeof item === 'string' && item.indexOf(match) > -1;
9
        });
10
    };
11
12
    function removePasswordPicker() {
13
        $j('.passwordPickerIframe').remove();
14
    }
15
16
    _this.removePasswordPicker = removePasswordPicker;
17
18
    function enterLoginDetails(login) {
19
        var username;
20
21
        if (login.hasOwnProperty('username')) {
22
            username = (login.username.trim() !== '' ) ? login.username : login.email;
23
        }
24
25
        fillPassword(username, login.password);
0 ignored issues
show
Bug introduced by
The variable username does not seem to be initialized in case login.hasOwnProperty("username") on line 21 is false. Are you sure the function fillPassword handles undefined variables?
Loading history...
26
        if ($j('.passwordPickerIframe').is(':visible')) {
27
            removePasswordPicker();
28
        }
29
    }
30
31
    _this.enterLoginDetails = enterLoginDetails;
32
33
34
    function showPasswordPicker(form) {
35
        if ($j('.passwordPickerIframe').length > 1) {
36
            return;
37
        }
38
        var loginField = $j(form[0]);
39
        var loginFieldPos = loginField.offset();
40
        var position = $j(form[1]).position();
0 ignored issues
show
Unused Code introduced by
The assignment to variable position seems to be never used. Consider removing it.
Loading history...
41
        var passwordField = $j(form[1]);
42
        var passwordFieldPos = passwordField.offset();
43
44
        var left = loginFieldPos.left;
45
        var top = loginFieldPos.top;
46
        var maxZ = Math.max.apply(null,
47
            $j.map($j('body *'), function (e) {
48
                if ($j(e).css('position') !== 'static')
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if $j(e).css("position") !== "static" is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
49
                    return parseInt($j(e).css('z-index')) || 1;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
50
            }));
51
        if (passwordFieldPos.top > loginFieldPos.top) {
52
            //console.log('login fields below each other')
53
            top = passwordFieldPos.top + passwordField.height() + 10;
54
55
        } else {
56
            // console.log('login fields next to each other')
57
            top = top + loginField.height() + 10;
58
        }
59
60
61
        var pickerUrl = API.extension.getURL('/html/inject/password_picker.html');
62
63
        var picker = $j('<iframe class="passwordPickerIframe" scrolling="no" height="400" width="350" frameborder="0" src="' + pickerUrl + '"></iframe>');
64
        picker.css('position', 'absolute');
65
        picker.css('left', left);
66
        picker.css('z-index', maxZ);
67
        picker.css('top', top);
68
        $j('body').append($j(picker));
69
        // picker.css('width', $j(form).width());
70
        $j('.passwordPickerIframe:not(:last)').remove();
71
    }
72
73
    function createFormIcon(el, form) {
74
        var offset = el.offset();
0 ignored issues
show
Unused Code introduced by
The assignment to variable offset seems to be never used. Consider removing it.
Loading history...
75
        var width = el.width();
76
        var height = el.height() * 1;
77
        var margin = (el.css('margin')) ? parseInt(el.css('margin').replace('px', '')) : 0;
0 ignored issues
show
Unused Code introduced by
The variable margin seems to be never used. Consider removing it.
Loading history...
78
        var padding = (el.css('padding')) ? parseInt(el.css('padding').replace('px', '')) : 0;
0 ignored issues
show
Unused Code introduced by
The variable padding seems to be never used. Consider removing it.
Loading history...
79
80
        var pickerIcon = API.extension.getURL('/icons/icon.svg');
81
        $j(el).css('background-image', 'url("' + pickerIcon + '")');
82
        $j(el).css('background-repeat', 'no-repeat');
83
        $j(el).css('background-position', 'right 3px center');
84
85
86
        function onClick(e) {
87
            e.preventDefault();
88
            var offsetX = e.offsetX;
89
            var offsetRight = (width - offsetX);
90
            if (offsetRight < height) {
91
                showPasswordPicker(form);
92
            }
93
        }
94
95
        // $j(el).bind('click', onClick);
96
        $j(el).click(onClick);
97
98
    }
99
100
    function createPasswordPicker(form) {
101
        for (var i = 0; i < form.length; i++) {
102
            var el = $j(form[i]);
103
            createFormIcon(el, form);
104
105
        }
106
    }
107
108
    function formSubmitted(fields) {
109
        var user = fields[0].value;
110
        var pass = fields[1].value;
111
        var params = {
112
            username: user,
113
            password: pass
114
        };
115
        //Disable password mining
116
        //$j(fields[1]).attr('type', 'hidden');
117
        API.runtime.sendMessage(API.runtime.id, {method: "minedForm", args: params});
118
119
    }
120
121
    function inIframe() {
122
        try {
123
            return window.self !== window.top;
124
        } catch (e) {
125
            return true;
126
        }
127
    }
128
129
    function showDoorhanger(data) {
130
        if (inIframe()) {
131
            return;
132
        }
133
        data.data.currentLocation = window.location.href;
134
        API.runtime.sendMessage(API.runtime.id, {method: "setDoorhangerData", args: data});
135
        var pickerUrl = API.extension.getURL('/html/inject/doorhanger.html');
136
137
        var doorhanger = $j('<iframe id="password-toolbarIframe" style="display: none;" scrolling="no" height="80" width="100%" frameborder="0" src="' + pickerUrl + '"></iframe>');
138
        $j('#password-toolbarIframe').remove();
139
        $j('body').after(doorhanger);
140
        $j('#password-toolbarIframe').slideDown();
141
    }
142
143
    _this.showDoorhanger = showDoorhanger;
144
145
    function showUrlUpdateDoorhanger(data) {
146
        var buttons = ['cancel', 'updateUrl'];
147
        showDoorhanger({
148
            data: data.data,
149
            buttons: buttons
150
        });
151
    }
152
153
    _this.showUrlUpdateDoorhanger = showUrlUpdateDoorhanger;
154
155
    function checkForMined() {
156
        if (inIframe()) {
157
            return;
158
        }
159
160
        API.runtime.sendMessage(API.runtime.id, {method: "getMinedData"}).then(function (data) {
161
            if (!data) {
162
                return;
163
            }
164
            if (data.hasOwnProperty('username') && data.hasOwnProperty('password') && data.hasOwnProperty('url')) {
165
                var buttons = [ 'cancel', 'save', 'ignore'];
166
                showDoorhanger({data: data, buttons: buttons});
167
            }
168
        });
169
    }
170
171
172
173
    function closeDoorhanger() {
174
        $j('#password-toolbarIframe').slideUp(400, function () {
175
            $j('#password-toolbarIframe').remove();
176
        });
177
    }
178
    _this.closeDoorhanger = closeDoorhanger;
179
180
    function initForms() {
181
        API.runtime.sendMessage(API.runtime.id, {method: 'getRuntimeSettings'}).then(function (result) {
182
            var disablePasswordPicker = result.disablePasswordPicker;
183
            var url = window.location.href;
184
            var loginFields = getLoginFields();
185
            if (loginFields.length > 0) {
186
                for (var i = 0; i < loginFields.length; i++) {
187
                    var form = getFormFromElement(loginFields[i][0]);
188
                    if (!disablePasswordPicker) {
189
                        createPasswordPicker(loginFields[i], form);
0 ignored issues
show
Bug introduced by
The call to createPasswordPicker seems to have too many arguments starting with form.
Loading history...
190
                    }
191
                    //Password miner
192
                    /* jshint ignore:start */
193
                    if (!result.hasOwnProperty('ignored_sites') || result.ignored_sites.findUrl(url) !== -1) {
194
                        $j(form).submit((function (loginFields) {
195
                            return function () {
196
                                formSubmitted(loginFields);
197
                            };
198
                        })(loginFields[i]));
199
                    }
200
                    /* jshint ignore:end */
201
                }
202
203
                API.runtime.sendMessage(API.runtime.id, {
204
                    method: "getCredentialsByUrl",
205
                    args: url
206
                }).then(function (logins) {
207
                    console.log('Found ' + logins.length + ' logins for this site');
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
208
                    if (logins.length === 1) {
209
                        API.runtime.sendMessage(API.runtime.id, {method: 'isAutoFillEnabled'}).then(function (isEnabled) {
210
                            if (isEnabled) {
211
                                enterLoginDetails(logins[0]);
212
                            }
213
                        });
214
                    }
215
216
                });
217
            }
218
219
        });
220
    }
221
222
    function minedLoginSaved(args) {
223
        // If the login added by the user then this is true
224
        if (args.selfAdded) {
225
            enterLoginDetails(args.credential);
226
        }
227
    }
228
    _this.minedLoginSaved = minedLoginSaved;
229
230
    function copyText(text) {
231
        var txtToCopy = document.createElement('input');
232
        txtToCopy.style.left = '-300px';
233
        txtToCopy.style.position = 'absolute';
234
        txtToCopy.value = text;
235
        document.body.appendChild(txtToCopy);
236
        txtToCopy.select();
237
        document.execCommand('copy');
238
        txtToCopy.parentNode.removeChild(txtToCopy);
239
    }
240
241
    _this.copyText = copyText;
242
243
    function init() {
244
        checkForMined();
245
        initForms();
246
    }
247
248
    var readyStateCheckInterval = setInterval(function () {
249
        if (document.readyState === "complete") {
250
            clearInterval(readyStateCheckInterval);
251
            API.runtime.sendMessage(API.runtime.id, {method: 'getMasterPasswordSet'}).then(function (result) {
252
                if (result) {
253
                    init();
254
                    // var body = document.getElementsByTagName('body')[0];
255
                    // observeDOM(body, function () {
256
                    //     initForms();
257
                    // });
258
                } else {
259
                    console.log('[Passman extension] Stopping, vault key not set');
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
260
                }
261
            });
262
        }
263
    }, 10);
264
265
    API.runtime.onMessage.addListener(function (msg, sender) {
266
        //console.log('Method call', msg.method);
267
        if (_this[msg.method]) {
268
            _this[msg.method](msg.args, sender);
269
        }
270
    });
271
});