Completed
Pull Request — master (#137)
by Sander
43s
created

$j.ready   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 6
rs 9.4285
1
/* global API */
2
var $j = jQuery.noConflict();
3
4
$j(document).ready(function () {
5
6
    $j(document).click(function (event) {
7
        var passwordPickerRef = '.passwordPickerIframe';
8
        if (!$j(event.target).closest(passwordPickerRef).length) {
9
            if ($j(passwordPickerRef).is(":visible")) {
10
                removePasswordPicker();
11
            }
12
        }
13
    });
14
15
    var _this = this;
16
    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...
17
        return this.filter(function (item) {
18
            return typeof item === 'string' && item.indexOf(match) > -1;
19
        });
20
    };
21
22
    function removePasswordPicker() {
23
        activeForm = undefined;
24
        $j('.passwordPickerIframe').remove();
25
    }
26
27
    _this.removePasswordPicker = removePasswordPicker;
28
29
    function enterLoginDetails(login) {
30
        var username;
31
32
        if (login.hasOwnProperty('username')) {
33
            username = (login.username !== '' ) ? login.username : login.email;
34
        }
35
        if (!username) {
36
            username = null;
37
        }
38
39
        fillPassword(username, login.password);
40
41
        if (activeForm) {
42
            API.runtime.sendMessage(API.runtime.id, {method: 'isAutoSubmitEnabled'}).then(function (isEnabled) {
43
                if (isEnabled) {
44
                    submitLoginForm(username);
45
                }
46
            });
47
        }
48
    }
49
50
    _this.enterLoginDetails = enterLoginDetails;
51
52
    function submitLoginForm(username) {
53
        if (!activeForm) {
54
            // @TODO detect login form on the current page
55
            return;
56
        }
57
58
        var formEl = $j(activeForm).closest('form');
59
        var iframeUrl = API.extension.getURL('/html/inject/auto_login.html');
60
        $j('#loginPopupIframe').remove();
61
        var loginPopup = $j('<iframe class="loginPopupIframe" scrolling="no" frameborder="0" src="' + iframeUrl + '"></iframe>');
62
        var padding = parseInt($j(formEl).css('padding').replace('px', ''));
63
        var margin = parseInt($j(formEl).css('margin').replace('px', ''));
64
        var height = Math.round($j(formEl).height() + (padding * 2) + (margin * 2));
65
        var width = Math.round($j(formEl).width() + (padding * 2) + (margin * 2));
66
        loginPopup.attr('height', height);
67
        loginPopup.attr('width', width);
68
        loginPopup.css('position', 'absolute');
69
        loginPopup.css('z-index', getMaxZ() + 1);
70
        loginPopup.css('background-color', 'rgba(0, 0, 0, 0.73)');
71
        loginPopup.css('left', Math.floor($j(formEl).offset().left - padding - margin));
72
        loginPopup.css('top', Math.floor($j(formEl).offset().top - padding - margin));
73
        removePasswordPicker();
74
        $j(document.body).prepend(loginPopup);
75
        API.runtime.sendMessage(API.runtime.id, {'setIframeUsername': username}).then(function () {
76
            $j(formEl).submit();
77
            setTimeout(function () {
78
                loginPopup.remove();
79
            }, 2000);
80
        });
81
    }
82
83
    function getMaxZ() {
84
        return Math.max.apply(null,
85
            $j.map($j('body *'), function (e) {
86
                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...
87
                    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...
88
            }));
89
    }
90
91
    var activeForm;
92
93
    function showPasswordPicker(form) {
94
        var jPasswordPicker = $j('.passwordPickerIframe');
95
        if (jPasswordPicker.length > 1) {
96
            return;
97
        }
98
        var loginField = $j(form[0]);
99
        var loginFieldPos = loginField.offset();
100
        var loginFieldVisible = loginField.is(':visible');
101
102
        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...
103
        var passwordField = $j(form[1]);
104
        var passwordFieldPos = passwordField.offset();
105
        var passwordFieldVisible = loginField.is(':visible');
0 ignored issues
show
Unused Code introduced by
The variable passwordFieldVisible seems to be never used. Consider removing it.
Loading history...
106
        var left = (loginFieldPos) ? loginFieldPos.left : passwordFieldPos.left;
107
        var top = (loginFieldPos) ? loginFieldPos.top : passwordFieldPos.top;
108
        var maxZ = getMaxZ();
109
110
        if (loginFieldPos && passwordFieldPos.top > loginFieldPos.top) {
111
            //console.log('login fields below each other')
112
            top = passwordFieldPos.top + passwordField.height() + 10;
113
        } else {
114
            // console.log('login fields next to each other')
115
            if (loginFieldPos) {
116
                top = top + loginField.height() + 10;
117
            } else {
118
                top = top + passwordField.height() + 10;
119
            }
120
        }
121
        if (!loginFieldVisible) {
122
            left = passwordFieldPos.left;
123
        }
124
125
        var pickerUrl = API.extension.getURL('/html/inject/password_picker.html');
126
127
        var picker = $j('<iframe class="passwordPickerIframe" scrolling="no" height="385" width="350" frameborder="0" src="' + pickerUrl + '"></iframe>');
128
        picker.css('position', 'absolute');
129
        picker.css('left', left);
130
        picker.css('z-index', maxZ + 10);
131
        picker.css('top', top);
132
        $j('body').prepend($j(picker));
133
        activeForm = form;
134
        // picker.css('width', $j(form).width());
135
        $j('.passwordPickerIframe:not(:last)').remove();
136
    }
137
138
    function onFormIconClick(e) {
139
        e.preventDefault();
140
        e.stopPropagation();
141
        var offsetX = e.offsetX;
142
        var offsetRight = (e.data.width - offsetX);
143
        if (offsetRight < e.data.height) {
144
            showPasswordPicker(e.data.form);
145
        }
146
    }
147
148
    function createFormIcon(el, form) {
149
        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...
150
        var width = el.width();
151
        var height = el.height() * 1;
152
        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...
153
        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...
154
155
        var pickerIcon = API.extension.getURL('/icons/icon.svg');
156
        $j(el).css('background-image', 'url("' + pickerIcon + '")');
157
        $j(el).css('background-repeat', 'no-repeat');
158
        //$j(el).css('background-position', '');
159
        $j(el).css('cssText', el.attr('style') + ' background-position: right 3px center !important;');
160
161
        $j(el).unbind('click', onFormIconClick);
162
        $j(el).click({width: width, height: height, form: form}, onFormIconClick);
163
    }
164
165
    function createPasswordPicker(form) {
166
        for (var i = 0; i < form.length; i++) {
167
            var el = $j(form[i]);
168
            createFormIcon(el, form);
169
170
        }
171
    }
172
173
    function formSubmitted(fields) {
174
        var user = fields[0].value;
175
        var pass = fields[1].value;
176
        var params = {
177
            username: user,
178
            password: pass
179
        };
180
        //Disable password mining
181
        //$j(fields[1]).attr('type', 'hidden');
182
        API.runtime.sendMessage(API.runtime.id, {method: "minedForm", args: params});
183
184
    }
185
186
    function inIframe() {
187
        try {
188
            return window.self !== window.top;
189
        } catch (e) {
190
            return true;
191
        }
192
    }
193
194
    function showDoorhanger(data) {
195
        if (inIframe()) {
196
            return;
197
        }
198
        data.data.currentLocation = window.location.href;
199
        API.runtime.sendMessage(API.runtime.id, {method: "setDoorhangerData", args: data});
200
        var pickerUrl = API.extension.getURL('/html/inject/doorhanger.html');
201
202
        var doorhanger = $j('<iframe id="password-toolbarIframe" style="display: none;" scrolling="no" height="60" width="100%" frameborder="0" src="' + pickerUrl + '"></iframe>');
203
        $j('#password-toolbarIframe').remove();
204
        doorhanger.css('z-index', getMaxZ() + 1);
205
        $j('body').prepend(doorhanger);
206
        $j('#password-toolbarIframe').fadeIn();
207
    }
208
209
    _this.showDoorhanger = showDoorhanger;
210
211
    function showUrlUpdateDoorhanger(data) {
212
        var buttons = ['cancel', 'updateUrl'];
213
        showDoorhanger({
214
            data: data.data,
215
            buttons: buttons
216
        });
217
    }
218
219
    _this.showUrlUpdateDoorhanger = showUrlUpdateDoorhanger;
220
221
    function checkForMined() {
222
        if (inIframe()) {
223
            return;
224
        }
225
226
        API.runtime.sendMessage(API.runtime.id, {method: "getMinedData"}).then(function (data) {
227
            if (!data) {
228
                return;
229
            }
230
            if (data.hasOwnProperty('username') && data.hasOwnProperty('password') && data.hasOwnProperty('url')) {
231
                var buttons = ['cancel', 'ignore', 'save'];
232
                showDoorhanger({data: data, buttons: buttons});
233
            }
234
        });
235
    }
236
237
238
    function closeDoorhanger() {
239
        $j('#password-toolbarIframe').hide(400);
240
        $j('#password-toolbarIframe').remove();
241
    }
242
243
    _this.closeDoorhanger = closeDoorhanger;
244
245
    function initForms() {
246
        API.runtime.sendMessage(API.runtime.id, {method: 'getRuntimeSettings'}).then(function (settings) {
247
            var enablePasswordPicker = settings.enablePasswordPicker;
248
            var url = window.location.href;
249
            var loginFields = getLoginFields();
250
            if (loginFields.length > 0) {
251
                for (var i = 0; i < loginFields.length; i++) {
252
                    var form = getFormFromElement(loginFields[i][0]);
253
                    if (enablePasswordPicker) {
254
                        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...
255
                    }
256
                    //Password miner
257
                    /* jshint ignore:start */
258
                    if (!settings.hasOwnProperty('ignored_sites') || settings.ignored_sites.findUrl(url) !== -1) {
259
                        $j(form).submit((function (loginFields) {
260
                            return function () {
261
                                formSubmitted(loginFields);
262
                            };
263
                        })(loginFields[i]));
264
                    }
265
                    /* jshint ignore:end */
266
                }
267
268
                API.runtime.sendMessage(API.runtime.id, {
269
                    method: "getCredentialsByUrl",
270
                    args: url
271
                }).then(function (logins) {
272
                    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...
273
                    if (logins.length === 1) {
274
                        API.runtime.sendMessage(API.runtime.id, {method: 'isAutoFillEnabled'}).then(function (isEnabled) {
275
                            if (isEnabled) {
276
                                enterLoginDetails(logins[0]);
277
                            }
278
                        });
279
                    }
280
281
                });
282
            }
283
284
        });
285
    }
286
287
    function minedLoginSaved(args) {
288
        // If the login added by the user then this is true
289
        if (args.selfAdded) {
290
            enterLoginDetails(args.credential);
291
        }
292
    }
293
294
    _this.minedLoginSaved = minedLoginSaved;
295
296
    function resizeIframe(height) {
297
        $j('#password-toolbarIframe').height(60 + height);
298
    }
299
300
    _this.resizeIframe = resizeIframe;
301
302
    function copyText(text) {
303
        var txtToCopy = document.createElement('input');
304
        txtToCopy.style.left = '-300px';
305
        txtToCopy.style.position = 'absolute';
306
        txtToCopy.value = text;
307
        document.body.appendChild(txtToCopy);
308
        txtToCopy.select();
309
        document.execCommand('copy');
310
        txtToCopy.parentNode.removeChild(txtToCopy);
311
    }
312
313
    _this.copyText = copyText;
314
315
    function init() {
316
        checkForMined();
317
        initForms();
318
    }
319
320
    var readyStateCheckInterval = setInterval(function () {
321
        if (document.readyState === "complete") {
322
            clearInterval(readyStateCheckInterval);
323
            API.runtime.sendMessage(API.runtime.id, {method: 'getMasterPasswordSet'}).then(function (result) {
324
                if (result) {
325
                    init();
326
                    var body = document.getElementsByTagName('body')[0];
327
                    observeDOM(body, initForms);
328
                } else {
329
                    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...
330
                }
331
            });
332
        }
333
    }, 10);
334
335
    API.runtime.onMessage.addListener(function (msg, sender) {
336
        //console.log('Method call', msg.method);
337
        if (_this[msg.method]) {
338
            _this[msg.method](msg.args, sender);
339
        }
340
    });
341
});
342