bootstrap-pwstrength.js 25 KB

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