Subversion Repositories Integrator Subversion

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 espaco 1
/*!
2
* jQuery Password Strength plugin for Twitter Bootstrap
3
*
4
* Copyright (c) 2008-2013 Tane Piper
5
* Copyright (c) 2013 Alejandro Blanco
6
* Dual licensed under the MIT and GPL licenses.
7
*/
8
 
9
(function (jQuery) {
10
// Source: src/rules.js
11
 
12
 
13
 
14
 
15
var rulesEngine = {};
16
 
17
try {
18
    if (!jQuery && module && module.exports) {
19
        var jQuery = require("jquery"),
20
            jsdom = require("jsdom").jsdom;
21
        jQuery = jQuery(jsdom().parentWindow);
22
    }
23
} catch (ignore) {}
24
 
25
(function ($, rulesEngine) {
26
    "use strict";
27
    var validation = {};
28
 
29
    rulesEngine.forbiddenSequences = [
30
        "0123456789", "abcdefghijklmnopqrstuvwxyz", "qwertyuiop", "asdfghjkl",
31
        "zxcvbnm", "!@#$%^&*()_+"
32
    ];
33
 
34
    validation.wordNotEmail = function (options, word, score) {
35
        if (word.match(/^([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,6})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)$/i)) {
36
            return score;
37
        }
38
        return 0;
39
    };
40
 
41
    validation.wordLength = function (options, word, score) {
42
        var wordlen = word.length,
43
            lenScore = Math.pow(wordlen, options.rules.raisePower);
44
        if (wordlen < options.common.minChar) {
45
            lenScore = (lenScore + score);
46
        }
47
        return lenScore;
48
    };
49
 
50
    validation.wordSimilarToUsername = function (options, word, score) {
51
        var username = $(options.common.usernameField).val();
52
        if (username && word.toLowerCase().match(username.toLowerCase())) {
53
            return score;
54
        }
55
        return 0;
56
    };
57
 
58
    validation.wordTwoCharacterClasses = function (options, word, score) {
59
        if (word.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/) ||
60
                (word.match(/([a-zA-Z])/) && word.match(/([0-9])/)) ||
61
                (word.match(/(.[!,@,#,$,%,\^,&,*,?,_,~])/) && word.match(/[a-zA-Z0-9_]/))) {
62
            return score;
63
        }
64
        return 0;
65
    };
66
 
67
    validation.wordRepetitions = function (options, word, score) {
68
        if (word.match(/(.)\1\1/)) { return score; }
69
        return 0;
70
    };
71
 
72
    validation.wordSequences = function (options, word, score) {
73
        var found = false,
74
            j;
75
        if (word.length > 2) {
76
            $.each(rulesEngine.forbiddenSequences, function (idx, seq) {
77
                var sequences = [seq, seq.split('').reverse().join('')];
78
                $.each(sequences, function (idx, sequence) {
79
                    for (j = 0; j < (word.length - 2); j += 1) { // iterate the word trough a sliding window of size 3:
80
                        if (sequence.indexOf(word.toLowerCase().substring(j, j + 3)) > -1) {
81
                            found = true;
82
                        }
83
                    }
84
                });
85
            });
86
            if (found) { return score; }
87
        }
88
        return 0;
89
    };
90
 
91
    validation.wordLowercase = function (options, word, score) {
92
        return word.match(/[a-z]/) && score;
93
    };
94
 
95
    validation.wordUppercase = function (options, word, score) {
96
        return word.match(/[A-Z]/) && score;
97
    };
98
 
99
    validation.wordOneNumber = function (options, word, score) {
100
        return word.match(/\d+/) && score;
101
    };
102
 
103
    validation.wordThreeNumbers = function (options, word, score) {
104
        return word.match(/(.*[0-9].*[0-9].*[0-9])/) && score;
105
    };
106
 
107
    validation.wordOneSpecialChar = function (options, word, score) {
108
        return word.match(/.[!,@,#,$,%,\^,&,*,?,_,~]/) && score;
109
    };
110
 
111
    validation.wordTwoSpecialChar = function (options, word, score) {
112
        return word.match(/(.*[!,@,#,$,%,\^,&,*,?,_,~].*[!,@,#,$,%,\^,&,*,?,_,~])/) && score;
113
    };
114
 
115
    validation.wordUpperLowerCombo = function (options, word, score) {
116
        return word.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/) && score;
117
    };
118
 
119
    validation.wordLetterNumberCombo = function (options, word, score) {
120
        return word.match(/([a-zA-Z])/) && word.match(/([0-9])/) && score;
121
    };
122
 
123
    validation.wordLetterNumberCharCombo = function (options, word, score) {
124
        return word.match(/([a-zA-Z0-9].*[!,@,#,$,%,\^,&,*,?,_,~])|([!,@,#,$,%,\^,&,*,?,_,~].*[a-zA-Z0-9])/) && score;
125
    };
126
 
127
    rulesEngine.validation = validation;
128
 
129
    rulesEngine.executeRules = function (options, word) {
130
        var totalScore = 0;
131
 
132
        $.each(options.rules.activated, function (rule, active) {
133
            if (active) {
134
                var score = options.rules.scores[rule],
135
                    funct = rulesEngine.validation[rule],
136
                    result,
137
                    errorMessage;
138
 
139
                if (!$.isFunction(funct)) {
140
                    funct = options.rules.extra[rule];
141
                }
142
 
143
                if ($.isFunction(funct)) {
144
                    result = funct(options, word, score);
145
                    if (result) {
146
                        totalScore += result;
147
                    }
148
                    if (result < 0 || (!$.isNumeric(result) && !result)) {
149
                        errorMessage = options.ui.spanError(options, rule);
150
                        if (errorMessage.length > 0) {
151
                            options.instances.errors.push(errorMessage);
152
                        }
153
                    }
154
                }
155
            }
156
        });
157
 
158
        return totalScore;
159
    };
160
}(jQuery, rulesEngine));
161
 
162
try {
163
    if (module && module.exports) {
164
        module.exports = rulesEngine;
165
    }
166
} catch (ignore) {}
167
 
168
// Source: src/options.js
169
 
170
 
171
 
172
 
173
var defaultOptions = {};
174
 
175
defaultOptions.common = {};
176
defaultOptions.common.minChar = 6;
177
defaultOptions.common.usernameField = "#username";
178
defaultOptions.common.userInputs = [
179
    // Selectors for input fields with user input
180
];
181
defaultOptions.common.onLoad = undefined;
182
defaultOptions.common.onKeyUp = undefined;
183
defaultOptions.common.zxcvbn = false;
184
defaultOptions.common.debug = false;
185
 
186
defaultOptions.rules = {};
187
defaultOptions.rules.extra = {};
188
defaultOptions.rules.scores = {
189
    wordNotEmail: -100,
190
    wordLength: -50,
191
    wordSimilarToUsername: -100,
192
    wordSequences: -50,
193
    wordTwoCharacterClasses: 2,
194
    wordRepetitions: -25,
195
    wordLowercase: 1,
196
    wordUppercase: 3,
197
    wordOneNumber: 3,
198
    wordThreeNumbers: 5,
199
    wordOneSpecialChar: 3,
200
    wordTwoSpecialChar: 5,
201
    wordUpperLowerCombo: 2,
202
    wordLetterNumberCombo: 2,
203
    wordLetterNumberCharCombo: 2
204
};
205
defaultOptions.rules.activated = {
206
    wordNotEmail: true,
207
    wordLength: true,
208
    wordSimilarToUsername: true,
209
    wordSequences: true,
210
    wordTwoCharacterClasses: false,
211
    wordRepetitions: false,
212
    wordLowercase: true,
213
    wordUppercase: true,
214
    wordOneNumber: true,
215
    wordThreeNumbers: true,
216
    wordOneSpecialChar: true,
217
    wordTwoSpecialChar: true,
218
    wordUpperLowerCombo: true,
219
    wordLetterNumberCombo: true,
220
    wordLetterNumberCharCombo: true
221
};
222
defaultOptions.rules.raisePower = 1.4;
223
 
224
defaultOptions.ui = {};
225
defaultOptions.ui.bootstrap2 = false;
226
defaultOptions.ui.showProgressBar = true;
227
defaultOptions.ui.showPopover = false;
228
defaultOptions.ui.showStatus = false;
229
defaultOptions.ui.spanError = function (options, key) {
230
    "use strict";
231
    var text = options.ui.errorMessages[key];
232
    if (!text) { return ''; }
233
    return '<span style="color: #d52929">' + text + '</span>';
234
};
235
defaultOptions.ui.errorMessages = {
236
    wordLength: "Your password is too short",
237
    wordNotEmail: "Do not use your email as your password",
238
    wordSimilarToUsername: "Your password cannot contain your username",
239
    wordTwoCharacterClasses: "Use different character classes",
240
    wordRepetitions: "Too many repetitions",
241
    wordSequences: "Your password contains sequences"
242
};
243
defaultOptions.ui.verdicts = ["Weak", "Normal", "Medium", "Strong", "Very Strong"];
244
defaultOptions.ui.showVerdicts = true;
245
defaultOptions.ui.showVerdictsInsideProgressBar = false;
246
defaultOptions.ui.showErrors = false;
247
defaultOptions.ui.container = undefined;
248
defaultOptions.ui.viewports = {
249
    progress: undefined,
250
    verdict: undefined,
251
    errors: undefined
252
};
253
defaultOptions.ui.scores = [14, 26, 38, 50];
254
 
255
// Source: src/ui.js
256
 
257
 
258
 
259
 
260
var ui = {};
261
 
262
(function ($, ui) {
263
    "use strict";
264
 
265
    var barClasses = ["danger", "warning", "success"],
266
        statusClasses = ["error", "warning", "success"];
267
 
268
    ui.getContainer = function (options, $el) {
269
        var $container;
270
 
271
        $container = $(options.ui.container);
272
        if (!($container && $container.length === 1)) {
273
            $container = $el.parent();
274
        }
275
        return $container;
276
    };
277
 
278
    ui.findElement = function ($container, viewport, cssSelector) {
279
        if (viewport) {
280
            return $container.find(viewport).find(cssSelector);
281
        }
282
        return $container.find(cssSelector);
283
    };
284
 
285
    ui.getUIElements = function (options, $el) {
286
        var $container, result;
287
 
288
        if (options.instances.viewports) {
289
            return options.instances.viewports;
290
        }
291
 
292
        $container = ui.getContainer(options, $el);
293
 
294
        result = {};
295
        result.$progressbar = ui.findElement($container, options.ui.viewports.progress, "div.progress");
296
        if (options.ui.showVerdictsInsideProgressBar) {
297
            result.$verdict = result.$progressbar.find("span.password-verdict");
298
        }
299
 
300
        if (!options.ui.showPopover) {
301
            if (!options.ui.showVerdictsInsideProgressBar) {
302
                result.$verdict = ui.findElement($container, options.ui.viewports.verdict, "span.password-verdict");
303
            }
304
            result.$errors = ui.findElement($container, options.ui.viewports.errors, "ul.error-list");
305
        }
306
 
307
        options.instances.viewports = result;
308
        return result;
309
    };
310
 
311
    ui.initProgressBar = function (options, $el) {
312
        var $container = ui.getContainer(options, $el),
313
            progressbar = "<div class='progress'><div class='";
314
 
315
        if (!options.ui.bootstrap2) {
316
            progressbar += "progress-";
317
        }
318
        progressbar += "bar'>";
319
        if (options.ui.showVerdictsInsideProgressBar) {
320
            progressbar += "<span class='password-verdict'></span>";
321
        }
322
        progressbar += "</div></div>";
323
 
324
        if (options.ui.viewports.progress) {
325
            $container.find(options.ui.viewports.progress).append(progressbar);
326
        } else {
327
            $(progressbar).insertAfter($el);
328
        }
329
    };
330
 
331
    ui.initHelper = function (options, $el, html, viewport) {
332
        var $container = ui.getContainer(options, $el);
333
        if (viewport) {
334
            $container.find(viewport).append(html);
335
        } else {
336
            $(html).insertAfter($el);
337
        }
338
    };
339
 
340
    ui.initVerdict = function (options, $el) {
341
        ui.initHelper(options, $el, "<span class='password-verdict'></span>",
342
                        options.ui.viewports.verdict);
343
    };
344
 
345
    ui.initErrorList = function (options, $el) {
346
        ui.initHelper(options, $el, "<ul class='error-list'></ul>",
347
                        options.ui.viewports.errors);
348
    };
349
 
350
    ui.initPopover = function (options, $el) {
351
        $el.popover("destroy");
352
        $el.popover({
353
            html: true,
354
            placement: "bottom",
355
            trigger: "manual",
356
            content: " "
357
        });
358
    };
359
 
360
    ui.initUI = function (options, $el) {
361
        if (options.ui.showPopover) {
362
            ui.initPopover(options, $el);
363
        } else {
364
            if (options.ui.showErrors) { ui.initErrorList(options, $el); }
365
            if (options.ui.showVerdicts && !options.ui.showVerdictsInsideProgressBar) {
366
                ui.initVerdict(options, $el);
367
            }
368
        }
369
        if (options.ui.showProgressBar) {
370
            ui.initProgressBar(options, $el);
371
        }
372
    };
373
 
374
    ui.possibleProgressBarClasses = ["danger", "warning", "success"];
375
 
376
    ui.updateProgressBar = function (options, $el, cssClass, percentage) {
377
        var $progressbar = ui.getUIElements(options, $el).$progressbar,
378
            $bar = $progressbar.find(".progress-bar"),
379
            cssPrefix = "progress-";
380
 
381
        if (options.ui.bootstrap2) {
382
            $bar = $progressbar.find(".bar");
383
            cssPrefix = "";
384
        }
385
 
386
        $.each(ui.possibleProgressBarClasses, function (idx, value) {
387
            $bar.removeClass(cssPrefix + "bar-" + value);
388
        });
389
        $bar.addClass(cssPrefix + "bar-" + barClasses[cssClass]);
390
        $bar.css("width", percentage + '%');
391
    };
392
 
393
    ui.updateVerdict = function (options, $el, text) {
394
        var $verdict = ui.getUIElements(options, $el).$verdict;
395
        $verdict.text(text);
396
    };
397
 
398
    ui.updateErrors = function (options, $el) {
399
        var $errors = ui.getUIElements(options, $el).$errors,
400
            html = "";
401
        $.each(options.instances.errors, function (idx, err) {
402
            html += "<li>" + err + "</li>";
403
        });
404
        $errors.html(html);
405
    };
406
 
407
    ui.updatePopover = function (options, $el, verdictText) {
408
        var popover = $el.data("bs.popover"),
409
            html = "",
410
            hide = true;
411
 
412
        if (options.ui.showVerdicts &&
413
                !options.ui.showVerdictsInsideProgressBar &&
414
                verdictText.length > 0) {
415
            html = "<h5><span class='password-verdict'>" + verdictText +
416
                "</span></h5>";
417
            hide = false;
418
        }
419
        if (options.ui.showErrors) {
420
            html += "<div>Errors:<ul class='error-list' style='margin-bottom: 0;'>";
421
            $.each(options.instances.errors, function (idx, err) {
422
                html += "<li>" + err + "</li>";
423
                hide = false;
424
            });
425
            html += "</ul></div>";
426
        }
427
 
428
        if (hide) {
429
            $el.popover("hide");
430
            return;
431
        }
432
 
433
        if (options.ui.bootstrap2) { popover = $el.data("popover"); }
434
 
435
        if (popover.$arrow && popover.$arrow.parents("body").length > 0) {
436
            $el.find("+ .popover .popover-content").html(html);
437
        } else {
438
            // It's hidden
439
            popover.options.content = html;
440
            $el.popover("show");
441
        }
442
    };
443
 
444
    ui.updateFieldStatus = function (options, $el, cssClass) {
445
        var targetClass = options.ui.bootstrap2 ? ".control-group" : ".form-group",
446
            $container = $el.parents(targetClass).first();
447
 
448
        $.each(statusClasses, function (idx, css) {
449
            if (!options.ui.bootstrap2) { css = "has-" + css; }
450
            $container.removeClass(css);
451
        });
452
 
453
        cssClass = statusClasses[cssClass];
454
        if (!options.ui.bootstrap2) { cssClass = "has-" + cssClass; }
455
        $container.addClass(cssClass);
456
    };
457
 
458
    ui.percentage = function (score, maximun) {
459
        var result = Math.floor(100 * score / maximun);
460
        result = result < 0 ? 0 : result;
461
        result = result > 100 ? 100 : result;
462
        return result;
463
    };
464
 
465
    ui.getVerdictAndCssClass = function (options, score) {
466
        var cssClass, verdictText, level;
467
 
468
        if (score <= 0) {
469
            cssClass = 0;
470
            level = -1;
471
            verdictText = options.ui.verdicts[0];
472
        } else if (score < options.ui.scores[0]) {
473
            cssClass = 0;
474
            level = 0;
475
            verdictText = options.ui.verdicts[0];
476
        } else if (score < options.ui.scores[1]) {
477
            cssClass = 0;
478
            level = 1;
479
            verdictText = options.ui.verdicts[1];
480
        } else if (score < options.ui.scores[2]) {
481
            cssClass = 1;
482
            level = 2;
483
            verdictText = options.ui.verdicts[2];
484
        } else if (score < options.ui.scores[3]) {
485
            cssClass = 1;
486
            level = 3;
487
            verdictText = options.ui.verdicts[3];
488
        } else {
489
            cssClass = 2;
490
            level = 4;
491
            verdictText = options.ui.verdicts[4];
492
        }
493
 
494
        return [verdictText, cssClass, level];
495
    };
496
 
497
    ui.updateUI = function (options, $el, score) {
498
        var cssClass, barPercentage, verdictText;
499
 
500
        cssClass = ui.getVerdictAndCssClass(options, score);
501
        verdictText = cssClass[0];
502
        cssClass = cssClass[1];
503
 
504
        if (options.ui.showProgressBar) {
505
            barPercentage = ui.percentage(score, options.ui.scores[3]);
506
            ui.updateProgressBar(options, $el, cssClass, barPercentage);
507
            if (options.ui.showVerdictsInsideProgressBar) {
508
                ui.updateVerdict(options, $el, verdictText);
509
            }
510
        }
511
 
512
        if (options.ui.showStatus) {
513
            ui.updateFieldStatus(options, $el, cssClass);
514
        }
515
 
516
        if (options.ui.showPopover) {
517
            ui.updatePopover(options, $el, verdictText);
518
        } else {
519
            if (options.ui.showVerdicts && !options.ui.showVerdictsInsideProgressBar) {
520
                ui.updateVerdict(options, $el, verdictText);
521
            }
522
            if (options.ui.showErrors) {
523
                ui.updateErrors(options, $el);
524
            }
525
        }
526
    };
527
}(jQuery, ui));
528
 
529
// Source: src/methods.js
530
 
531
 
532
 
533
 
534
var methods = {};
535
 
536
(function ($, methods) {
537
    "use strict";
538
    var onKeyUp, applyToAll;
539
 
540
    onKeyUp = function (event) {
541
        var $el = $(event.target),
542
            options = $el.data("pwstrength-bootstrap"),
543
            word = $el.val(),
544
            userInputs,
545
            verdictText,
546
            verdictLevel,
547
            score;
548
 
549
        if (options === undefined) { return; }
550
 
551
        options.instances.errors = [];
552
        if (options.common.zxcvbn) {
553
            userInputs = [];
554
            $.each(options.common.userInputs, function (idx, selector) {
555
                userInputs.push($(selector).val());
556
            });
557
            userInputs.push($(options.common.usernameField).val());
558
            score = zxcvbn(word, userInputs).entropy;
559
        } else {
560
            score = rulesEngine.executeRules(options, word);
561
        }
562
        ui.updateUI(options, $el, score);
563
        verdictText = ui.getVerdictAndCssClass(options, score);
564
        verdictLevel = verdictText[2];
565
        verdictText = verdictText[0];
566
 
567
        if (options.common.debug) { console.log(score + ' - ' + verdictText); }
568
 
569
        if ($.isFunction(options.common.onKeyUp)) {
570
            options.common.onKeyUp(event, {
571
                score: score,
572
                verdictText: verdictText,
573
                verdictLevel: verdictLevel
574
            });
575
        }
576
    };
577
 
578
    methods.init = function (settings) {
579
        this.each(function (idx, el) {
580
            // Make it deep extend (first param) so it extends too the
581
            // rules and other inside objects
582
            var clonedDefaults = $.extend(true, {}, defaultOptions),
583
                localOptions = $.extend(true, clonedDefaults, settings),
584
                $el = $(el);
585
 
586
            localOptions.instances = {};
587
            $el.data("pwstrength-bootstrap", localOptions);
588
            $el.on("keyup", onKeyUp);
589
            $el.on("change", onKeyUp);
590
            $el.on("onpaste", onKeyUp);
591
 
592
            ui.initUI(localOptions, $el);
593
            if ($.trim($el.val())) { // Not empty, calculate the strength
594
                $el.trigger("keyup");
595
            }
596
 
597
            if ($.isFunction(localOptions.common.onLoad)) {
598
                localOptions.common.onLoad();
599
            }
600
        });
601
 
602
        return this;
603
    };
604
 
605
    methods.destroy = function () {
606
        this.each(function (idx, el) {
607
            var $el = $(el),
608
                options = $el.data("pwstrength-bootstrap"),
609
                elements = ui.getUIElements(options, $el);
610
            elements.$progressbar.remove();
611
            elements.$verdict.remove();
612
            elements.$errors.remove();
613
            $el.removeData("pwstrength-bootstrap");
614
        });
615
    };
616
 
617
    methods.forceUpdate = function () {
618
        this.each(function (idx, el) {
619
            var event = { target: el };
620
            onKeyUp(event);
621
        });
622
    };
623
 
624
    methods.addRule = function (name, method, score, active) {
625
        this.each(function (idx, el) {
626
            var options = $(el).data("pwstrength-bootstrap");
627
 
628
            options.rules.activated[name] = active;
629
            options.rules.scores[name] = score;
630
            options.rules.extra[name] = method;
631
        });
632
    };
633
 
634
    applyToAll = function (rule, prop, value) {
635
        this.each(function (idx, el) {
636
            $(el).data("pwstrength-bootstrap").rules[prop][rule] = value;
637
        });
638
    };
639
 
640
    methods.changeScore = function (rule, score) {
641
        applyToAll.call(this, rule, "scores", score);
642
    };
643
 
644
    methods.ruleActive = function (rule, active) {
645
        applyToAll.call(this, rule, "activated", active);
646
    };
647
 
648
    $.fn.pwstrength = function (method) {
649
        var result;
650
 
651
        if (methods[method]) {
652
            result = methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
653
        } else if (typeof method === "object" || !method) {
654
            result = methods.init.apply(this, arguments);
655
        } else {
656
            $.error("Method " +  method + " does not exist on jQuery.pwstrength-bootstrap");
657
        }
658
 
659
        return result;
660
    };
661
}(jQuery, methods));
662
}(jQuery));