├── .babelrc ├── .bowerrc ├── .csscomb.json ├── .editorconfig ├── .eslintrc.json ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .npmignore ├── .release.json ├── .stylelintrc.json ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── bower.json ├── config.js ├── dist ├── css │ ├── strength.css │ └── strength.min.css ├── jquery-strength.es.js ├── jquery-strength.js ├── jquery-strength.min.js └── jquery-strength.min.js.map ├── examples ├── css │ ├── main.css │ └── normalize.css ├── images │ └── checkbox.png ├── index.html ├── js │ ├── jquery.js │ └── password_strength.js └── password_strength.js ├── gulp ├── tasks │ ├── archive.js │ ├── assets.js │ ├── browser.js │ ├── clean.js │ ├── deploy.js │ ├── lint-scripts.js │ ├── lint-styles.js │ ├── release.js │ ├── scripts.js │ ├── styles.js │ └── test.js └── util │ ├── getFolders.js │ ├── getSrcFiles.js │ └── handleErrors.js ├── gulpfile.babel.js ├── karma.conf.js ├── manifest.json ├── package-lock.json ├── package.json └── src ├── defaults.js ├── info.js ├── main.js ├── scss └── strength.scss └── strength.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"] 3 | } 4 | -------------------------------------------------------------------------------- /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /.csscomb.json: -------------------------------------------------------------------------------- 1 | { 2 | "remove-empty-rulesets": true, 3 | "always-semicolon": true, 4 | "color-case": "lower", 5 | "block-indent": " ", 6 | "color-shorthand": true, 7 | "element-case": "lower", 8 | "eof-newline": true, 9 | "leading-zero": false, 10 | "quotes": "double", 11 | "space-before-colon": "", 12 | "space-after-colon": " ", 13 | "space-before-combinator": " ", 14 | "space-after-combinator": " ", 15 | "space-between-declarations": "\n", 16 | "space-before-opening-brace": " ", 17 | "space-after-opening-brace": "\n", 18 | "space-after-selector-delimiter": " ", 19 | "space-before-selector-delimiter": "", 20 | "space-before-closing-brace": "\n", 21 | "strip-spaces": true, 22 | "tab-size": true, 23 | "unitless-zero": true, 24 | "vendor-prefix-align": false, 25 | "sort-order": [ 26 | "position", 27 | "top", 28 | "right", 29 | "bottom", 30 | "left", 31 | "z-index", 32 | "-moz-box-sizing", 33 | "-webkit-box-sizing", 34 | "box-sizing", 35 | "display", 36 | "flex", 37 | "flex-align", 38 | "flex-basis", 39 | "flex-direction", 40 | "flex-flow", 41 | "flex-grow", 42 | "flex-order", 43 | "flex-pack", 44 | "float", 45 | "width", 46 | "min-width", 47 | "max-width", 48 | "height", 49 | "min-height", 50 | "max-height", 51 | "padding", 52 | "padding-top", 53 | "padding-right", 54 | "padding-bottom", 55 | "padding-left", 56 | "margin", 57 | "margin-top", 58 | "margin-right", 59 | "margin-bottom", 60 | "margin-left", 61 | "overflow", 62 | "overflow-x", 63 | "overflow-y", 64 | "-webkit-overflow-scrolling", 65 | "-ms-overflow-x", 66 | "-ms-overflow-y", 67 | "-ms-overflow-style", 68 | "clip", 69 | "clear", 70 | "font", 71 | "font-family", 72 | "font-size", 73 | "font-style", 74 | "font-weight", 75 | "font-variant", 76 | "font-size-adjust", 77 | "font-stretch", 78 | "font-effect", 79 | "font-emphasize", 80 | "font-emphasize-position", 81 | "font-emphasize-style", 82 | "font-smooth", 83 | "-webkit-hyphens", 84 | "-moz-hyphens", 85 | "hyphens", 86 | "line-height", 87 | "color", 88 | "text-align", 89 | "-webkit-text-align-last", 90 | "-moz-text-align-last", 91 | "-ms-text-align-last", 92 | "text-align-last", 93 | "text-emphasis", 94 | "text-emphasis-color", 95 | "text-emphasis-style", 96 | "text-emphasis-position", 97 | "text-decoration", 98 | "text-indent", 99 | "text-justify", 100 | "text-outline", 101 | "-ms-text-overflow", 102 | "text-overflow", 103 | "text-overflow-ellipsis", 104 | "text-overflow-mode", 105 | "text-shadow", 106 | "text-transform", 107 | "text-wrap", 108 | "-webkit-text-size-adjust", 109 | "-ms-text-size-adjust", 110 | "letter-spacing", 111 | "-ms-word-break", 112 | "word-break", 113 | "word-spacing", 114 | "-ms-word-wrap", 115 | "word-wrap", 116 | "-moz-tab-size", 117 | "-o-tab-size", 118 | "tab-size", 119 | "white-space", 120 | "vertical-align", 121 | "list-style", 122 | "list-style-position", 123 | "list-style-type", 124 | "list-style-image", 125 | "pointer-events", 126 | "-ms-touch-action", 127 | "touch-action", 128 | "cursor", 129 | "visibility", 130 | "zoom", 131 | "table-layout", 132 | "empty-cells", 133 | "caption-side", 134 | "border-spacing", 135 | "border-collapse", 136 | "content", 137 | "quotes", 138 | "counter-reset", 139 | "counter-increment", 140 | "resize", 141 | "-webkit-user-select", 142 | "-moz-user-select", 143 | "-ms-user-select", 144 | "-o-user-select", 145 | "user-select", 146 | "nav-index", 147 | "nav-up", 148 | "nav-right", 149 | "nav-down", 150 | "nav-left", 151 | "background", 152 | "background-color", 153 | "background-image", 154 | "-ms-filter:\\'progid:DXImageTransform.Microsoft.gradient", 155 | "filter:progid:DXImageTransform.Microsoft.gradient", 156 | "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader", 157 | "filter", 158 | "background-repeat", 159 | "background-attachment", 160 | "background-position", 161 | "background-position-x", 162 | "background-position-y", 163 | "-moz-background-clip", 164 | "-webkit-background-clip", 165 | "background-clip", 166 | "background-origin", 167 | "-moz-background-size", 168 | "-o-background-size", 169 | "-webkit-background-size", 170 | "background-size", 171 | "border", 172 | "border-color", 173 | "border-style", 174 | "border-width", 175 | "border-top", 176 | "border-top-color", 177 | "border-top-style", 178 | "border-top-width", 179 | "border-right", 180 | "border-right-color", 181 | "border-right-style", 182 | "border-right-width", 183 | "border-bottom", 184 | "border-bottom-color", 185 | "border-bottom-style", 186 | "border-bottom-width", 187 | "border-left", 188 | "border-left-color", 189 | "border-left-style", 190 | "border-left-width", 191 | "-moz-border-radius", 192 | "-o-border-radius", 193 | "-webkit-border-radius", 194 | "border-radius", 195 | "border-top-left-radius", 196 | "border-top-right-radius", 197 | "border-bottom-right-radius", 198 | "border-bottom-left-radius", 199 | "-moz-border-image", 200 | "-o-border-image", 201 | "-webkit-border-image", 202 | "border-image", 203 | "-moz-border-image-source", 204 | "-o-border-image-source", 205 | "-webkit-border-image-source", 206 | "border-image-source", 207 | "-moz-border-image-slice", 208 | "-o-border-image-slice", 209 | "-webkit-border-image-slice", 210 | "border-image-slice", 211 | "-moz-border-image-width", 212 | "-o-border-image-width", 213 | "-webkit-border-image-width", 214 | "border-image-width", 215 | "-moz-border-image-outset", 216 | "-o-border-image-outset", 217 | "-webkit-border-image-outset", 218 | "border-image-outset", 219 | "-moz-border-image-repeat", 220 | "-o-border-image-repeat", 221 | "-webkit-border-image-repeat", 222 | "border-image-repeat", 223 | "outline", 224 | "outline-width", 225 | "outline-style", 226 | "outline-color", 227 | "outline-offset", 228 | "-webkit-box-shadow", 229 | "-moz-box-shadow", 230 | "box-shadow", 231 | "filter:progid:DXImageTransform.Microsoft.Alpha(Opacity", 232 | "-ms-filter:\\'progid:DXImageTransform.Microsoft.Alpha", 233 | "opacity", 234 | "-ms-interpolation-mode", 235 | "-moz-transition", 236 | "-ms-transition", 237 | "-o-transition", 238 | "-webkit-transition", 239 | "transition", 240 | "-moz-transition-delay", 241 | "-ms-transition-delay", 242 | "-o-transition-delay", 243 | "-webkit-transition-delay", 244 | "transition-delay", 245 | "-moz-transition-timing-function", 246 | "-ms-transition-timing-function", 247 | "-o-transition-timing-function", 248 | "-webkit-transition-timing-function", 249 | "transition-timing-function", 250 | "-moz-transition-duration", 251 | "-ms-transition-duration", 252 | "-o-transition-duration", 253 | "-webkit-transition-duration", 254 | "transition-duration", 255 | "-moz-transition-property", 256 | "-ms-transition-property", 257 | "-o-transition-property", 258 | "-webkit-transition-property", 259 | "transition-property", 260 | "-moz-transform", 261 | "-ms-transform", 262 | "-o-transform", 263 | "-webkit-transform", 264 | "transform", 265 | "-moz-transform-origin", 266 | "-ms-transform-origin", 267 | "-o-transform-origin", 268 | "-webkit-transform-origin", 269 | "transform-origin", 270 | "-webkit-animation", 271 | "-moz-animation", 272 | "-ms-animation", 273 | "-o-animation", 274 | "animation", 275 | "-moz-animation-name", 276 | "-ms-animation-name", 277 | "-o-animation-name", 278 | "-webkit-animation-name", 279 | "animation-name", 280 | "-moz-animation-duration", 281 | "-ms-animation-duration", 282 | "-o-animation-duration", 283 | "-webkit-animation-duration", 284 | "animation-duration", 285 | "-moz-animation-play-state", 286 | "-ms-animation-play-state", 287 | "-o-animation-play-state", 288 | "-webkit-animation-play-state", 289 | "animation-play-state", 290 | "-moz-animation-timing-function", 291 | "-ms-animation-timing-function", 292 | "-o-animation-timing-function", 293 | "-webkit-animation-timing-function", 294 | "animation-timing-function", 295 | "-moz-animation-delay", 296 | "-ms-animation-delay", 297 | "-o-animation-delay", 298 | "-webkit-animation-delay", 299 | "animation-delay", 300 | "-moz-animation-iteration-count", 301 | "-ms-animation-iteration-count", 302 | "-o-animation-iteration-count", 303 | "-webkit-animation-iteration-count", 304 | "animation-iteration-count", 305 | "-moz-animation-direction", 306 | "-ms-animation-direction", 307 | "-o-animation-direction", 308 | "-webkit-animation-direction" 309 | ] 310 | } 311 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # End every file with a newline 7 | [*] 8 | charset = utf-8 9 | indent_style = space 10 | indent_size = 2 11 | end_of_line = lf 12 | insert_final_newline = true 13 | trim_trailing_whitespace = true 14 | 15 | [*.{css,less,scss}] 16 | indent_style = space 17 | indent_size = 2 18 | 19 | [*.{js,json}] 20 | indent_style = space 21 | indent_size = 2 22 | 23 | [*.{html,hbs}] 24 | indent_style = tab 25 | indent_size = 2 26 | 27 | [*.md] 28 | indent_style = space 29 | indent_size = 2 30 | insert_final_newline = false 31 | trim_trailing_whitespace = false 32 | 33 | [*.xml] 34 | indent_style = space 35 | indent_size = 2 36 | 37 | [*.yml] 38 | indent_style = space 39 | indent_size = 2 40 | 41 | [*.inc] 42 | indent_style = tab 43 | indent_size = 2 -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "babel-eslint", 4 | "env": { 5 | "browser": true, 6 | "es6": true, 7 | "jquery": true, 8 | "builtin": true, 9 | "mocha": true, 10 | "node": true 11 | }, 12 | "extends": "eslint:recommended", 13 | "parserOptions": { 14 | "ecmaVersion": 8, 15 | "sourceType": "module" 16 | }, 17 | // "plugins": [ 18 | // "prettier" 19 | // ], 20 | "rules": { 21 | // Prettier 22 | // "prettier/prettier": "error", 23 | // Possible Errors 24 | "no-inner-declarations": "off", 25 | "no-await-in-loop": "error", 26 | "no-compare-neg-zero": "error", 27 | "no-extra-parens": ["error", "all", { 28 | "returnAssign": false, 29 | "enforceForArrowConditionals": false 30 | } 31 | ], 32 | "no-prototype-builtins": "off", 33 | "no-template-curly-in-string": "error", 34 | "valid-jsdoc": "off", 35 | 36 | // Best Practices 37 | "accessor-pairs": "error", 38 | "array-callback-return": "error", 39 | "block-scoped-var": "error", 40 | "class-methods-use-this": "off", 41 | "complexity": "error", 42 | "consistent-return": "error", 43 | "curly": "error", 44 | "default-case": "error", 45 | "dot-location": ["error", "property"], 46 | "dot-notation": "error", 47 | "eqeqeq": "error", 48 | "guard-for-in": "error", 49 | "no-alert": "error", 50 | "no-caller": "error", 51 | "no-div-regex": "error", 52 | "no-else-return": "error", 53 | "no-empty-function": "error", 54 | "no-eq-null": "error", 55 | "no-eval": "error", 56 | "no-extend-native": "error", 57 | "no-extra-bind": "error", 58 | "no-extra-label": "error", 59 | "no-floating-decimal": "error", 60 | "no-implicit-coercion": "error", 61 | "no-implicit-globals": "error", 62 | "no-implied-eval": "error", 63 | "no-invalid-this": "off", 64 | "no-iterator": "error", 65 | "no-labels": "error", 66 | "no-lone-blocks": "error", 67 | "no-loop-func": "error", 68 | "no-magic-numbers": "off", 69 | "no-multi-spaces": ["error", { 70 | "exceptions": { 71 | "AssignmentExpression": true, 72 | "ArrowFunctionExpression": true, 73 | "CallExpression": true, 74 | "VariableDeclarator": true 75 | } 76 | } 77 | ], 78 | "no-multi-str": "error", 79 | "no-new": "error", 80 | "no-new-func": "error", 81 | "no-new-wrappers": "error", 82 | "no-octal-escape": "error", 83 | "no-param-reassign": "off", 84 | "no-proto": "error", 85 | "no-restricted-properties": "error", 86 | "no-return-assign": "off", 87 | "no-return-await": "error", 88 | "no-script-url": "error", 89 | "no-self-compare": "error", 90 | "no-sequences": "error", 91 | "no-throw-literal": "error", 92 | "no-unmodified-loop-condition": "error", 93 | "no-unused-expressions": "error", 94 | "no-useless-call": "error", 95 | "no-useless-concat": "error", 96 | "no-useless-escape": "error", 97 | "no-useless-return": "off", 98 | "no-void": "error", 99 | "no-warning-comments": "off", 100 | "no-with": "error", 101 | "prefer-promise-reject-errors": "error", 102 | "radix": "error", 103 | "require-await": "error", 104 | "vars-on-top": "error", 105 | "wrap-iife": "error", 106 | "yoda": "error", 107 | 108 | // Strict Mode 109 | "strict": "error", 110 | 111 | // Variables 112 | "init-declarations": "off", 113 | "no-catch-shadow": "error", 114 | "no-label-var": "error", 115 | "no-restricted-globals": "error", 116 | "no-shadow": "off", 117 | "no-shadow-restricted-names": "error", 118 | "no-undef-init": "error", 119 | "no-undefined": "off", 120 | "no-use-before-define": "off", 121 | 122 | // Node.js and CommonJS 123 | "callback-return": "off", 124 | "global-require": "error", 125 | "handle-callback-err": "error", 126 | "no-mixed-requires": "error", 127 | "no-new-require": "error", 128 | "no-path-concat": "error", 129 | "no-process-env": "error", 130 | "no-process-exit": "error", 131 | "no-restricted-modules": "error", 132 | "no-sync": "error", 133 | 134 | // Stylistic Issues 135 | "array-bracket-spacing": "error", 136 | "block-spacing": "error", 137 | "brace-style": "error", 138 | "camelcase": "error", 139 | "capitalized-comments": "off", 140 | "comma-dangle": "error", 141 | "comma-spacing": "error", 142 | "comma-style": "error", 143 | "computed-property-spacing": "error", 144 | "consistent-this": "error", 145 | "eol-last": "error", 146 | "func-call-spacing": "error", 147 | "func-name-matching": "error", 148 | "func-names": "off", 149 | "func-style": ["error", "declaration", { 150 | "allowArrowFunctions": true 151 | }], 152 | "id-blacklist": "error", 153 | "id-length": "off", 154 | "id-match": "error", 155 | "indent": "off", 156 | "indent-legacy": ["error", 2, { "SwitchCase": 1 }], 157 | "jsx-quotes": "error", 158 | "key-spacing": "off", 159 | "keyword-spacing": "error", 160 | "line-comment-position": "off", 161 | "linebreak-style": ["error", "unix"], 162 | "lines-around-comment": "off", 163 | "lines-around-directive": "error", 164 | "max-depth": ["error", 10], 165 | "max-len": "off", 166 | "max-lines": "off", 167 | "max-nested-callbacks": "error", 168 | "max-params": "off", 169 | "max-statements": "off", 170 | "max-statements-per-line": "error", 171 | "multiline-ternary": "off", 172 | "new-cap": ["error", { "capIsNewExceptionPattern": "$.*" }], 173 | "new-parens": "error", 174 | "newline-after-var": "off", 175 | "newline-before-return": "off", 176 | "newline-per-chained-call": ["error", { "ignoreChainWithDepth": 5 }], 177 | "no-array-constructor": "error", 178 | "no-bitwise": "error", 179 | "no-continue": "off", 180 | "no-inline-comments": "off", 181 | "no-lonely-if": "error", 182 | "no-mixed-operators": "off", 183 | "no-multi-assign": "error", 184 | "no-multiple-empty-lines": "error", 185 | "no-negated-condition": "off", 186 | "no-nested-ternary": "error", 187 | "no-new-object": "error", 188 | "no-plusplus": "off", 189 | "no-restricted-syntax": "error", 190 | "no-tabs": "error", 191 | "no-ternary": "off", 192 | "no-trailing-spaces": "error", 193 | "no-underscore-dangle": "off", 194 | "no-unneeded-ternary": "error", 195 | "no-whitespace-before-property": "error", 196 | "nonblock-statement-body-position": "error", 197 | "object-curly-newline": ["error", { "minProperties": 1 }], 198 | "object-curly-spacing": ["error", "always"], 199 | "object-property-newline": "error", 200 | "one-var": ["error", "never"], 201 | "one-var-declaration-per-line": "error", 202 | "operator-assignment": "error", 203 | "operator-linebreak": "off", 204 | "padded-blocks": "off", 205 | "quote-props": ["error", "as-needed"], 206 | "quotes": ["error", "single", { "allowTemplateLiterals": true }], 207 | "require-jsdoc": "off", 208 | "semi": ["error", "always"], 209 | "semi-spacing": ["error", {"before": false, "after": true}], 210 | "sort-keys": "off", 211 | "sort-vars": "error", 212 | "space-before-blocks": "error", 213 | "space-before-function-paren": ["error", { 214 | "anonymous": "always", 215 | "named": "never" 216 | }], 217 | "space-in-parens": "error", 218 | "space-infix-ops": "error", 219 | "space-unary-ops": "error", 220 | "spaced-comment": "error", 221 | "template-tag-spacing": "error", 222 | "unicode-bom": "error", 223 | "wrap-regex": "off", 224 | 225 | // ECMAScript 6 226 | "arrow-body-style": "off", 227 | "arrow-parens": "error", 228 | "arrow-spacing": "error", 229 | "generator-star-spacing": "error", 230 | "no-confusing-arrow": ["error", { "allowParens": true }], 231 | "no-duplicate-imports": "error", 232 | "no-restricted-imports": "error", 233 | "no-useless-computed-key": "error", 234 | "no-useless-constructor": "error", 235 | "no-useless-rename": "error", 236 | "no-var": "error", 237 | "object-shorthand": "error", 238 | "prefer-arrow-callback": "error", 239 | "prefer-const": "error", 240 | "prefer-destructuring": "off", 241 | "prefer-numeric-literals": "error", 242 | "prefer-rest-params": "error", 243 | "prefer-spread": "error", 244 | "prefer-template": "error", 245 | "rest-spread-spacing": "error", 246 | "sort-imports": "off", 247 | "symbol-description": "error", 248 | "template-curly-spacing": "error", 249 | "yield-star-spacing": "error" 250 | } 251 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ################################## 2 | # project-specific.gitattributes # 3 | ################################## 4 | 5 | 6 | ########################### 7 | # documents.gitattributes # 8 | ########################### 9 | *.doc diff=astextplain 10 | *.docx diff=astextplain 11 | *.dot diff=astextplain 12 | *.pdf diff=astextplain 13 | *.rtf diff=astextplain 14 | *.css text eol=lf 15 | *.scss text eol=lf 16 | *.less text eol=lf 17 | *.html text eol=lf 18 | *.js text eol=lf 19 | *.json text eol=lf 20 | *.md text eol=lf 21 | *.txt text eol=lf 22 | *.hbs text eol=lf 23 | *.mustache text eol=lf 24 | *.xml text eol=lf 25 | *.csv text eol=lf 26 | *.tab text eol=lf 27 | *.tsv text eol=lf 28 | *.sql text eol=lf 29 | 30 | ############################ 31 | ### graphics.gitattributes # 32 | ############################ 33 | *.png binary 34 | *.jpg binary 35 | *.jpeg binary 36 | *.gif binary 37 | *.tif binary 38 | *.tiff binary 39 | *.ico binary 40 | *.svg binary 41 | *.eps binary 42 | *.psd binary 43 | *.ai binary 44 | 45 | ########################### 46 | # git-crypt.gitattributes # 47 | ########################### 48 | vault_* binary filter=git-crypt diff=git-crypt 49 | vault.* binary filter=git-crypt diff=git-crypt 50 | 51 | ####################### 52 | # shell.gitattributes # 53 | ####################### 54 | # Linux 55 | *.sh text eol=lf 56 | 57 | # Windows 58 | *.bat text eol=crlf 59 | *.cmd text eol=crlf 60 | *.ps1 text eol=crlf -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | _[Remove this line and all of the above before submitting your issue]_ 7 | 8 | ### Checklist 9 | * [ ] I'm using **version** [x.x.x] 10 | * [ ] My **browser** is: 11 | * [ ] This is a **Sass** issue: I'm using version [x.x.x] 12 | * [ ] I am sure this issue is **not a duplicate**? 13 | 14 | ### Description 15 | 16 | [Description of the bug, enhancement, or question] 17 | [Please tag accordingly] 18 | 19 | ### How can we reproduce this bug? 20 | 21 | 1. [First Step] 22 | 2. [Second Step] 23 | 3. [and so on...] 24 | 25 | ### What did you expect to happen? 26 | 27 | ### What happened instead? 28 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 13 | 14 | _[Remove this line and all of the above before submitting your PR]_ 15 | 16 | ### Pull Request 17 | 18 | Fixes # 19 | 20 | Changes proposed: 21 | 22 | * [ ] Add 23 | * [ ] Fix 24 | * [ ] Remove 25 | * [ ] Update 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #################### 2 | # project-specific # 3 | #################### 4 | 5 | 6 | #################### 7 | # Generated source # 8 | #################### 9 | _build/ 10 | archives/ 11 | results 12 | screenshots 13 | 14 | ########################## 15 | # Dependency directories # 16 | ########################## 17 | bower_components 18 | node_modules 19 | 20 | ####### 21 | # OSX # 22 | ####### 23 | .DS_Store 24 | .AppleDouble 25 | .LSOverride 26 | 27 | # Icon must end with two \r 28 | Icon 29 | 30 | # Thumbnails 31 | ._* 32 | 33 | # Files that might appear in the root of a volume 34 | .DocumentRevisions-V100 35 | .fseventsd 36 | .Spotlight-V100 37 | .TemporaryItems 38 | .Trashes 39 | .VolumeIcon.icns 40 | 41 | # Directories potentially created on remote AFP share 42 | .AppleDB 43 | .AppleDesktop 44 | Network Trash Folder 45 | Temporary Items 46 | .apdisk 47 | 48 | ########### 49 | # Windows # 50 | ########### 51 | # Windows image file caches 52 | Thumbs.db 53 | ehthumbs.db 54 | 55 | # Folder config file 56 | Desktop.ini 57 | 58 | # Recycle Bin used on file shares 59 | $RECYCLE.BIN/ 60 | 61 | # Windows Installer files 62 | *.cab 63 | *.msi 64 | *.msm 65 | *.msp 66 | 67 | # Windows shortcuts 68 | *.lnk 69 | 70 | ######## 71 | # Node # 72 | ######## 73 | # Logs 74 | logs 75 | *.log 76 | npm-debug.log* 77 | 78 | # Runtime data 79 | pids 80 | *.pid 81 | *.seed 82 | 83 | # Directory for instrumented libs generated by jscoverage/JSCover 84 | lib-cov 85 | 86 | # Coverage directory used by tools like istanbul 87 | coverage 88 | 89 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 90 | .grunt 91 | 92 | # Sass 93 | .sass-cache/ 94 | *.css.map 95 | 96 | # node-waf configuration 97 | .lock-wscript 98 | 99 | # Compiled binary addons (http://nodejs.org/api/addons.html) 100 | build/Release 101 | 102 | # Dependency directory 103 | node_modules 104 | 105 | # JSPM 106 | jspm_packages 107 | 108 | # Optional npm cache directory 109 | .npm 110 | 111 | ############### 112 | # SublimeText # 113 | ############### 114 | # cache files for sublime text 115 | *.tmlanguage.cache 116 | *.tmPreferences.cache 117 | *.stTheme.cache 118 | 119 | # workspace files are user-specific 120 | *.sublime-workspace 121 | 122 | # project files should be checked into the repository, unless a significant 123 | # proportion of contributors will probably not be using SublimeText 124 | # *.sublime-project 125 | 126 | # sftp configuration file 127 | sftp-config.json 128 | 129 | ####### 130 | # Vim # 131 | ####### 132 | # swap 133 | [._]*.s[a-w][a-z] 134 | [._]s[a-w][a-z] 135 | # persistent undo 136 | *.un~ 137 | # session 138 | Session.vim 139 | # temporary 140 | .netrwhist 141 | *~ 142 | 143 | ############# 144 | # NotepadPP # 145 | ############## 146 | *.bak 147 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .travis.xml 2 | yarn.lock 3 | package-lock.json 4 | .editorconfig 5 | .babelrc 6 | .beautifyrc 7 | .sass-cache 8 | .bowerrc 9 | .eslintrc.yml 10 | .github 11 | .gitattributes 12 | .release.json 13 | _build 14 | bower.json 15 | composer.json 16 | gulpfile.js 17 | gulpfile.babel.js 18 | README.md 19 | CONTRIBUTING.md 20 | coverage 21 | docs 22 | test 23 | gulp 24 | test 25 | demo 26 | libs 27 | archives 28 | config.js 29 | karma.conf.js 30 | bower_components 31 | -------------------------------------------------------------------------------- /.release.json: -------------------------------------------------------------------------------- 1 | { 2 | "verbose": false, 3 | "force": false, 4 | "pkgFiles": ["package.json", "bower.json"], 5 | "increment": "patch", 6 | "commitMessage": "Release v%s", 7 | "tagName": "v%s", 8 | "tagAnnotation": "Release v%s", 9 | "buildCommand": "gulp build", 10 | "npm": { 11 | "publish": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.stylelintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "stylelint-config-bootstrap" 3 | } 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "5.10" 4 | - "6.2" 5 | - "stable" 6 | sudo: false 7 | 8 | script: 9 | - npm run test 10 | 11 | cache: 12 | directories: 13 | - node_modules 14 | 15 | addons: 16 | sauce_connect: true 17 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to this project 2 | 3 | Please take a moment to review this document in order to make the contribution 4 | process easy and effective for everyone involved. 5 | 6 | Following these guidelines helps to communicate that you respect the time of 7 | the developers managing and developing this open source project. In return, 8 | they should reciprocate that respect in addressing your issue or assessing 9 | patches and features. 10 | 11 | 12 | ## Using the issue tracker 13 | 14 | The issue tracker is the preferred channel for [bug reports](#bug-reports), 15 | [features requests](#feature-requests) and submitting pull requests, but please 16 | respect the following restrictions: 17 | 18 | * Please **do not** use the issue tracker for personal support requests (use 19 | [Stack Overflow](http://stackoverflow.com) or IRC). 20 | 21 | * Please **do not** derail or troll issues. Keep the discussion on topic and 22 | respect the opinions of others. 23 | 24 | 25 | ## Bug reports 26 | 27 | A bug is a _demonstrable problem_ that is caused by the code in the repository. 28 | Good bug reports are extremely helpful - thank you! 29 | 30 | Guidelines for bug reports: 31 | 32 | 1. **Use the GitHub issue search.** Check if the issue has already been 33 | reported. 34 | 35 | 2. **Check if the issue has been fixed.** Try to reproduce it using the 36 | latest `master` or development branch in the repository. 37 | 38 | 3. **Provide environment details.** Provide your operating system, browser(s), 39 | devices, and jquery-strength version. 40 | 41 | 4. **Create an isolated and reproducible test case.** Create a [reduced test 42 | case](http://css-tricks.com/6263-reduced-test-cases/). 43 | 44 | 5. **Include a live example.** Make use of jsFiddle or jsBin to share your 45 | isolated test cases. 46 | 47 | A good bug report shouldn't leave others needing to chase you up for more 48 | information. Please try to be as detailed as possible in your report. What is 49 | your environment? What steps will reproduce the issue? What browser(s) and OS 50 | experience the problem? What would you expect to be the outcome? All these 51 | details will help people to fix any potential bugs. 52 | 53 | Example: 54 | 55 | > Short and descriptive example bug report title 56 | > 57 | > A summary of the issue and the browser/OS environment in which it occurs. If 58 | > suitable, include the steps required to reproduce the bug. 59 | > 60 | > 1. This is the first step 61 | > 2. This is the second step 62 | > 3. Further steps, etc. 63 | > 64 | > `` - a link to the reduced test case 65 | > 66 | > Any other information you want to share that is relevant to the issue being 67 | > reported. This might include the lines of code that you have identified as 68 | > causing the bug, and potential solutions (and your opinions on their 69 | > merits). 70 | 71 | 72 | ## Feature requests 73 | 74 | Feature requests are welcome. But take a moment to find out whether your idea 75 | fits with the scope and aims of the project. It's up to *you* to make a strong 76 | case to convince the project's developers of the merits of this feature. Please 77 | provide as much detail and context as possible. 78 | 79 | 80 | ## Pull Requests 81 | 82 | **Working on your first Pull Request?** You can learn how from this *free* series [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github) 83 | 84 | Good pull requests - patches, improvements, new features - are a fantastic 85 | help. They should remain focused in scope and avoid containing unrelated 86 | commits. 87 | 88 | **Please ask first** before embarking on any significant pull request (e.g. 89 | implementing features, refactoring code, porting to a different language), 90 | otherwise you risk spending a lot of time working on something that the 91 | project's developers might not want to merge into the project. 92 | 93 | Please adhere to the coding conventions used throughout a project (indentation, 94 | accurate comments, etc.) and any other requirements (such as test coverage). 95 | 96 | Follow this process if you'd like your work considered for inclusion in the 97 | project: 98 | 99 | 1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork, 100 | and configure the remotes: 101 | 102 | ```bash 103 | # Clone your fork of the repo into the current directory 104 | git clone https://github.com// 105 | # Navigate to the newly cloned directory 106 | cd 107 | # Assign the original repo to a remote called "upstream" 108 | git remote add upstream https://github.com// 109 | ``` 110 | 111 | 2. If you cloned a while ago, get the latest changes from upstream: 112 | 113 | ```bash 114 | git checkout 115 | git pull upstream 116 | ``` 117 | 118 | 3. Create a new topic branch (off the main project development branch) to 119 | contain your feature, change, or fix: 120 | 121 | ```bash 122 | git checkout -b 123 | ``` 124 | 125 | 4. Commit your changes in logical chunks. Please adhere to these [git commit 126 | message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) 127 | or your code is unlikely be merged into the main project. Use Git's 128 | [interactive rebase](https://help.github.com/articles/interactive-rebase) 129 | feature to tidy up your commits before making them public. 130 | 131 | 5. Locally merge (or rebase) the upstream development branch into your topic branch: 132 | 133 | ```bash 134 | git pull [--rebase] upstream 135 | ``` 136 | 137 | 6. Push your topic branch up to your fork: 138 | 139 | ```bash 140 | git push origin 141 | ``` 142 | 143 | 7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) 144 | with a clear title and description. 145 | 146 | **IMPORTANT**: By submitting a patch, you agree to allow the project owner to 147 | license your work under the same license as that used by the project. 148 | 149 | 150 | ## Code Consitency 151 | 152 | To help create consistent looking code throughout the project, we use a few tools to help us. 153 | 154 | #### ESlint 155 | We use [ESlint](http://eslint.org) on each build to find easy-to-catch errors and potential problems in our js. You can find our ESlint settings in the `.eslintrc.yml` file in the root of the project. 156 | 157 | #### EditorConfig 158 | 159 | We use [EditorConfig](http://EditorConfig.org) to maintain consistent coding styles between various editors and IDEs. You can find our settings in the `.editorconfig` file in the root of the project. 160 | 161 | 162 | ## Development 163 | 164 | We are using node, gulp and babel to build and (in the future) test this project. This means that you must setup a local development environment: 165 | 166 | 1. Install `node` and `npm` using your preferred method 167 | 2. Install the gulp CLI: `npm install -g gulp-cli` 168 | 3. Install the Babel CLI: `npm install -g babel-cli` 169 | 4. Install the project's development dependencies: `npm install` 170 | 171 | #### Available Tasks 172 | - `gulp` or `gulp watch` Start watch for changes and server with Browsersync. 173 | - `gulp build` Run all development tasks 174 | - `gulp serve` Start server with Browsersync. 175 | - `gulp clean` Clean output directories. 176 | - `gulp bundler` Bundle javasript modules. 177 | - `gulp scripts` Concatenate and minify JavaScript to `dist`. 178 | - `gulp lint:script` Lint ES6 files using eslint. 179 | - `gulp clean` Clean out distribution javascript files. 180 | - `gulp tdd` Test for Test Driven Development purposes. 181 | - `gulp test` Test for Continuous Integration purposes. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [jQuery strength](https://github.com/amazingSurge/jquery-strength) ![bower][bower-image] [![NPM version][npm-image]][npm-url] [![Dependency Status][daviddm-image]][daviddm-url] [![prs-welcome]](#contributing) 2 | 3 | > jQuery strength provides a toggle feature for password input fields that allows the user to view or asterisk the password. It also features a strength indicator to show how secure a users password is. 4 | 5 | It base on Nando Vieira's [password_strength](https://github.com/fnando/password_strength) which validates the strength of a password according to several rules. 6 | 7 | ## Table of contents 8 | - [Main files](#main-files) 9 | - [Quick start](#quick-start) 10 | - [Requirements](#requirements) 11 | - [Usage](#usage) 12 | - [Examples](#examples) 13 | - [Options](#options) 14 | - [Methods](#methods) 15 | - [Events](#events) 16 | - [No conflict](#no-conflict) 17 | - [Browser support](#browser-support) 18 | - [Contributing](#contributing) 19 | - [Development](#development) 20 | - [Changelog](#changelog) 21 | - [Copyright and license](#copyright-and-license) 22 | 23 | ## Main files 24 | ``` 25 | dist/ 26 | ├── jquery-strength.js 27 | ├── jquery-strength.es.js 28 | ├── jquery-strength.min.js 29 | └── css/ 30 |    ├── strength.css 31 |    └── strength.min.css 32 | ``` 33 | 34 | ## Quick start 35 | Several quick start options are available: 36 | #### Download the latest build 37 | 38 | * [Development](https://raw.githubusercontent.com/amazingSurge/jquery-strength/master/dist/jquery-strength.js) - unminified 39 | * [Production](https://raw.githubusercontent.com/amazingSurge/jquery-strength/master/dist/jquery-strength.min.js) - minified 40 | 41 | #### Install From Bower 42 | ```sh 43 | bower install jquery-strength --save 44 | ``` 45 | 46 | #### Install From Npm 47 | ```sh 48 | npm install jquery-strength --save 49 | ``` 50 | 51 | #### Install From Yarn 52 | ```sh 53 | yarn add jquery-strength 54 | ``` 55 | 56 | #### Build From Source 57 | If you want build from source: 58 | 59 | ```sh 60 | git clone git@github.com:amazingSurge/jquery-strength.git 61 | cd jquery-strength 62 | npm install 63 | npm install -g gulp-cli babel-cli 64 | gulp build 65 | ``` 66 | 67 | Done! 68 | 69 | ## Requirements 70 | `jquery-strength` requires the latest version of [`jQuery`](https://jquery.com/download/). 71 | 72 | ## Usage 73 | #### Including files: 74 | 75 | ```html 76 | 77 | 78 | 79 | ``` 80 | 81 | #### Required HTML structure 82 | 83 | ```html 84 | 85 | ``` 86 | 87 | #### Initialization 88 | All you need to do is call the plugin on the element: 89 | 90 | ```javascript 91 | jQuery(function($) { 92 | $('.example').strength(); 93 | }); 94 | ``` 95 | 96 | ## Examples 97 | There are some example usages that you can look at to get started. They can be found in the 98 | [examples folder](https://github.com/amazingSurge/jquery-strength/tree/master/examples). 99 | 100 | ## Options 101 | `jquery-strength` can accept an options object to alter the way it behaves. You can see the default options by call `$.strength.setDefaults()`. The structure of an options object is as follows: 102 | 103 | ``` 104 | { 105 | namespace: 'strength', 106 | skin: null, 107 | 108 | showMeter: true, 109 | showToggle: true, 110 | 111 | usernameField: '', 112 | 113 | templates: { 114 | toggle: '', 115 | meter: '
{score}
', 116 | score: '', 117 | main: '
{input}{toggle}
{meter}
' 118 | }, 119 | 120 | classes: { 121 | container: 'strength-container', 122 | status: 'strength-{status}', 123 | input: 'strength-input', 124 | toggle: 'strength-toggle', 125 | meter: 'strength-meter', 126 | score: 'strength-score' 127 | }, 128 | 129 | scoreLables: { 130 | empty: 'Empty', 131 | invalid: 'Invalid', 132 | weak: 'Weak', 133 | good: 'Good', 134 | strong: 'Strong' 135 | }, 136 | 137 | scoreClasses: { 138 | empty: '', 139 | invalid: 'label-danger', 140 | weak: 'label-warning', 141 | good: 'label-info', 142 | strong: 'label-success' 143 | }, 144 | 145 | emptyStatus: true, 146 | 147 | scoreCallback: null, 148 | statusCallback: null 149 | } 150 | ``` 151 | 152 | ## Methods 153 | Methods are called on strength instances through the strength method itself. 154 | You can also save the instances to variable for further use. 155 | 156 | ```javascript 157 | // call directly 158 | $().strength('destroy'); 159 | 160 | // or 161 | var api = $().data('strength'); 162 | api.destroy(); 163 | ``` 164 | 165 | #### getScore() 166 | Get the password score. 167 | ```javascript 168 | var score = $().strength('getScore'); 169 | ``` 170 | 171 | #### getStatus() 172 | Get the input status. 173 | ```javascript 174 | var status = $().strength('getStatus'); 175 | ``` 176 | 177 | #### toggle() 178 | Toggle to show or hide the password. 179 | ```javascript 180 | $().strength('toggle'); 181 | ``` 182 | 183 | 184 | #### destroy() 185 | Destroy the strength instance. 186 | ```javascript 187 | $().strength('destroy'); 188 | ``` 189 | 190 | ## Events 191 | `jquery-strength` provides custom events for the plugin’s unique actions. 192 | 193 | ```javascript 194 | $('.the-element').on('strength::ready', function (e) { 195 | // on instance ready 196 | }); 197 | 198 | ``` 199 | 200 | Event | Description 201 | ------------ | ----------- 202 | init | Fires when the instance is setup for the first time. 203 | ready | Fires when the instance is ready for API use. 204 | toggle | Fired immediately when the `toggle` instance method has been called. 205 | statusChange | Fires when the input status changed. 206 | check | Fired immediately when the `check` instance method has been called. 207 | destroy | Fires when an instance is destroyed. 208 | 209 | ## No conflict 210 | If you have to use other plugin with the same namespace, just call the `$.strength.noConflict` method to revert to it. 211 | 212 | ```html 213 | 214 | 215 | 219 | ``` 220 | 221 | ## Browser support 222 | 223 | Tested on all major browsers. 224 | 225 | | Safari | Chrome | Firefox | Edge | IE | Opera | 226 | |:--:|:--:|:--:|:--:|:--:|:--:| 227 | | Latest ✓ | Latest ✓ | Latest ✓ | Latest ✓ | 9-11 ✓ | Latest ✓ | 228 | 229 | As a jQuery plugin, you also need to see the [jQuery Browser Support](http://jquery.com/browser-support/). 230 | 231 | ## Contributing 232 | Anyone and everyone is welcome to contribute. Please take a moment to 233 | review the [guidelines for contributing](CONTRIBUTING.md). Make sure you're using the latest version of `jquery-strength` before submitting an issue. There are several ways to help out: 234 | 235 | * [Bug reports](CONTRIBUTING.md#bug-reports) 236 | * [Feature requests](CONTRIBUTING.md#feature-requests) 237 | * [Pull requests](CONTRIBUTING.md#pull-requests) 238 | * Write test cases for open bug issues 239 | * Contribute to the documentation 240 | 241 | ## Development 242 | `jquery-strength` is built modularly and uses Gulp as a build system to build its distributable files. To install the necessary dependencies for the build system, please run: 243 | 244 | ```sh 245 | npm install -g gulp 246 | npm install -g babel-cli 247 | npm install 248 | ``` 249 | 250 | Then you can generate new distributable files from the sources, using: 251 | ``` 252 | gulp build 253 | ``` 254 | 255 | More gulp tasks can be found [here](CONTRIBUTING.md#available-tasks). 256 | 257 | ## Changelog 258 | To see the list of recent changes, see [Releases section](https://github.com/amazingSurge/jquery-strength/releases). 259 | 260 | ## Copyright and license 261 | Copyright (C) 2016 amazingSurge. 262 | 263 | Licensed under [the LGPL license](LICENSE). 264 | 265 | [⬆ back to top](#table-of-contents) 266 | 267 | [bower-image]: https://img.shields.io/bower/v/jquery-strength.svg?style=flat 268 | [bower-link]: https://david-dm.org/amazingSurge/jquery-strength/dev-status.svg 269 | [npm-image]: https://badge.fury.io/js/jquery-strength.svg?style=flat 270 | [npm-url]: https://npmjs.org/package/jquery-strength 271 | [license]: https://img.shields.io/npm/l/jquery-strength.svg?style=flat 272 | [prs-welcome]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg 273 | [daviddm-image]: https://david-dm.org/amazingSurge/jquery-strength.svg?style=flat 274 | [daviddm-url]: https://david-dm.org/amazingSurge/jquery-strength -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-strength", 3 | "version": "0.2.5", 4 | "description": "A jquery plugin that do amazing things.", 5 | "main": "dist/jquery-strength.js", 6 | "copyright": "amazingSurge", 7 | "license": "LGPL-3.0", 8 | "ignore": [ 9 | "**/.*", 10 | "node_modules", 11 | "bower_components", 12 | "test", 13 | "demo", 14 | "gulp", 15 | "CONTRIBUTING.md", 16 | "manifest.json", 17 | "karma.conf.js", 18 | "config.js", 19 | "package.json", 20 | "gulpfile.babel.js", 21 | "tests" 22 | ], 23 | "homepage": "https://github.com/amazingSurge/jquery-strength", 24 | "authors": [ 25 | "amazingSurge " 26 | ], 27 | "moduleType": [ 28 | "globals" 29 | ], 30 | "dependencies": { 31 | "jquery": ">=2.2.0", 32 | "password_strength": "git@github.com:fnando/password_strength.git" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import fs from 'graceful-fs'; 4 | import {argv} from 'yargs'; 5 | 6 | const production = argv.production || argv.prod || false; 7 | 8 | export default { 9 | getConfig: function(pkg, src, dest) { 10 | return { 11 | version: pkg.version, 12 | name: pkg.name, 13 | title: pkg.title, 14 | description: pkg.description, 15 | author: pkg.author, 16 | banner: `/** 17 | * ${pkg.title} v${pkg.version} 18 | * ${pkg.homepage} 19 | * 20 | * Copyright (c) ${pkg.author.name} 21 | * Released under the ${pkg.license} license 22 | */ 23 | `, 24 | // basic locations 25 | paths: { 26 | root: './', 27 | srcDir: `${src}/`, 28 | destDir: `${dest}/`, 29 | }, 30 | 31 | styles: { 32 | files: '**/*.scss', 33 | src: `${src}/scss`, 34 | dest: `${dest}/css`, 35 | prodSourcemap: false, 36 | sassIncludePaths: [], 37 | autoprefixer: { 38 | browsers: ['last 2 versions', 'ie >= 9', 'Android >= 2.3'] 39 | } 40 | }, 41 | 42 | scripts: { 43 | input: 'main.js', 44 | version: 'info.js', 45 | files: '**/*.js', 46 | src: `${src}`, 47 | dest: `${dest}`, 48 | prodSourcemap: false, 49 | test: './test', 50 | gulp: './gulp' 51 | }, 52 | 53 | archive: { 54 | src: `${dest}/**/*`, 55 | dest: './archives/', 56 | zip: {} 57 | }, 58 | 59 | browser: { 60 | baseDir: './', 61 | startPath: "examples/index.html", 62 | browserPort: 3000, 63 | UIPort: 3001, 64 | testPort: 3002, 65 | }, 66 | 67 | deploy: { 68 | versionFiles: ['package.json', 'bower.json'], 69 | increment: "patch", // major, minor, patch, premajor, preminor, prepatch, or prerelease. 70 | }, 71 | 72 | notify: { 73 | title: pkg.title 74 | }, 75 | 76 | env: 'development', 77 | production: production, 78 | setEnv: function(env) { 79 | if (typeof env !== 'string') return; 80 | this.env = env; 81 | this.production = env === 'production'; 82 | process.env.NODE_ENV = env; 83 | }, 84 | 85 | test: {}, 86 | }; 87 | }, 88 | 89 | init: function() { 90 | const pkg = JSON.parse(fs.readFileSync('./package.json', { encoding: 'utf-8' })); 91 | 92 | let src = 'src'; 93 | let dest = 'dist'; 94 | 95 | Object.assign(this, this.getConfig(pkg, src, dest, production)); 96 | this.setEnv(production? 'production': 'development'); 97 | 98 | return this; 99 | } 100 | 101 | }.init(); 102 | -------------------------------------------------------------------------------- /dist/css/strength.css: -------------------------------------------------------------------------------- 1 | /** 2 | * jQuery strength v0.2.5 3 | * https://github.com/amazingSurge/jquery-strength 4 | * 5 | * Copyright (c) amazingSurge 6 | * Released under the LGPL-3.0 license 7 | */ 8 | -------------------------------------------------------------------------------- /dist/css/strength.min.css: -------------------------------------------------------------------------------- 1 | /** 2 | * jQuery strength v0.2.5 3 | * https://github.com/amazingSurge/jquery-strength 4 | * 5 | * Copyright (c) amazingSurge 6 | * Released under the LGPL-3.0 license 7 | */ 8 | 9 | /*# sourceMappingURL=strength.min.css.map */ 10 | -------------------------------------------------------------------------------- /dist/jquery-strength.es.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jQuery strength v0.2.5 3 | * https://github.com/amazingSurge/jquery-strength 4 | * 5 | * Copyright (c) amazingSurge 6 | * Released under the LGPL-3.0 license 7 | */ 8 | import $ from 'jquery'; 9 | import PasswordStrength from 'password_strength'; 10 | 11 | var DEFAULTS = { 12 | namespace: 'strength', 13 | skin: null, 14 | 15 | showMeter: true, 16 | showToggle: true, 17 | 18 | usernameField: '', 19 | 20 | templates: { 21 | toggle: '', 22 | meter: '
{score}
', 23 | score: '', 24 | main: '
{input}{toggle}
{meter}
' 25 | }, 26 | 27 | classes: { 28 | container: 'strength-container', 29 | status: 'strength-{status}', 30 | input: 'strength-input', 31 | toggle: 'strength-toggle', 32 | meter: 'strength-meter', 33 | score: 'strength-score', 34 | shown: 'strength-shown' 35 | }, 36 | 37 | scoreLables: { 38 | empty: 'Empty', 39 | invalid: 'Invalid', 40 | weak: 'Weak', 41 | good: 'Good', 42 | strong: 'Strong' 43 | }, 44 | 45 | scoreClasses: { 46 | empty: '', 47 | invalid: 'label-danger', 48 | weak: 'label-warning', 49 | good: 'label-info', 50 | strong: 'label-success' 51 | }, 52 | 53 | emptyStatus: true, 54 | 55 | scoreCallback: null, 56 | statusCallback: null 57 | }; 58 | 59 | const NAMESPACE$1 = 'strength'; 60 | 61 | /** 62 | * Plugin constructor 63 | **/ 64 | class Strength { 65 | constructor(element, options = {}) { 66 | this.element = element; 67 | this.$element = $(element); 68 | 69 | this.options = $.extend(true, {}, DEFAULTS, options, this.$element.data()); 70 | this.classes = this.options.classes; 71 | 72 | this.$username = $(this.options.usernameField); 73 | 74 | this.score = 0; 75 | this.status = null; 76 | 77 | this.shown = false; 78 | 79 | this.trigger('init'); 80 | this.init(); 81 | } 82 | 83 | init() { 84 | this.createHtml(); 85 | 86 | this.$element.addClass(this.classes.input); 87 | 88 | this.$toggle = this.$container.find(`.${this.classes.toggle}`); 89 | this.$meter = this.$container.find(`.${this.classes.meter}`); 90 | 91 | this.$score = this.$container.find(`.${this.classes.score}`); 92 | this.$input = this.$container.find(`.${this.classes.input}`); 93 | 94 | this.bindEvents(); 95 | 96 | this.initialized = true; 97 | this.trigger('ready'); 98 | } 99 | 100 | bindEvents() { 101 | if(this.$toggle.is(':checkbox')){ 102 | this.$toggle.on('change', () => { 103 | this.toggle(); 104 | }); 105 | } else { 106 | this.$toggle.on('click', () => { 107 | this.toggle(); 108 | }); 109 | } 110 | 111 | 112 | this.$input.bind('keyup.strength keydown.strength', () => { 113 | this.check(); 114 | }); 115 | 116 | this.$element.on(`${NAMESPACE$1}::check`, (e, api, score, status) => { 117 | this.$score.html(this.options.scoreLables[status]); 118 | 119 | if (status !== this.status) { 120 | const newClass = this.options.scoreClasses[status]; 121 | const oldClass = this.options.scoreClasses[this.status]; 122 | this.$score.removeClass(oldClass).addClass(newClass); 123 | 124 | this.trigger('statusChange', status, this.status); 125 | } 126 | 127 | this.status = status; 128 | this.score = score; 129 | }); 130 | 131 | this.$element.on(`${NAMESPACE$1}::statusChange`, (e, api, current, old) => { 132 | this.$container.removeClass(this.getStatusClass(old)).addClass(this.getStatusClass(current)); 133 | }); 134 | } 135 | 136 | getStatusClass(status) { 137 | return this.options.classes.status.replace('{status}', status); 138 | } 139 | 140 | createHtml() { 141 | let output = this.options.templates.main; 142 | 143 | output = output.replace('{containerClass}', this.classes.container); 144 | output = output.replace('{toggle}', this.generateToggle()); 145 | output = output.replace('{meter}', this.generateMeter()); 146 | output = output.replace('{score}', this.generateScore()); 147 | output = output.replace('{input}', `
`); 148 | this.$container = $(output); 149 | 150 | if (this.options.skin) { 151 | this.$container.addClass(this.options.skin); 152 | } 153 | 154 | this.$element.before(this.$container); 155 | const $holder = this.$container.find(`.${this.classes.input}`); 156 | const el = this.$element.detach(); 157 | $holder.before(el); 158 | $holder.remove(); 159 | } 160 | 161 | generateToggle() { 162 | if (this.options.showToggle) { 163 | let output = this.options.templates.toggle; 164 | 165 | output = output.replace('{toggleClass}', this.classes.toggle); 166 | return output; 167 | } 168 | return ''; 169 | } 170 | 171 | generateMeter() { 172 | if (this.options.showMeter) { 173 | let output = this.options.templates.meter; 174 | 175 | output = output.replace('{meterClass}', this.classes.meter); 176 | return output; 177 | } 178 | return ''; 179 | } 180 | 181 | generateScore() { 182 | let output = this.options.templates.score; 183 | 184 | output = output.replace('{scoreClass}', this.classes.score); 185 | return output; 186 | } 187 | 188 | check() { 189 | let score = 0; 190 | let status = null; 191 | 192 | if ($.isFunction(this.options.scoreCallback)) { 193 | score = this.options.scoreCallback.call(this); 194 | 195 | if ($.isFunction(this.options.statusCallback)) { 196 | status = this.options.statusCallback.call(this, score); 197 | } 198 | } else { 199 | const check = new PasswordStrength(); 200 | check.username = this.$username.val() || null; 201 | check.password = this.$input.val(); 202 | 203 | score = check.test(); 204 | status = check.status; 205 | } 206 | 207 | if (this.options.emptyStatus && status !== 'invalid' && this.$input.val() === '') { 208 | status = 'empty'; 209 | } 210 | 211 | this.trigger('check', score, status); 212 | } 213 | 214 | getScore() { 215 | if (!this.score) { 216 | this.check(); 217 | } 218 | return this.score; 219 | } 220 | 221 | getStatus() { 222 | if (!this.status) { 223 | this.check(); 224 | } 225 | return this.status; 226 | } 227 | 228 | toggle() { 229 | let type; 230 | 231 | if(this.$toggle.is(':checkbox')) { 232 | type = this.$toggle.is(":checked")? "text" : "password"; 233 | } else { 234 | type = this.shown === false?"text" : "password"; 235 | } 236 | 237 | this.shown = type === "text"; 238 | 239 | if(this.shown) { 240 | this.$container.addClass(this.classes.shown); 241 | } else { 242 | this.$container.removeClass(this.classes.shown); 243 | } 244 | this.$input.attr('type', type); 245 | 246 | this.trigger('toggle', type); 247 | } 248 | 249 | trigger(eventType, ...params) { 250 | let data = [this].concat(params); 251 | 252 | // event 253 | this.$element.trigger(`${NAMESPACE$1}::${eventType}`, data); 254 | 255 | // callback 256 | eventType = eventType.replace(/\b\w+\b/g, (word) => { 257 | return word.substring(0, 1).toUpperCase() + word.substring(1); 258 | }); 259 | let onFunction = `on${eventType}`; 260 | 261 | if (typeof this.options[onFunction] === 'function') { 262 | this.options[onFunction].apply(this, params); 263 | } 264 | } 265 | 266 | destroy() { 267 | this.$element.data(NAMESPACE$1, null); 268 | this.trigger('destroy'); 269 | } 270 | 271 | static setDefaults(options) { 272 | $.extend(true, DEFAULTS, $.isPlainObject(options) && options); 273 | } 274 | } 275 | 276 | var info = { 277 | version:'0.2.5' 278 | }; 279 | 280 | const NAMESPACE = 'strength'; 281 | const OtherStrength = $.fn.strength; 282 | 283 | const jQueryStrength = function(options, ...args) { 284 | if (typeof options === 'string') { 285 | const method = options; 286 | 287 | if (/^_/.test(method)) { 288 | return false; 289 | } else if ((/^(get)/.test(method))) { 290 | const instance = this.first().data(NAMESPACE); 291 | if (instance && typeof instance[method] === 'function') { 292 | return instance[method](...args); 293 | } 294 | } else { 295 | return this.each(function() { 296 | const instance = $.data(this, NAMESPACE); 297 | if (instance && typeof instance[method] === 'function') { 298 | instance[method](...args); 299 | } 300 | }); 301 | } 302 | } 303 | 304 | return this.each(function() { 305 | if (!$(this).data(NAMESPACE)) { 306 | $(this).data(NAMESPACE, new Strength(this, options)); 307 | } 308 | }); 309 | }; 310 | 311 | $.fn.strength = jQueryStrength; 312 | 313 | $.strength = $.extend({ 314 | setDefaults: Strength.setDefaults, 315 | noConflict: function() { 316 | $.fn.strength = OtherStrength; 317 | return jQueryStrength; 318 | } 319 | }, info); 320 | -------------------------------------------------------------------------------- /dist/jquery-strength.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jQuery strength v0.2.5 3 | * https://github.com/amazingSurge/jquery-strength 4 | * 5 | * Copyright (c) amazingSurge 6 | * Released under the LGPL-3.0 license 7 | */ 8 | (function(global, factory) { 9 | if (typeof define === 'function' && define.amd) { 10 | define(['jquery', 'password_strength'], factory); 11 | } else if (typeof exports !== 'undefined') { 12 | factory(require('jquery'), require('password_strength')); 13 | } else { 14 | var mod = { 15 | exports: {} 16 | }; 17 | factory(global.jQuery, global.PasswordStrength); 18 | global.jqueryStrengthEs = mod.exports; 19 | } 20 | })(this, function(_jquery, _password_strength) { 21 | 'use strict'; 22 | 23 | var _jquery2 = _interopRequireDefault(_jquery); 24 | 25 | var _password_strength2 = _interopRequireDefault(_password_strength); 26 | 27 | function _interopRequireDefault(obj) { 28 | return obj && obj.__esModule 29 | ? obj 30 | : { 31 | default: obj 32 | }; 33 | } 34 | 35 | function _classCallCheck(instance, Constructor) { 36 | if (!(instance instanceof Constructor)) { 37 | throw new TypeError('Cannot call a class as a function'); 38 | } 39 | } 40 | 41 | var _createClass = (function() { 42 | function defineProperties(target, props) { 43 | for (var i = 0; i < props.length; i++) { 44 | var descriptor = props[i]; 45 | descriptor.enumerable = descriptor.enumerable || false; 46 | descriptor.configurable = true; 47 | if ('value' in descriptor) descriptor.writable = true; 48 | Object.defineProperty(target, descriptor.key, descriptor); 49 | } 50 | } 51 | 52 | return function(Constructor, protoProps, staticProps) { 53 | if (protoProps) defineProperties(Constructor.prototype, protoProps); 54 | if (staticProps) defineProperties(Constructor, staticProps); 55 | return Constructor; 56 | }; 57 | })(); 58 | 59 | var DEFAULTS = { 60 | namespace: 'strength', 61 | skin: null, 62 | 63 | showMeter: true, 64 | showToggle: true, 65 | 66 | usernameField: '', 67 | 68 | templates: { 69 | toggle: 70 | '', 71 | meter: '
{score}
', 72 | score: '', 73 | main: 74 | '
{input}{toggle}
{meter}
' 75 | }, 76 | 77 | classes: { 78 | container: 'strength-container', 79 | status: 'strength-{status}', 80 | input: 'strength-input', 81 | toggle: 'strength-toggle', 82 | meter: 'strength-meter', 83 | score: 'strength-score', 84 | shown: 'strength-shown' 85 | }, 86 | 87 | scoreLables: { 88 | empty: 'Empty', 89 | invalid: 'Invalid', 90 | weak: 'Weak', 91 | good: 'Good', 92 | strong: 'Strong' 93 | }, 94 | 95 | scoreClasses: { 96 | empty: '', 97 | invalid: 'label-danger', 98 | weak: 'label-warning', 99 | good: 'label-info', 100 | strong: 'label-success' 101 | }, 102 | 103 | emptyStatus: true, 104 | 105 | scoreCallback: null, 106 | statusCallback: null 107 | }; 108 | 109 | var NAMESPACE$1 = 'strength'; 110 | 111 | /** 112 | * Plugin constructor 113 | **/ 114 | 115 | var Strength = (function() { 116 | function Strength(element) { 117 | var options = 118 | arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 119 | 120 | _classCallCheck(this, Strength); 121 | 122 | this.element = element; 123 | this.$element = (0, _jquery2.default)(element); 124 | 125 | this.options = _jquery2.default.extend( 126 | true, 127 | {}, 128 | DEFAULTS, 129 | options, 130 | this.$element.data() 131 | ); 132 | this.classes = this.options.classes; 133 | 134 | this.$username = (0, _jquery2.default)(this.options.usernameField); 135 | 136 | this.score = 0; 137 | this.status = null; 138 | 139 | this.shown = false; 140 | 141 | this.trigger('init'); 142 | this.init(); 143 | } 144 | 145 | _createClass( 146 | Strength, 147 | [ 148 | { 149 | key: 'init', 150 | value: function init() { 151 | this.createHtml(); 152 | 153 | this.$element.addClass(this.classes.input); 154 | 155 | this.$toggle = this.$container.find('.' + this.classes.toggle); 156 | this.$meter = this.$container.find('.' + this.classes.meter); 157 | 158 | this.$score = this.$container.find('.' + this.classes.score); 159 | this.$input = this.$container.find('.' + this.classes.input); 160 | 161 | this.bindEvents(); 162 | 163 | this.initialized = true; 164 | this.trigger('ready'); 165 | } 166 | }, 167 | { 168 | key: 'bindEvents', 169 | value: function bindEvents() { 170 | var _this = this; 171 | 172 | if (this.$toggle.is(':checkbox')) { 173 | this.$toggle.on('change', function() { 174 | _this.toggle(); 175 | }); 176 | } else { 177 | this.$toggle.on('click', function() { 178 | _this.toggle(); 179 | }); 180 | } 181 | 182 | this.$input.bind('keyup.strength keydown.strength', function() { 183 | _this.check(); 184 | }); 185 | 186 | this.$element.on(NAMESPACE$1 + '::check', function( 187 | e, 188 | api, 189 | score, 190 | status 191 | ) { 192 | _this.$score.html(_this.options.scoreLables[status]); 193 | 194 | if (status !== _this.status) { 195 | var newClass = _this.options.scoreClasses[status]; 196 | var oldClass = _this.options.scoreClasses[_this.status]; 197 | _this.$score.removeClass(oldClass).addClass(newClass); 198 | 199 | _this.trigger('statusChange', status, _this.status); 200 | } 201 | 202 | _this.status = status; 203 | _this.score = score; 204 | }); 205 | 206 | this.$element.on(NAMESPACE$1 + '::statusChange', function( 207 | e, 208 | api, 209 | current, 210 | old 211 | ) { 212 | _this.$container 213 | .removeClass(_this.getStatusClass(old)) 214 | .addClass(_this.getStatusClass(current)); 215 | }); 216 | } 217 | }, 218 | { 219 | key: 'getStatusClass', 220 | value: function getStatusClass(status) { 221 | return this.options.classes.status.replace('{status}', status); 222 | } 223 | }, 224 | { 225 | key: 'createHtml', 226 | value: function createHtml() { 227 | var output = this.options.templates.main; 228 | 229 | output = output.replace('{containerClass}', this.classes.container); 230 | output = output.replace('{toggle}', this.generateToggle()); 231 | output = output.replace('{meter}', this.generateMeter()); 232 | output = output.replace('{score}', this.generateScore()); 233 | output = output.replace( 234 | '{input}', 235 | '
' 236 | ); 237 | this.$container = (0, _jquery2.default)(output); 238 | 239 | if (this.options.skin) { 240 | this.$container.addClass(this.options.skin); 241 | } 242 | 243 | this.$element.before(this.$container); 244 | var $holder = this.$container.find('.' + this.classes.input); 245 | var el = this.$element.detach(); 246 | $holder.before(el); 247 | $holder.remove(); 248 | } 249 | }, 250 | { 251 | key: 'generateToggle', 252 | value: function generateToggle() { 253 | if (this.options.showToggle) { 254 | var output = this.options.templates.toggle; 255 | 256 | output = output.replace('{toggleClass}', this.classes.toggle); 257 | return output; 258 | } 259 | return ''; 260 | } 261 | }, 262 | { 263 | key: 'generateMeter', 264 | value: function generateMeter() { 265 | if (this.options.showMeter) { 266 | var output = this.options.templates.meter; 267 | 268 | output = output.replace('{meterClass}', this.classes.meter); 269 | return output; 270 | } 271 | return ''; 272 | } 273 | }, 274 | { 275 | key: 'generateScore', 276 | value: function generateScore() { 277 | var output = this.options.templates.score; 278 | 279 | output = output.replace('{scoreClass}', this.classes.score); 280 | return output; 281 | } 282 | }, 283 | { 284 | key: 'check', 285 | value: function check() { 286 | var score = 0; 287 | var status = null; 288 | 289 | if (_jquery2.default.isFunction(this.options.scoreCallback)) { 290 | score = this.options.scoreCallback.call(this); 291 | 292 | if (_jquery2.default.isFunction(this.options.statusCallback)) { 293 | status = this.options.statusCallback.call(this, score); 294 | } 295 | } else { 296 | var check = new _password_strength2.default(); 297 | check.username = this.$username.val() || null; 298 | check.password = this.$input.val(); 299 | 300 | score = check.test(); 301 | status = check.status; 302 | } 303 | 304 | if ( 305 | this.options.emptyStatus && 306 | status !== 'invalid' && 307 | this.$input.val() === '' 308 | ) { 309 | status = 'empty'; 310 | } 311 | 312 | this.trigger('check', score, status); 313 | } 314 | }, 315 | { 316 | key: 'getScore', 317 | value: function getScore() { 318 | if (!this.score) { 319 | this.check(); 320 | } 321 | return this.score; 322 | } 323 | }, 324 | { 325 | key: 'getStatus', 326 | value: function getStatus() { 327 | if (!this.status) { 328 | this.check(); 329 | } 330 | return this.status; 331 | } 332 | }, 333 | { 334 | key: 'toggle', 335 | value: function toggle() { 336 | var type = void 0; 337 | 338 | if (this.$toggle.is(':checkbox')) { 339 | type = this.$toggle.is(':checked') ? 'text' : 'password'; 340 | } else { 341 | type = this.shown === false ? 'text' : 'password'; 342 | } 343 | 344 | this.shown = type === 'text'; 345 | 346 | if (this.shown) { 347 | this.$container.addClass(this.classes.shown); 348 | } else { 349 | this.$container.removeClass(this.classes.shown); 350 | } 351 | this.$input.attr('type', type); 352 | 353 | this.trigger('toggle', type); 354 | } 355 | }, 356 | { 357 | key: 'trigger', 358 | value: function trigger(eventType) { 359 | for ( 360 | var _len = arguments.length, 361 | params = Array(_len > 1 ? _len - 1 : 0), 362 | _key = 1; 363 | _key < _len; 364 | _key++ 365 | ) { 366 | params[_key - 1] = arguments[_key]; 367 | } 368 | 369 | var data = [this].concat(params); 370 | 371 | // event 372 | this.$element.trigger(NAMESPACE$1 + '::' + eventType, data); 373 | 374 | // callback 375 | eventType = eventType.replace(/\b\w+\b/g, function(word) { 376 | return word.substring(0, 1).toUpperCase() + word.substring(1); 377 | }); 378 | var onFunction = 'on' + eventType; 379 | 380 | if (typeof this.options[onFunction] === 'function') { 381 | this.options[onFunction].apply(this, params); 382 | } 383 | } 384 | }, 385 | { 386 | key: 'destroy', 387 | value: function destroy() { 388 | this.$element.data(NAMESPACE$1, null); 389 | this.trigger('destroy'); 390 | } 391 | } 392 | ], 393 | [ 394 | { 395 | key: 'setDefaults', 396 | value: function setDefaults(options) { 397 | _jquery2.default.extend( 398 | true, 399 | DEFAULTS, 400 | _jquery2.default.isPlainObject(options) && options 401 | ); 402 | } 403 | } 404 | ] 405 | ); 406 | 407 | return Strength; 408 | })(); 409 | 410 | var info = { 411 | version: '0.2.5' 412 | }; 413 | 414 | var NAMESPACE = 'strength'; 415 | var OtherStrength = _jquery2.default.fn.strength; 416 | 417 | var jQueryStrength = function jQueryStrength(options) { 418 | for ( 419 | var _len2 = arguments.length, 420 | args = Array(_len2 > 1 ? _len2 - 1 : 0), 421 | _key2 = 1; 422 | _key2 < _len2; 423 | _key2++ 424 | ) { 425 | args[_key2 - 1] = arguments[_key2]; 426 | } 427 | 428 | if (typeof options === 'string') { 429 | var method = options; 430 | 431 | if (/^_/.test(method)) { 432 | return false; 433 | } else if (/^(get)/.test(method)) { 434 | var instance = this.first().data(NAMESPACE); 435 | if (instance && typeof instance[method] === 'function') { 436 | return instance[method].apply(instance, args); 437 | } 438 | } else { 439 | return this.each(function() { 440 | var instance = _jquery2.default.data(this, NAMESPACE); 441 | if (instance && typeof instance[method] === 'function') { 442 | instance[method].apply(instance, args); 443 | } 444 | }); 445 | } 446 | } 447 | 448 | return this.each(function() { 449 | if (!(0, _jquery2.default)(this).data(NAMESPACE)) { 450 | (0, _jquery2.default)(this).data( 451 | NAMESPACE, 452 | new Strength(this, options) 453 | ); 454 | } 455 | }); 456 | }; 457 | 458 | _jquery2.default.fn.strength = jQueryStrength; 459 | 460 | _jquery2.default.strength = _jquery2.default.extend( 461 | { 462 | setDefaults: Strength.setDefaults, 463 | noConflict: function noConflict() { 464 | _jquery2.default.fn.strength = OtherStrength; 465 | return jQueryStrength; 466 | } 467 | }, 468 | info 469 | ); 470 | }); 471 | -------------------------------------------------------------------------------- /dist/jquery-strength.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jQuery strength v0.2.5 3 | * https://github.com/amazingSurge/jquery-strength 4 | * 5 | * Copyright (c) amazingSurge 6 | * Released under the LGPL-3.0 license 7 | */ 8 | !function(t,e){if("function"==typeof define&&define.amd)define(["jquery","password_strength"],e);else if("undefined"!=typeof exports)e(require("jquery"),require("password_strength"));else{var s={exports:{}};e(t.jQuery,t.PasswordStrength),t.jqueryStrengthEs=s.exports}}(this,function(t,e){"use strict";function s(t){return t&&t.__esModule?t:{default:t}}function n(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}var i=s(t),a=s(e),r=function(){function t(t,e){for(var s=0;s',meter:'
{score}
',score:'',main:'
{input}{toggle}
{meter}
'},classes:{container:"strength-container",status:"strength-{status}",input:"strength-input",toggle:"strength-toggle",meter:"strength-meter",score:"strength-score",shown:"strength-shown"},scoreLables:{empty:"Empty",invalid:"Invalid",weak:"Weak",good:"Good",strong:"Strong"},scoreClasses:{empty:"",invalid:"label-danger",weak:"label-warning",good:"label-info",strong:"label-success"},emptyStatus:!0,scoreCallback:null,statusCallback:null},l=function(){function t(e){var s=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};n(this,t),this.element=e,this.$element=(0,i.default)(e),this.options=i.default.extend(!0,{},o,s,this.$element.data()),this.classes=this.options.classes,this.$username=(0,i.default)(this.options.usernameField),this.score=0,this.status=null,this.shown=!1,this.trigger("init"),this.init()}return r(t,[{key:"init",value:function(){this.createHtml(),this.$element.addClass(this.classes.input),this.$toggle=this.$container.find("."+this.classes.toggle),this.$meter=this.$container.find("."+this.classes.meter),this.$score=this.$container.find("."+this.classes.score),this.$input=this.$container.find("."+this.classes.input),this.bindEvents(),this.initialized=!0,this.trigger("ready")}},{key:"bindEvents",value:function(){var t=this;this.$toggle.is(":checkbox")?this.$toggle.on("change",function(){t.toggle()}):this.$toggle.on("click",function(){t.toggle()}),this.$input.bind("keyup.strength keydown.strength",function(){t.check()}),this.$element.on("strength::check",function(e,s,n,i){if(t.$score.html(t.options.scoreLables[i]),i!==t.status){var a=t.options.scoreClasses[i],r=t.options.scoreClasses[t.status];t.$score.removeClass(r).addClass(a),t.trigger("statusChange",i,t.status)}t.status=i,t.score=n}),this.$element.on("strength::statusChange",function(e,s,n,i){t.$container.removeClass(t.getStatusClass(i)).addClass(t.getStatusClass(n))})}},{key:"getStatusClass",value:function(t){return this.options.classes.status.replace("{status}",t)}},{key:"createHtml",value:function(){var t=this.options.templates.main;t=(t=(t=(t=(t=t.replace("{containerClass}",this.classes.container)).replace("{toggle}",this.generateToggle())).replace("{meter}",this.generateMeter())).replace("{score}",this.generateScore())).replace("{input}",'
'),this.$container=(0,i.default)(t),this.options.skin&&this.$container.addClass(this.options.skin),this.$element.before(this.$container);var e=this.$container.find("."+this.classes.input),s=this.$element.detach();e.before(s),e.remove()}},{key:"generateToggle",value:function(){if(this.options.showToggle){var t=this.options.templates.toggle;return t=t.replace("{toggleClass}",this.classes.toggle)}return""}},{key:"generateMeter",value:function(){if(this.options.showMeter){var t=this.options.templates.meter;return t=t.replace("{meterClass}",this.classes.meter)}return""}},{key:"generateScore",value:function(){var t=this.options.templates.score;return t=t.replace("{scoreClass}",this.classes.score)}},{key:"check",value:function(){var t=0,e=null;if(i.default.isFunction(this.options.scoreCallback))t=this.options.scoreCallback.call(this),i.default.isFunction(this.options.statusCallback)&&(e=this.options.statusCallback.call(this,t));else{var s=new a.default;s.username=this.$username.val()||null,s.password=this.$input.val(),t=s.test(),e=s.status}this.options.emptyStatus&&"invalid"!==e&&""===this.$input.val()&&(e="empty"),this.trigger("check",t,e)}},{key:"getScore",value:function(){return this.score||this.check(),this.score}},{key:"getStatus",value:function(){return this.status||this.check(),this.status}},{key:"toggle",value:function(){var t=void 0;t=this.$toggle.is(":checkbox")?this.$toggle.is(":checked")?"text":"password":!1===this.shown?"text":"password",this.shown="text"===t,this.shown?this.$container.addClass(this.classes.shown):this.$container.removeClass(this.classes.shown),this.$input.attr("type",t),this.trigger("toggle",t)}},{key:"trigger",value:function(t){for(var e=arguments.length,s=Array(e>1?e-1:0),n=1;n1?e-1:0),n=1;n',\n meter: '
{score}
',\n score: '',\n main: '
{input}{toggle}
{meter}
'\n },\n\n classes: {\n container: 'strength-container',\n status: 'strength-{status}',\n input: 'strength-input',\n toggle: 'strength-toggle',\n meter: 'strength-meter',\n score: 'strength-score',\n shown: 'strength-shown'\n },\n\n scoreLables: {\n empty: 'Empty',\n invalid: 'Invalid',\n weak: 'Weak',\n good: 'Good',\n strong: 'Strong'\n },\n\n scoreClasses: {\n empty: '',\n invalid: 'label-danger',\n weak: 'label-warning',\n good: 'label-info',\n strong: 'label-success'\n },\n\n emptyStatus: true,\n\n scoreCallback: null,\n statusCallback: null\n};\n\nconst NAMESPACE$1 = 'strength';\n\n/**\n * Plugin constructor\n **/\nclass Strength {\n constructor(element, options = {}) {\n this.element = element;\n this.$element = $(element);\n\n this.options = $.extend(true, {}, DEFAULTS, options, this.$element.data());\n this.classes = this.options.classes;\n\n this.$username = $(this.options.usernameField);\n\n this.score = 0;\n this.status = null;\n\n this.shown = false;\n\n this.trigger('init');\n this.init();\n }\n\n init() {\n this.createHtml();\n\n this.$element.addClass(this.classes.input);\n\n this.$toggle = this.$container.find(`.${this.classes.toggle}`);\n this.$meter = this.$container.find(`.${this.classes.meter}`);\n\n this.$score = this.$container.find(`.${this.classes.score}`);\n this.$input = this.$container.find(`.${this.classes.input}`);\n\n this.bindEvents();\n\n this.initialized = true;\n this.trigger('ready');\n }\n\n bindEvents() {\n if(this.$toggle.is(':checkbox')){\n this.$toggle.on('change', () => {\n this.toggle();\n });\n } else {\n this.$toggle.on('click', () => {\n this.toggle();\n });\n }\n\n\n this.$input.bind('keyup.strength keydown.strength', () => {\n this.check();\n });\n\n this.$element.on(`${NAMESPACE$1}::check`, (e, api, score, status) => {\n this.$score.html(this.options.scoreLables[status]);\n\n if (status !== this.status) {\n const newClass = this.options.scoreClasses[status];\n const oldClass = this.options.scoreClasses[this.status];\n this.$score.removeClass(oldClass).addClass(newClass);\n\n this.trigger('statusChange', status, this.status);\n }\n\n this.status = status;\n this.score = score;\n });\n\n this.$element.on(`${NAMESPACE$1}::statusChange`, (e, api, current, old) => {\n this.$container.removeClass(this.getStatusClass(old)).addClass(this.getStatusClass(current));\n });\n }\n\n getStatusClass(status) {\n return this.options.classes.status.replace('{status}', status);\n }\n\n createHtml() {\n let output = this.options.templates.main;\n\n output = output.replace('{containerClass}', this.classes.container);\n output = output.replace('{toggle}', this.generateToggle());\n output = output.replace('{meter}', this.generateMeter());\n output = output.replace('{score}', this.generateScore());\n output = output.replace('{input}', `
`);\n this.$container = $(output);\n\n if (this.options.skin) {\n this.$container.addClass(this.options.skin);\n }\n\n this.$element.before(this.$container);\n const $holder = this.$container.find(`.${this.classes.input}`);\n const el = this.$element.detach();\n $holder.before(el);\n $holder.remove();\n }\n\n generateToggle() {\n if (this.options.showToggle) {\n let output = this.options.templates.toggle;\n\n output = output.replace('{toggleClass}', this.classes.toggle);\n return output;\n }\n return '';\n }\n\n generateMeter() {\n if (this.options.showMeter) {\n let output = this.options.templates.meter;\n\n output = output.replace('{meterClass}', this.classes.meter);\n return output;\n }\n return '';\n }\n\n generateScore() {\n let output = this.options.templates.score;\n\n output = output.replace('{scoreClass}', this.classes.score);\n return output;\n }\n\n check() {\n let score = 0;\n let status = null;\n\n if ($.isFunction(this.options.scoreCallback)) {\n score = this.options.scoreCallback.call(this);\n\n if ($.isFunction(this.options.statusCallback)) {\n status = this.options.statusCallback.call(this, score);\n }\n } else {\n const check = new PasswordStrength();\n check.username = this.$username.val() || null;\n check.password = this.$input.val();\n\n score = check.test();\n status = check.status;\n }\n\n if (this.options.emptyStatus && status !== 'invalid' && this.$input.val() === '') {\n status = 'empty';\n }\n\n this.trigger('check', score, status);\n }\n\n getScore() {\n if (!this.score) {\n this.check();\n }\n return this.score;\n }\n\n getStatus() {\n if (!this.status) {\n this.check();\n }\n return this.status;\n }\n\n toggle() {\n let type;\n\n if(this.$toggle.is(':checkbox')) {\n type = this.$toggle.is(\":checked\")? \"text\" : \"password\";\n } else {\n type = this.shown === false?\"text\" : \"password\";\n }\n\n this.shown = type === \"text\";\n\n if(this.shown) {\n this.$container.addClass(this.classes.shown);\n } else {\n this.$container.removeClass(this.classes.shown);\n }\n this.$input.attr('type', type);\n\n this.trigger('toggle', type);\n }\n\n trigger(eventType, ...params) {\n let data = [this].concat(params);\n\n // event\n this.$element.trigger(`${NAMESPACE$1}::${eventType}`, data);\n\n // callback\n eventType = eventType.replace(/\\b\\w+\\b/g, (word) => {\n return word.substring(0, 1).toUpperCase() + word.substring(1);\n });\n let onFunction = `on${eventType}`;\n\n if (typeof this.options[onFunction] === 'function') {\n this.options[onFunction].apply(this, params);\n }\n }\n\n destroy() {\n this.$element.data(NAMESPACE$1, null);\n this.trigger('destroy');\n }\n\n static setDefaults(options) {\n $.extend(true, DEFAULTS, $.isPlainObject(options) && options);\n }\n}\n\nvar info = {\n version:'0.2.5'\n};\n\nconst NAMESPACE = 'strength';\nconst OtherStrength = $.fn.strength;\n\nconst jQueryStrength = function(options, ...args) {\n if (typeof options === 'string') {\n const method = options;\n\n if (/^_/.test(method)) {\n return false;\n } else if ((/^(get)/.test(method))) {\n const instance = this.first().data(NAMESPACE);\n if (instance && typeof instance[method] === 'function') {\n return instance[method](...args);\n }\n } else {\n return this.each(function() {\n const instance = $.data(this, NAMESPACE);\n if (instance && typeof instance[method] === 'function') {\n instance[method](...args);\n }\n });\n }\n }\n\n return this.each(function() {\n if (!$(this).data(NAMESPACE)) {\n $(this).data(NAMESPACE, new Strength(this, options));\n }\n });\n};\n\n$.fn.strength = jQueryStrength;\n\n$.strength = $.extend({\n setDefaults: Strength.setDefaults,\n noConflict: function() {\n $.fn.strength = OtherStrength;\n return jQueryStrength;\n }\n}, info);\n"]} -------------------------------------------------------------------------------- /examples/css/main.css: -------------------------------------------------------------------------------- 1 | .container { 2 | width: 960px; 3 | max-width: 90%; 4 | min-width: 320px; 5 | 6 | margin: 0 auto; 7 | } -------------------------------------------------------------------------------- /examples/css/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v4.1.1 | MIT License | github.com/necolas/normalize.css */ 2 | 3 | /** 4 | * 1. Change the default font family in all browsers (opinionated). 5 | * 2. Correct the line height in all browsers. 6 | * 3. Prevent adjustments of font size after orientation changes in IE and iOS. 7 | */ 8 | 9 | html { 10 | font-family: sans-serif; /* 1 */ 11 | line-height: 1.15; /* 2 */ 12 | -ms-text-size-adjust: 100%; /* 3 */ 13 | -webkit-text-size-adjust: 100%; /* 3 */ 14 | } 15 | 16 | /** 17 | * Remove the margin in all browsers (opinionated). 18 | */ 19 | 20 | body { 21 | margin: 0; 22 | } 23 | 24 | /* HTML5 display definitions 25 | ========================================================================== */ 26 | 27 | /** 28 | * Add the correct display in IE 9-. 29 | * 1. Add the correct display in Edge, IE, and Firefox. 30 | * 2. Add the correct display in IE. 31 | */ 32 | 33 | article, 34 | aside, 35 | details, /* 1 */ 36 | figcaption, 37 | figure, 38 | footer, 39 | header, 40 | main, /* 2 */ 41 | menu, 42 | nav, 43 | section, 44 | summary { /* 1 */ 45 | display: block; 46 | } 47 | 48 | /** 49 | * Add the correct display in IE 9-. 50 | */ 51 | 52 | audio, 53 | canvas, 54 | progress, 55 | video { 56 | display: inline-block; 57 | } 58 | 59 | /** 60 | * Add the correct display in iOS 4-7. 61 | */ 62 | 63 | audio:not([controls]) { 64 | display: none; 65 | height: 0; 66 | } 67 | 68 | /** 69 | * Add the correct vertical alignment in Chrome, Firefox, and Opera. 70 | */ 71 | 72 | progress { 73 | vertical-align: baseline; 74 | } 75 | 76 | /** 77 | * Add the correct display in IE 10-. 78 | * 1. Add the correct display in IE. 79 | */ 80 | 81 | template, /* 1 */ 82 | [hidden] { 83 | display: none; 84 | } 85 | 86 | /* Links 87 | ========================================================================== */ 88 | 89 | /** 90 | * 1. Remove the gray background on active links in IE 10. 91 | * 2. Remove gaps in links underline in iOS 8+ and Safari 8+. 92 | */ 93 | 94 | a { 95 | background-color: transparent; /* 1 */ 96 | -webkit-text-decoration-skip: objects; /* 2 */ 97 | } 98 | 99 | /** 100 | * Remove the outline on focused links when they are also active or hovered 101 | * in all browsers (opinionated). 102 | */ 103 | 104 | a:active, 105 | a:hover { 106 | outline-width: 0; 107 | } 108 | 109 | /* Text-level semantics 110 | ========================================================================== */ 111 | 112 | /** 113 | * 1. Remove the bottom border in Firefox 39-. 114 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. 115 | */ 116 | 117 | abbr[title] { 118 | border-bottom: none; /* 1 */ 119 | text-decoration: underline; /* 2 */ 120 | text-decoration: underline dotted; /* 2 */ 121 | } 122 | 123 | /** 124 | * Prevent the duplicate application of `bolder` by the next rule in Safari 6. 125 | */ 126 | 127 | b, 128 | strong { 129 | font-weight: inherit; 130 | } 131 | 132 | /** 133 | * Add the correct font weight in Chrome, Edge, and Safari. 134 | */ 135 | 136 | b, 137 | strong { 138 | font-weight: bolder; 139 | } 140 | 141 | /** 142 | * Add the correct font style in Android 4.3-. 143 | */ 144 | 145 | dfn { 146 | font-style: italic; 147 | } 148 | 149 | /** 150 | * Correct the font size and margin on `h1` elements within `section` and 151 | * `article` contexts in Chrome, Firefox, and Safari. 152 | */ 153 | 154 | h1 { 155 | font-size: 2em; 156 | margin: 0.67em 0; 157 | } 158 | 159 | /** 160 | * Add the correct background and color in IE 9-. 161 | */ 162 | 163 | mark { 164 | background-color: #ff0; 165 | color: #000; 166 | } 167 | 168 | /** 169 | * Add the correct font size in all browsers. 170 | */ 171 | 172 | small { 173 | font-size: 80%; 174 | } 175 | 176 | /** 177 | * Prevent `sub` and `sup` elements from affecting the line height in 178 | * all browsers. 179 | */ 180 | 181 | sub, 182 | sup { 183 | font-size: 75%; 184 | line-height: 0; 185 | position: relative; 186 | vertical-align: baseline; 187 | } 188 | 189 | sub { 190 | bottom: -0.25em; 191 | } 192 | 193 | sup { 194 | top: -0.5em; 195 | } 196 | 197 | /* Embedded content 198 | ========================================================================== */ 199 | 200 | /** 201 | * Remove the border on images inside links in IE 10-. 202 | */ 203 | 204 | img { 205 | border-style: none; 206 | } 207 | 208 | /** 209 | * Hide the overflow in IE. 210 | */ 211 | 212 | svg:not(:root) { 213 | overflow: hidden; 214 | } 215 | 216 | /* Grouping content 217 | ========================================================================== */ 218 | 219 | /** 220 | * 1. Correct the inheritance and scaling of font size in all browsers. 221 | * 2. Correct the odd `em` font sizing in all browsers. 222 | */ 223 | 224 | code, 225 | kbd, 226 | pre, 227 | samp { 228 | font-family: monospace, monospace; /* 1 */ 229 | font-size: 1em; /* 2 */ 230 | } 231 | 232 | /** 233 | * Add the correct margin in IE 8. 234 | */ 235 | 236 | figure { 237 | margin: 1em 40px; 238 | } 239 | 240 | /** 241 | * 1. Add the correct box sizing in Firefox. 242 | * 2. Show the overflow in Edge and IE. 243 | */ 244 | 245 | hr { 246 | box-sizing: content-box; /* 1 */ 247 | height: 0; /* 1 */ 248 | overflow: visible; /* 2 */ 249 | } 250 | 251 | /* Forms 252 | ========================================================================== */ 253 | 254 | /** 255 | * 1. Change font properties to `inherit` in all browsers (opinionated). 256 | * 2. Remove the margin in Firefox and Safari. 257 | */ 258 | 259 | button, 260 | input, 261 | optgroup, 262 | select, 263 | textarea { 264 | font: inherit; /* 1 */ 265 | margin: 0; /* 2 */ 266 | } 267 | 268 | /** 269 | * Restore the font weight unset by the previous rule. 270 | */ 271 | 272 | optgroup { 273 | font-weight: bold; 274 | } 275 | 276 | /** 277 | * Show the overflow in IE. 278 | * 1. Show the overflow in Edge. 279 | */ 280 | 281 | button, 282 | input { /* 1 */ 283 | overflow: visible; 284 | } 285 | 286 | /** 287 | * Remove the inheritance of text transform in Edge, Firefox, and IE. 288 | * 1. Remove the inheritance of text transform in Firefox. 289 | */ 290 | 291 | button, 292 | select { /* 1 */ 293 | text-transform: none; 294 | } 295 | 296 | /** 297 | * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video` 298 | * controls in Android 4. 299 | * 2. Correct the inability to style clickable types in iOS and Safari. 300 | */ 301 | 302 | button, 303 | html [type="button"], /* 1 */ 304 | [type="reset"], 305 | [type="submit"] { 306 | -webkit-appearance: button; /* 2 */ 307 | } 308 | 309 | /** 310 | * Remove the inner border and padding in Firefox. 311 | */ 312 | 313 | button::-moz-focus-inner, 314 | [type="button"]::-moz-focus-inner, 315 | [type="reset"]::-moz-focus-inner, 316 | [type="submit"]::-moz-focus-inner { 317 | border-style: none; 318 | padding: 0; 319 | } 320 | 321 | /** 322 | * Restore the focus styles unset by the previous rule. 323 | */ 324 | 325 | button:-moz-focusring, 326 | [type="button"]:-moz-focusring, 327 | [type="reset"]:-moz-focusring, 328 | [type="submit"]:-moz-focusring { 329 | outline: 1px dotted ButtonText; 330 | } 331 | 332 | /** 333 | * Change the border, margin, and padding in all browsers (opinionated). 334 | */ 335 | 336 | fieldset { 337 | border: 1px solid #c0c0c0; 338 | margin: 0 2px; 339 | padding: 0.35em 0.625em 0.75em; 340 | } 341 | 342 | /** 343 | * 1. Correct the text wrapping in Edge and IE. 344 | * 2. Correct the color inheritance from `fieldset` elements in IE. 345 | * 3. Remove the padding so developers are not caught out when they zero out 346 | * `fieldset` elements in all browsers. 347 | */ 348 | 349 | legend { 350 | box-sizing: border-box; /* 1 */ 351 | color: inherit; /* 2 */ 352 | display: table; /* 1 */ 353 | max-width: 100%; /* 1 */ 354 | padding: 0; /* 3 */ 355 | white-space: normal; /* 1 */ 356 | } 357 | 358 | /** 359 | * Remove the default vertical scrollbar in IE. 360 | */ 361 | 362 | textarea { 363 | overflow: auto; 364 | } 365 | 366 | /** 367 | * 1. Add the correct box sizing in IE 10-. 368 | * 2. Remove the padding in IE 10-. 369 | */ 370 | 371 | [type="checkbox"], 372 | [type="radio"] { 373 | box-sizing: border-box; /* 1 */ 374 | padding: 0; /* 2 */ 375 | } 376 | 377 | /** 378 | * Correct the cursor style of increment and decrement buttons in Chrome. 379 | */ 380 | 381 | [type="number"]::-webkit-inner-spin-button, 382 | [type="number"]::-webkit-outer-spin-button { 383 | height: auto; 384 | } 385 | 386 | /** 387 | * 1. Correct the odd appearance in Chrome and Safari. 388 | * 2. Correct the outline style in Safari. 389 | */ 390 | 391 | [type="search"] { 392 | -webkit-appearance: textfield; /* 1 */ 393 | outline-offset: -2px; /* 2 */ 394 | } 395 | 396 | /** 397 | * Remove the inner padding and cancel buttons in Chrome and Safari on OS X. 398 | */ 399 | 400 | [type="search"]::-webkit-search-cancel-button, 401 | [type="search"]::-webkit-search-decoration { 402 | -webkit-appearance: none; 403 | } 404 | 405 | /** 406 | * Correct the text style of placeholders in Chrome, Edge, and Safari. 407 | */ 408 | 409 | ::-webkit-input-placeholder { 410 | color: inherit; 411 | opacity: 0.54; 412 | } 413 | 414 | /** 415 | * 1. Correct the inability to style clickable types in iOS and Safari. 416 | * 2. Change font properties to `inherit` in Safari. 417 | */ 418 | 419 | ::-webkit-file-upload-button { 420 | -webkit-appearance: button; /* 1 */ 421 | font: inherit; /* 2 */ 422 | } 423 | -------------------------------------------------------------------------------- /examples/images/checkbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecreation/jquery-strength/4a8098ca2b9385496c6740a136776f5db291c607/examples/images/checkbox.png -------------------------------------------------------------------------------- /examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | jquery-strength 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 |
18 |
19 |
20 | 21 |
22 |
23 |
24 |
25 | 26 |
27 |
28 |
29 | 30 | 31 | 32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /examples/js/password_strength.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | var MULTIPLE_NUMBERS_RE = /\d.*?\d.*?\d/; 3 | var MULTIPLE_SYMBOLS_RE = /[!@#$%^&*?_~].*?[!@#$%^&*?_~]/; 4 | var UPPERCASE_LOWERCASE_RE = /([a-z].*[A-Z])|([A-Z].*[a-z])/; 5 | var SYMBOL_RE = /[!@#\$%^&*?_~]/; 6 | 7 | function escapeForRegexp(string) { 8 | return (string || "").replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); 9 | } 10 | 11 | function PasswordStrength() { 12 | this.username = null; 13 | this.password = null; 14 | this.score = 0; 15 | this.status = null; 16 | } 17 | 18 | PasswordStrength.fn = PasswordStrength.prototype; 19 | 20 | PasswordStrength.fn.test = function() { 21 | var score; 22 | this.score = score = 0; 23 | 24 | if (this.containInvalidMatches()) { 25 | this.status = "invalid"; 26 | } else if (this.usesCommonWord()) { 27 | this.status = "invalid"; 28 | } else if (this.containInvalidRepetition()) { 29 | this.status = "invalid"; 30 | } else { 31 | score += this.scoreFor("password_size"); 32 | score += this.scoreFor("numbers"); 33 | score += this.scoreFor("symbols"); 34 | score += this.scoreFor("uppercase_lowercase"); 35 | score += this.scoreFor("numbers_chars"); 36 | score += this.scoreFor("numbers_symbols"); 37 | score += this.scoreFor("symbols_chars"); 38 | score += this.scoreFor("only_chars"); 39 | score += this.scoreFor("only_numbers"); 40 | score += this.scoreFor("username"); 41 | score += this.scoreFor("sequences"); 42 | score += this.scoreFor("repetitions"); 43 | 44 | if (score < 0) { 45 | score = 0; 46 | } 47 | 48 | if (score > 100) { 49 | score = 100; 50 | } 51 | 52 | if (score < 35) { 53 | this.status = "weak"; 54 | } 55 | 56 | if (score >= 35 && score < 70) { 57 | this.status = "good"; 58 | } 59 | 60 | if (score >= 70) { 61 | this.status = "strong"; 62 | } 63 | } 64 | 65 | this.score = score; 66 | return this.score; 67 | }; 68 | 69 | PasswordStrength.fn.scoreFor = function(name) { 70 | var score = 0; 71 | 72 | switch (name) { 73 | case "password_size": 74 | if (this.password.length < 6) { 75 | score = -100; 76 | } else { 77 | score = this.password.length * 4; 78 | } 79 | break; 80 | 81 | case "numbers": 82 | if (this.password.match(MULTIPLE_NUMBERS_RE)) { 83 | score = 5; 84 | } 85 | break; 86 | 87 | case "symbols": 88 | if (this.password.match(MULTIPLE_SYMBOLS_RE)) { 89 | score = 5; 90 | } 91 | break; 92 | 93 | case "uppercase_lowercase": 94 | if (this.password.match(UPPERCASE_LOWERCASE_RE)) { 95 | score = 10; 96 | } 97 | break; 98 | 99 | case "numbers_chars": 100 | if (this.password.match(/[a-z]/i) && this.password.match(/[0-9]/)) { 101 | score = 15; 102 | } 103 | break; 104 | 105 | case "numbers_symbols": 106 | if (this.password.match(/[0-9]/) && this.password.match(SYMBOL_RE)) { 107 | score = 15; 108 | } 109 | break; 110 | 111 | case "symbols_chars": 112 | if (this.password.match(/[a-z]/i) && this.password.match(SYMBOL_RE)) { 113 | score = 15; 114 | } 115 | break; 116 | 117 | case "only_chars": 118 | if (this.password.match(/^[a-z]+$/i)) { 119 | score = -15; 120 | } 121 | break; 122 | 123 | case "only_numbers": 124 | if (this.password.match(/^\d+$/i)) { 125 | score = -15; 126 | } 127 | break; 128 | 129 | case "username": 130 | if (this.password == this.username) { 131 | score = -100; 132 | } else if (this.password.indexOf(this.username) != -1) { 133 | score = -15; 134 | } 135 | break; 136 | 137 | case "sequences": 138 | score += -15 * this.sequences(this.password); 139 | score += -15 * this.sequences(this.reversed(this.password)); 140 | break; 141 | 142 | case "repetitions": 143 | score += -(this.repetitions(this.password, 2) * 4); 144 | score += -(this.repetitions(this.password, 3) * 3); 145 | score += -(this.repetitions(this.password, 4) * 2); 146 | break; 147 | }; 148 | 149 | return score; 150 | }; 151 | 152 | PasswordStrength.fn.isGood = function() { 153 | return this.status == "good"; 154 | }; 155 | 156 | PasswordStrength.fn.isWeak = function() { 157 | return this.status == "weak"; 158 | }; 159 | 160 | PasswordStrength.fn.isStrong = function() { 161 | return this.status == "strong"; 162 | }; 163 | 164 | PasswordStrength.fn.isInvalid = function() { 165 | return this.status == "invalid"; 166 | }; 167 | 168 | PasswordStrength.fn.isValid = function(level) { 169 | if(level == "strong") { 170 | return this.isStrong(); 171 | } else if (level == "good") { 172 | return this.isStrong() || this.isGood(); 173 | } else { 174 | return !this.containInvalidMatches() && !this.usesCommonWord(); 175 | } 176 | }; 177 | 178 | PasswordStrength.fn.containInvalidMatches = function() { 179 | if (!this.exclude) { 180 | return false; 181 | } 182 | 183 | if (!this.exclude.test) { 184 | return false; 185 | } 186 | 187 | return this.exclude.test(this.password.toString()); 188 | }; 189 | 190 | PasswordStrength.fn.containInvalidRepetition = function() { 191 | var char = this.password[0]; 192 | 193 | if (!char) { 194 | return; 195 | } 196 | 197 | var regex = new RegExp("^" + escapeForRegexp(char) + "+$", "i"); 198 | 199 | return regex.test(this.password); 200 | }; 201 | 202 | PasswordStrength.fn.usesCommonWord = function() { 203 | return PasswordStrength.commonWords.indexOf(this.password.toLowerCase()) >= 0; 204 | }; 205 | 206 | PasswordStrength.fn.sequences = function(text) { 207 | var matches = 0; 208 | var sequenceSize = 0; 209 | var codes = []; 210 | var len = text.length; 211 | var previousCode, currentCode; 212 | 213 | for (var i = 0; i < len; i++) { 214 | currentCode = text.charCodeAt(i); 215 | previousCode = codes[codes.length - 1]; 216 | codes.push(currentCode); 217 | 218 | if (previousCode) { 219 | if (currentCode == previousCode + 1 || previousCode == currentCode) { 220 | sequenceSize += 1; 221 | } else { 222 | sequenceSize = 0; 223 | } 224 | } 225 | 226 | if (sequenceSize == 2) { 227 | matches += 1; 228 | } 229 | } 230 | 231 | return matches; 232 | }; 233 | 234 | PasswordStrength.fn.repetitions = function(text, size) { 235 | var count = 0; 236 | var matches = {}; 237 | var len = text.length; 238 | var substring; 239 | var occurrences; 240 | var tmpText; 241 | 242 | for (var i = 0; i < len; i++) { 243 | substring = text.substr(i, size); 244 | occurrences = 0; 245 | tmpText = text; 246 | 247 | if (matches[substring] || substring.length < size) { 248 | continue; 249 | } 250 | 251 | matches[substring] = true; 252 | 253 | while ((i = tmpText.indexOf(substring)) != -1) { 254 | occurrences += 1; 255 | tmpText = tmpText.substr(i + 1); 256 | }; 257 | 258 | if (occurrences > 1) { 259 | count += 1; 260 | } 261 | } 262 | 263 | return count; 264 | }; 265 | 266 | PasswordStrength.fn.reversed = function(text) { 267 | var newText = ""; 268 | var len = text.length; 269 | 270 | for (var i = len -1; i >= 0; i--) { 271 | newText += text.charAt(i); 272 | } 273 | 274 | return newText; 275 | }; 276 | 277 | PasswordStrength.test = function(username, password) { 278 | var strength = new PasswordStrength(); 279 | strength.username = username; 280 | strength.password = password; 281 | strength.test(); 282 | return strength; 283 | }; 284 | 285 | PasswordStrength.commonWords = ["!qaz1qaz", "!qaz2wsx", "!qazxsw2", "!qazzaq1", "#edc4rfv", "000000", "010203", "1111", "11111", "111111", "11111111", "112233", "1212", "121212", "123123", "1234", "12345", "123456", "1234567", "12345678", "123456789", "1234567890", "123qweasd", "12qw!@qw", "1313", "131313", "1941.salembbb.41", "1qaz!qaz", "1qaz2wsx", "1qaz@wsx", "1qazxsw@", "1qazzaq!", "2000", "2112", "2222", "232323", "2wsx@wsx", "3333", "3edc#edc", "4128", "4321", "4444", "5150", "5555", "55555", "555555", "654321", "6666", "666666", "6969", "696969", "7777", "777777", "7777777", "8675309", "987654", "987654321", "@wsx2wsx", "aaaa", "aaaaaa", "aaliyah1", "abc123", "abc123abc", "abcabc123", "abcd1234", "abcdef", "abgrtyu", "abigail1", "access", "access14", "action", "addison1", "admin", "adobe123", "affair", "airforce1", "alabama1", "albert", "alex", "alexander1", "alexandra1", "alexis", "allison1", "amanda", "amateur", "america1", "anderson1", "andrea", "andrew", "angel", "angel101", "angel123", "angela", "angelina1", "angels", "animal", "annabelle1", "anthony", "anthony1", "anthony11", "antonio1", "apollo", "apple", "apples", "arianna1", "arsenal", "arsenal1", "arsenal12", "arsenal123", "arthur", "asdf", "asdfasdf", "asdfg", "asdfgh", "asdfghjkl", "ashley", "ashley12", "asshole", "asshole1", "atlanta1", "august", "august08", "august10", "august12", "august20", "august22", "austin", "austin02", "austin316", "australia1", "awesome1", "azerty", "baby", "babyboy1", "babygirl1", "babygurl1", "badboy", "bailey", "bailey12", "banana", "barcelona1", "barney", "baseball", "baseball1", "batista1", "batman", "beach", "bear", "beautiful1", "beaver", "beavis", "beckham7", "beer", "bella123", "benjamin1", "bentley1", "bethany1", "bigcock", "bigdaddy", "bigdaddy1", "bigdick", "bigdog", "bigtits", "bill", "billy", "birdie", "bitch", "bitches", "biteme", "black", "blazer", "blessed1", "blink-182", "blink182", "blonde", "blondes", "blondie1", "blowjob", "blowme", "blue", "bond007", "bonnie", "booboo", "boobs", "booger", "boomer", "booty", "boricua1", "boston", "bradley1", "brandon", "brandon1", "brandon2", "brandon7", "brandy", "braves", "braxton1", "brayden1", "brazil", "breanna1", "brian", "brianna1", "brittany1", "brittney1", "bronco", "broncos", "broncos1", "brooklyn1", "brownie1", "bubba", "bubbles1", "buddy", "buddy123", "bulldog", "buster", "butter", "buttercup1", "butterfly1", "butterfly7", "butthead", "buttons1", "calvin", "camaro", "cameron", "cameron1", "canada", "candy123", "captain", "carlos", "carolina1", "carter", "casper", "cassandra1", "catherine1", "celtic1888", "chargers1", "charles", "charles1", "charlie", "charlie1", "charlotte1", "charmed1", "cheese", "chelsea", "chelsea1", "chelsea123", "chester", "chester1", "chevy", "cheyenne1", "chicago", "chicago1", "chicken", "chicken1", "chocolate1", "chopper1", "chris", "chris123", "christian1", "christina1", "christine1", "christmas1", "classof08", "clayton1", "cocacola", "cock", "coffee", "college", "college1", "colombia1", "colorado1", "compaq", "computer", "computer1", "cookie", "cool", "cooper", "corvette", "courtney1", "cowboy", "cowboys", "cowboys1", "cream", "cricket1", "crystal", "crystal1", "cumming", "cumshot", "cunt", "cutiepie1", "daisy123", "dakota", "dallas", "dallas22", "dan1elle", "daniel", "daniela1", "danielle", "danielle1", "dave", "david", "david123", "death666", "debbie", "december1", "december21", "DEFAULT", "dennis", "derrick1", "destiny1", "deuseamor", "devil666", "diablo", "diamond", "diamond1", "diamonds1", "dick", "dirty", "doctor", "doggie", "dolphin", "dolphin1", "dolphins", "dolphins1", "dominic1", "donald", "douglas1", "dragon", "dreams", "driver", "eagle", "eagle1", "eagles", "edward", "einstein", "elizabeth1", "elizabeth2", "england1", "enjoy", "enter", "eric", "erotic", "extreme", "falcon", "falcons1", "falcons7", "familia", "fender", "ferrari", "fire", "firebird", "fish", "fishing", "florida", "florida1", "flower", "flyers", "football", "football1", "ford", "forever", "forever1", "forever21", "formula1", "frank", "frankie1", "fred", "freddie1", "freddy", "freedom", "freedom1", "friday13", "friends1", "friends2", "fuck", "fucked", "fucker", "fucking", "fuckme", "fuckoff", "fuckoff1", "fuckyou", "fuckyou1", "fuckyou2", "gabriel1", "gandalf", "gangsta1", "garrett1", "gateway", "gateway1", "gators", "gemini", "genesis1", "george", "georgia1", "gerrard8", "giants", "giggles1", "ginger", "girl", "girls", "goddess1", "godislove1", "golden", "golf", "golfer", "gordon", "gordon24", "grandma1", "great", "green", "greenday1", "gregory", "guitar", "gunner", "hammer", "hannah", "happy", "hardcore", "harley", "harry123", "hawaii50", "heather", "heather1", "hello", "hello123", "helpme", "hentai", "hershey1", "hockey", "holiday1", "hollywood1", "honey123", "hooters", "horney", "horny", "hosts", "hotdog", "house", "houston1", "hunter", "hunter01", "hunting", "iceman", "iloveme1", "iloveme2", "iloveyou", "iloveyou1", "iloveyou2", "internet", "internet1", "inuyasha1", "ireland1", "isabella1", "isabelle1", "iverson3", "iwantu", "iydgtvmujl6f", "jack", "jackie", "jackson", "jackson1", "jackson5", "jaguar", "jake", "jamaica1", "james", "james123", "january1", "january29", "japan", "jasmine", "jasmine1", "jason", "jasper", "jazmine1", "jeffrey1", "jehovah1", "jennifer", "jennifer1", "jennifer2", "jeremiah1", "jeremy", "jessica", "jessica1", "jessica7", "jesus", "jesus123", "jesus143", "jesus1st", "jesus4me", "jesus777", "jesuscristo", "jesusis#1", "jesusis1", "john", "john3:16", "johncena1", "johnny", "johnson", "jonathan1", "jordan", "jordan01", "jordan12", "jordan23", "joseph", "joshua", "joshua01", "juice", "junior", "justice1", "justin", "justin01", "justin11", "justin21", "justin23", "katelyn1", "katherine1", "kathryn1", "katrina1", "kazuga", "kelly", "kendall1", "kennedy1", "kenneth1", "kevin", "killer", "kimberly1", "king", "kitty", "knight", "kristen1", "kristin1", "l6fkiy9on", "ladies", "ladybug1", "lakers", "lakers24", "lampard8", "laura123", "lauren", "leather", "lebron23", "legend", "letmein", "letmein1", "liberty1", "lindsay1", "lindsey1", "little", "liverp00l", "liverpool", "liverpool1", "liverpool123", "london", "longhorns1", "looking", "love", "love4ever", "lover", "lovers", "loveyou2", "lucky", "lucky123", "m1chelle", "mackenzie1", "maddog", "madison", "madison01", "madison1", "madonna1", "maggie", "magic", "magnum", "makayla1", "marcelo", "marie123", "marine", "marines1", "marissa1", "mark", "marlboro", "marshall1", "martin", "marvin", "master", "matrix", "matt", "matthew", "matthew1", "matthew2", "matthew3", "maverick", "maxwell", "maxwell1", "melanie1", "melissa", "melissa1", "member", "mercedes", "mercedes1", "merlin", "metallica1", "michael", "michael01", "michael07", "michael1", "michael2", "michael7", "micheal1", "michele1", "michelle", "michelle1", "michelle2", "mickey", "midnight", "midnight1", "mike", "miller", "mine", "miranda1", "mistress", "molly123", "money", "monica", "monique1", "monkey", "monkey01", "monkey12", "monkey13", "monkeys1", "monster", "monster1", "montana1", "morgan", "mother", "mountain", "movie", "muffin", "murphy", "music", "music123", "mustang", "mustang1", "myspace1", "naked", "nascar", "natalie1", "natasha1", "nathan", "nathan06", "naughty", "ncc1701", "newyork", "newyork1", "nicholas", "nicholas1", "nichole1", "nicole", "nicole12", "ninja", "nipple", "nipples", "nirvana1", "november1", "november11", "november15", "november16", "nursing1", "october1", "october13", "october22", "oliver", "omarion1", "orange", "orlando1", "ou812", "p4ssword", "p@$$w0rd", "p@55w0rd", "p@ssw0rd", "pa$$w0rd", "pa55w0rd", "pa55word", "packers", "panther", "panther1", "panthers1", "panties", "paris", "parker", "pass", "pass1234", "passion1", "passw0rd", "passw0rd1", "password", "password01", "password1", "password1!", "password11", "password12", "password123", "password13", "password2", "password21", "password3", "password4", "password5", "password7", "password9", "patches1", "patricia1", "patrick", "patrick1", "paul", "peaches", "peaches1", "peanut", "peanut01", "peanut11", "pebbles1", "penguin1", "penis", "pepper", "peter", "phantom", "phantom1", "phoenix", "phoenix1", "photoshop", "pickles1", "playboy1", "player", "please", "pokemon1", "poohbear1", "pookie", "popcorn1", "porn", "porno", "porsche", "power", "pr1nc3ss", "pr1ncess", "precious1", "preston1", "prince", "princess", "princess01", "princess07", "princess08", "princess1", "princess12", "princess123", "princess13", "princess15", "princess18", "princess19", "princess2", "princess21", "princess23", "princess24", "princess4", "princess5", "princess7", "private", "prototype1", "pumpkin1", "purple", "pussies", "pussy", "qazwsx", "qwert", "qwerty", "qwerty123", "qwertyui", "qwertyuiop", "rabbit", "rachel", "racing", "raiders", "raiders1", "rainbow", "rainbow1", "ranger", "rangers", "rangers1", "raymond1", "rebecca", "rebecca1", "rebelde1", "redskins", "redskins1", "redsox", "redwings", "ricardo1", "richard", "richard1", "robert", "robert01", "rock", "rocket", "rockstar1", "rocky123", "rockyou1", "ronaldo7", "rosebud", "runner", "rush2112", "russell1", "russia", "rusty123", "sabrina1", "sail2boat3", "samantha", "samantha1", "sammy", "samson", "sandra", "santana1", "saturn", "savannah1", "scooby", "scooter", "scooter1", "scorpio", "scorpio1", "scorpion", "scotland1", "scott", "scrappy1", "sebastian1", "secret", "senior06", "senior07", "september1", "serenity1", "sexsex", "sexy", "shadow", "shannon", "shannon1", "shaved", "shit", "shopping1", "sierra", "silver", "skippy", "skittles1", "slayer", "slipknot1", "slut", "smith", "smokey", "smokey01", "snickers1", "snoopy", "snowball1", "soccer", "soccer11", "soccer12", "soccer13", "soccer14", "soccer17", "softball1", "sophie", "spanky", "sparky", "spartan117", "special1", "spencer1", "spider", "spiderman1", "spongebob1", "squirt", "srinivas", "star", "stars", "start123", "startrek", "starwars", "starwars1", "steelers", "steelers1", "stephanie1", "stephen1", "steve", "steven", "sticky", "stupid", "success", "suckit", "summer", "summer01", "summer05", "summer06", "summer07", "summer08", "summer99", "sunshine", "sunshine1", "super", "superman", "superman1", "superstar1", "surfer", "sweetie1", "sweetpea1", "swimming", "sydney", "taylor", "taylor13", "tbfkiy9on", "teddybear1", "teens", "tennis", "teresa", "test", "tester", "testing", "theman", "thesims2", "thirteen13", "thomas", "thumper1", "thunder", "thunder1", "thx1138", "tiffany", "tiffany1", "tiger", "tiger123", "tigers", "tigger", "tigger01", "tigger12", "tigger123", "time", "timothy1", "tinkerbell1", "titanic1", "tits", "tomcat", "topgun", "toyota", "travis", "trinity1", "trinity3", "tristan1", "trouble", "trouble1", "trustno1", "tucker", "turtle", "twilight1", "twitter", "unicorn1", "united", "vagina", "valerie1", "vampire1", "vanessa1", "vanilla1", "veronica1", "victor", "victoria", "victoria1", "video", "viking", "vincent1", "viper", "voodoo", "voyager", "walter", "warrior", "welcome", "welcome1", "welcome123", "welcome2", "whatever", "whatever1", "white", "whitney1", "william", "william1", "willie", "wilson", "winner", "winston", "winston1", "winter", "winter06", "wizard", "wolf", "women", "xavier", "xxxx", "xxxxx", "xxxxxx", "xxxxxxxx", "yamaha", "yankee", "yankees", "yankees1", "yankees2", "yellow", "young", "z,iyd86i", "zachary1", "zaq!1qaz", "zaq!2wsx", "zaq!xsw2", "zaq1!qaz", "zaq1@wsx", "zaq1zaq!", "zxcvbn", "zxcvbnm", "zzzzzz"]; 286 | 287 | if (typeof(module) === "object" && module.exports) { 288 | module.exports = PasswordStrength; 289 | } else if (typeof define === "function" && define.amd) { 290 | define("password_strength", [], function() { 291 | return PasswordStrength; 292 | }); 293 | } else if (typeof(window) === "object") { 294 | window.PasswordStrength = PasswordStrength; 295 | } 296 | })(); 297 | -------------------------------------------------------------------------------- /examples/password_strength.js: -------------------------------------------------------------------------------- 1 | var PasswordStrength = (function(){ 2 | var MULTIPLE_NUMBERS_RE = /\d.*?\d.*?\d/; 3 | var MULTIPLE_SYMBOLS_RE = /[!@#$%^&*?_~].*?[!@#$%^&*?_~]/; 4 | var UPPERCASE_LOWERCASE_RE = /([a-z].*[A-Z])|([A-Z].*[a-z])/; 5 | var SYMBOL_RE = /[!@#\$%^&*?_~]/; 6 | 7 | function PasswordStrength() { 8 | this.username = null; 9 | this.password = null; 10 | this.score = 0; 11 | this.status = null; 12 | } 13 | 14 | PasswordStrength.fn = PasswordStrength.prototype; 15 | 16 | PasswordStrength.fn.test = function() { 17 | var score; 18 | this.score = score = 0; 19 | 20 | if (this.containInvalidMatches()) { 21 | this.status = "invalid"; 22 | } else if (this.usesCommonWord()) { 23 | this.status = "invalid"; 24 | } else { 25 | score += this.scoreFor("password_size"); 26 | score += this.scoreFor("numbers"); 27 | score += this.scoreFor("symbols"); 28 | score += this.scoreFor("uppercase_lowercase"); 29 | score += this.scoreFor("numbers_chars"); 30 | score += this.scoreFor("numbers_symbols"); 31 | score += this.scoreFor("symbols_chars"); 32 | score += this.scoreFor("only_chars"); 33 | score += this.scoreFor("only_numbers"); 34 | score += this.scoreFor("username"); 35 | score += this.scoreFor("sequences"); 36 | score += this.scoreFor("repetitions"); 37 | 38 | if (score < 0) { 39 | score = 0; 40 | } 41 | 42 | if (score > 100) { 43 | score = 100; 44 | } 45 | 46 | if (score < 35) { 47 | this.status = "weak"; 48 | } 49 | 50 | if (score >= 35 && score < 70) { 51 | this.status = "good"; 52 | } 53 | 54 | if (score >= 70) { 55 | this.status = "strong"; 56 | } 57 | } 58 | 59 | this.score = score; 60 | return this.score; 61 | }; 62 | 63 | PasswordStrength.fn.scoreFor = function(name) { 64 | score = 0; 65 | 66 | switch (name) { 67 | case "password_size": 68 | if (this.password.length < 6) { 69 | score = -100; 70 | } else { 71 | score = this.password.length * 4; 72 | } 73 | break; 74 | 75 | case "numbers": 76 | if (this.password.match(MULTIPLE_NUMBERS_RE)) { 77 | score = 5; 78 | } 79 | break; 80 | 81 | case "symbols": 82 | if (this.password.match(MULTIPLE_SYMBOLS_RE)) { 83 | score = 5; 84 | } 85 | break; 86 | 87 | case "uppercase_lowercase": 88 | if (this.password.match(UPPERCASE_LOWERCASE_RE)) { 89 | score = 10; 90 | } 91 | break; 92 | 93 | case "numbers_chars": 94 | if (this.password.match(/[a-z]/i) && this.password.match(/[0-9]/)) { 95 | score = 15; 96 | } 97 | break; 98 | 99 | case "numbers_symbols": 100 | if (this.password.match(/[0-9]/) && this.password.match(SYMBOL_RE)) { 101 | score = 15; 102 | } 103 | break; 104 | 105 | case "symbols_chars": 106 | if (this.password.match(/[a-z]/i) && this.password.match(SYMBOL_RE)) { 107 | score = 15; 108 | } 109 | break; 110 | 111 | case "only_chars": 112 | if (this.password.match(/^[a-z]+$/i)) { 113 | score = -15; 114 | } 115 | break; 116 | 117 | case "only_numbers": 118 | if (this.password.match(/^\d+$/i)) { 119 | score = -15; 120 | } 121 | break; 122 | 123 | case "username": 124 | if (this.password == this.username) { 125 | score = -100; 126 | } else if (this.password.indexOf(this.username) != -1) { 127 | score = -15; 128 | } 129 | break; 130 | 131 | case "sequences": 132 | score += -15 * this.sequences(this.password); 133 | score += -15 * this.sequences(this.reversed(this.password)); 134 | break; 135 | 136 | case "repetitions": 137 | score += -(this.repetitions(this.password, 2) * 4); 138 | score += -(this.repetitions(this.password, 3) * 3); 139 | score += -(this.repetitions(this.password, 4) * 2); 140 | break; 141 | }; 142 | 143 | return score; 144 | }; 145 | 146 | PasswordStrength.fn.isGood = function() { 147 | return this.status == "good"; 148 | }; 149 | 150 | PasswordStrength.fn.isWeak = function() { 151 | return this.status == "weak"; 152 | }; 153 | 154 | PasswordStrength.fn.isStrong = function() { 155 | return this.status == "strong"; 156 | }; 157 | 158 | PasswordStrength.fn.isInvalid = function() { 159 | return this.status == "invalid"; 160 | }; 161 | 162 | PasswordStrength.fn.isValid = function(level) { 163 | if(level == "strong") { 164 | return this.isStrong(); 165 | } else if (level == "good") { 166 | return this.isStrong() || this.isGood(); 167 | } else { 168 | return !this.containInvalidMatches() && !this.usesCommonWord(); 169 | } 170 | }; 171 | 172 | PasswordStrength.fn.containInvalidMatches = function() { 173 | if (!this.exclude) { 174 | return false; 175 | } 176 | 177 | if (!this.exclude.test) { 178 | return false; 179 | } 180 | 181 | return this.exclude.test(this.password.toString()); 182 | }; 183 | 184 | PasswordStrength.fn.usesCommonWord = function() { 185 | return PasswordStrength.commonWords.indexOf(this.password.toLowerCase()) >= 0; 186 | }; 187 | 188 | PasswordStrength.fn.sequences = function(text) { 189 | var matches = 0; 190 | var sequenceSize = 0; 191 | var codes = []; 192 | var len = text.length; 193 | var previousCode, currentCode; 194 | 195 | for (var i = 0; i < len; i++) { 196 | currentCode = text.charCodeAt(i); 197 | previousCode = codes[codes.length - 1]; 198 | codes.push(currentCode); 199 | 200 | if (previousCode) { 201 | if (currentCode == previousCode + 1 || previousCode == currentCode) { 202 | sequenceSize += 1; 203 | } else { 204 | sequenceSize = 0; 205 | } 206 | } 207 | 208 | if (sequenceSize == 2) { 209 | matches += 1; 210 | } 211 | } 212 | 213 | return matches; 214 | }; 215 | 216 | PasswordStrength.fn.repetitions = function(text, size) { 217 | var count = 0; 218 | var matches = {}; 219 | var len = text.length; 220 | var substring; 221 | var occurrences; 222 | var tmpText; 223 | 224 | for (var i = 0; i < len; i++) { 225 | substring = text.substr(i, size); 226 | occurrences = 0; 227 | tmpText = text; 228 | 229 | if (matches[substring] || substring.length < size) { 230 | continue; 231 | } 232 | 233 | matches[substring] = true; 234 | 235 | while ((i = tmpText.indexOf(substring)) != -1) { 236 | occurrences += 1; 237 | tmpText = tmpText.substr(i + 1); 238 | }; 239 | 240 | if (occurrences > 1) { 241 | count += 1; 242 | } 243 | } 244 | 245 | return count; 246 | }; 247 | 248 | PasswordStrength.fn.reversed = function(text) { 249 | var newText = ""; 250 | var len = text.length; 251 | 252 | for (var i = len -1; i >= 0; i--) { 253 | newText += text.charAt(i); 254 | } 255 | 256 | return newText; 257 | }; 258 | 259 | PasswordStrength.test = function(username, password) { 260 | strength = new PasswordStrength(); 261 | strength.username = username; 262 | strength.password = password; 263 | strength.test(); 264 | return strength; 265 | }; 266 | 267 | PasswordStrength.commonWords = ["!qaz1qaz", "!qaz2wsx", "!qaz2wsx", "!qazxsw2", "!qazzaq1", "#edc4rfv", "000000", "010203", "1111", "11111", "111111", "11111111", "112233", "1212", "121212", "123123", "1234", "12345", "123456", "1234567", "12345678", "123456789", "1234567890", "123qweasd", "12qw!@qw", "1313", "131313", "1941.salembbb.41", "1qaz!qaz", "1qaz@wsx", "1qazxsw@", "1qazzaq!", "2000", "2112", "2222", "232323", "2wsx@wsx", "3333", "3edc#edc", "4128", "4321", "4444", "5150", "5555", "555555", "654321", "6666", "666666", "6969", "696969", "7777", "777777", "7777777", "8675309", "987654", "@wsx2wsx", "aaaa", "aaaaaa", "aaliyah1", "abc123", "abc123abc", "abc123abc", "abcabc123", "abcabc123", "abcd1234", "abcdef", "abgrtyu", "abigail1", "access", "access14", "action", "addison1", "admin", "adobe123", "airforce1", "alabama1", "albert", "alex", "alexander1", "alexandra1", "alexis", "allison1", "amanda", "amateur", "america1", "anderson1", "andrea", "andrew", "angel", "angel101", "angel123", "angela", "angelina1", "angels", "animal", "annabelle1", "anthony", "anthony1", "anthony11", "antonio1", "apollo", "apple", "apples", "arianna1", "arsenal", "arsenal1", "arsenal12", "arsenal123", "arthur", "asdf", "asdfgh", "ashley", "ashley12", "asshole", "asshole1", "atlanta1", "august", "august08", "august10", "august12", "august20", "august22", "austin", "austin02", "austin316", "australia1", "awesome1", "azerty", "baby", "babyboy1", "babygirl1", "babygirl1", "babygurl1", "badboy", "bailey", "bailey12", "banana", "barcelona1", "barney", "baseball", "baseball1", "batista1", "batman", "beach", "bear", "beautiful1", "beaver", "beavis", "beckham7", "beer", "bella123", "benjamin1", "bentley1", "bethany1", "bigcock", "bigdaddy", "bigdaddy1", "bigdick", "bigdog", "bigtits", "bill", "billy", "birdie", "bitch", "bitches", "biteme", "black", "blazer", "blessed1", "blink-182", "blink182", "blonde", "blondes", "blondie1", "blowjob", "blowme", "blue", "bond007", "bonnie", "booboo", "boobs", "booger", "boomer", "booty", "boricua1", "boston", "bradley1", "brandon", "brandon1", "brandon2", "brandon7", "brandy", "braves", "braxton1", "brayden1", "brazil", "breanna1", "brian", "brianna1", "brittany1", "brittney1", "bronco", "broncos", "broncos1", "brooklyn1", "brownie1", "bubba", "bubbles1", "buddy", "buddy123", "bulldog", "buster", "butter", "buttercup1", "butterfly1", "butterfly7", "butthead", "buttons1", "calvin", "camaro", "cameron", "cameron1", "canada", "candy123", "captain", "carlos", "carolina1", "carter", "casper", "cassandra1", "catherine1", "celtic1888", "chargers1", "charles", "charles1", "charlie", "charlie1", "charlotte1", "charmed1", "cheese", "chelsea", "chelsea1", "chelsea123", "chester", "chester1", "chevy", "cheyenne1", "chicago", "chicago1", "chicken", "chicken1", "chocolate1", "chopper1", "chris", "chris123", "christian1", "christina1", "christine1", "christmas1", "classof08", "clayton1", "cocacola", "cock", "coffee", "college", "college1", "colombia1", "colorado1", "compaq", "computer", "computer1", "cookie", "cool", "cooper", "corvette", "courtney1", "cowboy", "cowboys", "cowboys1", "cream", "cricket1", "crystal", "crystal1", "cumming", "cumshot", "cunt", "cutiepie1", "daisy123", "dakota", "dallas", "dallas22", "dan1elle", "daniel", "daniela1", "danielle", "danielle1", "dave", "david", "david123", "death666", "debbie", "december1", "december21", "dennis", "derrick1", "destiny1", "deuseamor", "devil666", "diablo", "diamond", "diamond1", "diamonds1", "dick", "dirty", "doctor", "doggie", "dolphin", "dolphin1", "dolphins", "dolphins1", "dominic1", "donald", "douglas1", "dragon", "dreams", "driver", "eagle", "eagle1", "eagles", "edward", "einstein", "elizabeth1", "elizabeth2", "england1", "enjoy", "enter", "eric", "erotic", "extreme", "falcon", "falcons1", "falcons7", "familia", "fender", "ferrari", "fire", "firebird", "fish", "fishing", "florida", "florida1", "flower", "flyers", "football", "football1", "ford", "forever", "forever1", "forever21", "formula1", "frank", "frankie1", "fred", "freddie1", "freddy", "freedom", "freedom1", "friday13", "friends1", "friends2", "fuck", "fucked", "fucker", "fucking", "fuckme", "fuckoff1", "fuckyou", "fuckyou1", "fuckyou2", "fuckyou2", "gabriel1", "gandalf", "gangsta1", "garrett1", "gateway", "gateway1", "gators", "gemini", "genesis1", "george", "georgia1", "gerrard8", "giants", "giggles1", "ginger", "girl", "girls", "goddess1", "godislove1", "golden", "golf", "golfer", "gordon", "gordon24", "grandma1", "great", "green", "greenday1", "gregory", "guitar", "gunner", "hammer", "hannah", "happy", "hardcore", "harley", "harry123", "hawaii50", "heather", "heather1", "hello", "hello123", "helpme", "hentai", "hershey1", "hockey", "holiday1", "hollywood1", "honey123", "hooters", "horney", "horny", "hotdog", "house", "houston1", "hunter", "hunter01", "hunting", "iceman", "iloveme1", "iloveme2", "iloveyou", "iloveyou1", "iloveyou2", "iloveyou2", "internet", "internet1", "inuyasha1", "ireland1", "isabella1", "isabelle1", "iverson3", "iwantu", "iydgtvmujl6f", "jack", "jackie", "jackson", "jackson1", "jackson5", "jaguar", "jake", "jamaica1", "james", "james123", "january1", "january29", "japan", "jasmine", "jasmine1", "jason", "jasper", "jazmine1", "jeffrey1", "jehovah1", "jennifer", "jennifer1", "jennifer2", "jeremiah1", "jeremy", "jessica", "jessica1", "jessica7", "jesus", "jesus123", "jesus143", "jesus1st", "jesus4me", "jesus777", "jesuscristo", "jesusis#1", "jesusis1", "john", "john3:16", "johncena1", "johnny", "johnson", "jonathan1", "jordan", "jordan01", "jordan12", "jordan23", "joseph", "joshua", "joshua01", "juice", "junior", "justice1", "justin", "justin01", "justin11", "justin21", "justin23", "katelyn1", "katherine1", "kathryn1", "katrina1", "kelly", "kendall1", "kennedy1", "kenneth1", "kevin", "killer", "kimberly1", "king", "kitty", "knight", "kristen1", "kristin1", "l6fkiy9on", "ladies", "ladybug1", "lakers", "lakers24", "lampard8", "laura123", "lauren", "leather", "lebron23", "legend", "letmein", "letmein1", "liberty1", "lindsay1", "lindsey1", "little", "liverp00l", "liverpool1", "liverpool123", "london", "longhorns1", "love", "love4ever", "lover", "lovers", "loveyou2", "lucky", "lucky123", "m1chelle", "mackenzie1", "maddog", "madison", "madison01", "madison1", "madonna1", "maggie", "magic", "magnum", "makayla1", "marcelo", "marie123", "marine", "marines1", "marissa1", "mark", "marlboro", "marshall1", "martin", "marvin", "master", "matrix", "matt", "matthew", "matthew1", "matthew2", "matthew3", "maverick", "maxwell", "maxwell1", "melanie1", "melissa", "melissa1", "member", "mercedes", "mercedes1", "merlin", "metallica1", "michael", "michael01", "michael07", "michael1", "michael2", "michael7", "micheal1", "michele1", "michelle", "michelle1", "michelle2", "mickey", "midnight", "midnight1", "mike", "miller", "mine", "miranda1", "mistress", "molly123", "money", "monica", "monique1", "monkey", "monkey01", "monkey12", "monkey13", "monkeys1", "monster", "monster1", "montana1", "morgan", "mother", "mountain", "movie", "muffin", "murphy", "music", "music123", "mustang", "mustang1", "myspace1", "naked", "nascar", "natalie1", "natasha1", "nathan", "nathan06", "naughty", "ncc1701", "newyork", "newyork1", "nicholas", "nicholas1", "nichole1", "nicole", "nicole12", "ninja", "nipple", "nipples", "nirvana1", "november1", "november11", "november15", "november16", "nursing1", "october1", "october13", "october22", "oliver", "omarion1", "orange", "orlando1", "ou812", "p4ssword", "p@$$w0rd", "p@55w0rd", "p@ssw0rd", "pa$$w0rd", "pa55w0rd", "pa55word", "packers", "panther", "panther1", "panthers1", "panties", "paris", "parker", "pass", "pass1234", "passion1", "passw0rd", "passw0rd", "passw0rd1", "password", "password01", "password1", "password1", "password1!", "password11", "password12", "password12", "password123", "password123", "password13", "password2", "password21", "password3", "password4", "password5", "password7", "password9", "patches1", "patricia1", "patrick", "patrick1", "paul", "peaches", "peaches1", "peanut", "peanut01", "peanut11", "pebbles1", "penguin1", "penis", "pepper", "peter", "phantom", "phantom1", "phoenix", "phoenix1", "photoshop", "pickles1", "playboy1", "player", "please", "pokemon1", "poohbear1", "poohbear1", "pookie", "popcorn1", "porn", "porno", "porsche", "power", "pr1nc3ss", "pr1ncess", "precious1", "preston1", "prince", "princess", "princess01", "princess07", "princess08", "princess1", "princess12", "princess123", "princess13", "princess15", "princess18", "princess19", "princess2", "princess21", "princess23", "princess24", "princess4", "princess5", "princess7", "private", "prototype1", "pumpkin1", "purple", "pussies", "pussy", "qazwsx", "qwert", "qwerty", "qwerty123", "qwertyui", "rabbit", "rachel", "racing", "raiders", "raiders1", "rainbow", "rainbow1", "ranger", "rangers", "rangers1", "raymond1", "rebecca", "rebecca1", "rebelde1", "redskins", "redskins1", "redsox", "redwings", "ricardo1", "richard", "richard1", "robert", "robert01", "rock", "rocket", "rockstar1", "rocky123", "rockyou1", "rockyou1", "ronaldo7", "rosebud", "runner", "rush2112", "russell1", "russia", "rusty123", "sabrina1", "sail2boat3", "samantha", "samantha1", "sammy", "samson", "sandra", "santana1", "saturn", "savannah1", "scooby", "scooter", "scooter1", "scorpio", "scorpio1", "scorpion", "scotland1", "scott", "scrappy1", "sebastian1", "secret", "senior06", "senior07", "september1", "serenity1", "sexsex", "sexy", "shadow", "shannon", "shannon1", "shaved", "shit", "shopping1", "sierra", "silver", "skippy", "skittles1", "slayer", "slipknot1", "slut", "smith", "smokey", "smokey01", "snickers1", "snoopy", "snowball1", "soccer", "soccer11", "soccer12", "soccer13", "soccer14", "soccer17", "softball1", "sophie", "spanky", "sparky", "spartan117", "special1", "spencer1", "spider", "spiderman1", "spongebob1", "squirt", "srinivas", "star", "stars", "start123", "startrek", "starwars", "starwars1", "steelers", "steelers1", "stephanie1", "stephen1", "steve", "steven", "sticky", "stupid", "success", "suckit", "summer", "summer01", "summer05", "summer06", "summer07", "summer08", "summer99", "sunshine", "sunshine1", "super", "superman", "superman1", "superstar1", "surfer", "sweetie1", "sweetpea1", "swimming", "sydney", "taylor", "taylor13", "tbfkiy9on", "teddybear1", "teens", "tennis", "teresa", "test", "tester", "testing", "theman", "thesims2", "thirteen13", "thomas", "thumper1", "thunder", "thunder1", "thx1138", "tiffany", "tiffany1", "tiger", "tiger123", "tigers", "tigger", "tigger01", "tigger12", "tigger123", "time", "timothy1", "tinkerbell1", "titanic1", "tits", "tomcat", "topgun", "toyota", "travis", "trinity1", "trinity3", "tristan1", "trouble", "trouble1", "trustno1", "trustno1", "trustno1", "tucker", "turtle", "twilight1", "twitter", "unicorn1", "united", "vagina", "valerie1", "vampire1", "vanessa1", "vanilla1", "veronica1", "victor", "victoria", "victoria1", "video", "viking", "vincent1", "viper", "voodoo", "voyager", "walter", "warrior", "welcome", "welcome1", "welcome123", "welcome2", "whatever", "whatever1", "white", "whitney1", "william", "william1", "willie", "wilson", "winner", "winston", "winston1", "winter", "winter06", "wizard", "wolf", "women", "xavier", "xxxx", "xxxxx", "xxxxxx", "xxxxxxxx", "yamaha", "yankee", "yankees", "yankees1", "yankees2", "yellow", "young", "z,iyd86i", "zachary1", "zaq!1qaz", "zaq!2wsx", "zaq!xsw2", "zaq1!qaz", "zaq1@wsx", "zaq1zaq!", "zxcvbn", "zxcvbnm", "zzzzzz"]; 268 | 269 | return PasswordStrength; 270 | })(); 271 | -------------------------------------------------------------------------------- /gulp/tasks/archive.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import config from '../../config'; 4 | import gulp from 'gulp'; 5 | import zip from 'gulp-zip'; 6 | import notify from 'gulp-notify'; 7 | 8 | export default function (src = config.archive.src, dest = config.archive.dest, message = 'Archive task complete') { 9 | return function () { 10 | return gulp.src(src) 11 | .pipe(zip(`${config.version}.zip`)) 12 | .pipe(gulp.dest(dest)) 13 | .pipe(notify({ 14 | title: config.notify.title, 15 | message: message 16 | })); 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /gulp/tasks/assets.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import config from '../../config'; 4 | import merge from 'merge-stream'; 5 | import gulp from 'gulp'; 6 | import notify from 'gulp-notify'; 7 | import AssetsManager from 'assets-manager'; 8 | import {argv} from 'yargs'; 9 | 10 | function getPackage() { 11 | if (argv.package) { 12 | return argv.package; 13 | } 14 | return null; 15 | } 16 | 17 | /* 18 | * Checkout https://github.com/amazingSurge/assets-manager 19 | */ 20 | export function copy(options = config.assets, message = 'Assets task complete') { 21 | return function (done) { 22 | let pkgName = getPackage(); 23 | const manager = new AssetsManager('manifest.json', options); 24 | 25 | if(pkgName) { 26 | manager.copyPackage(pkgName).then(()=>{ 27 | done(); 28 | }); 29 | } else { 30 | manager.copyPackages().then(()=>{ 31 | done(); 32 | }); 33 | } 34 | } 35 | } 36 | 37 | export function clean(options = config.assets, message = 'Assets clean task complete') { 38 | return function (done) { 39 | let pkgName = getPackage(); 40 | const manager = new AssetsManager('manifest.json', options); 41 | 42 | if(pkgName) { 43 | manager.cleanPackage(pkgName).then(()=>{ 44 | done(); 45 | }); 46 | } else { 47 | manager.cleanPackages().then(()=>{ 48 | done(); 49 | }); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /gulp/tasks/browser.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import config from '../../config'; 4 | import browser from 'browser-sync'; 5 | import notifier from 'node-notifier'; 6 | 7 | export function init(options = {}, message = 'Browser starting') { 8 | options = Object.assign(options, { 9 | server: { 10 | baseDir: config.browser.baseDir, 11 | }, 12 | startPath: config.browser.startPath, 13 | port: config.browser.browserPort, 14 | ui: { 15 | port: config.browser.UIPort 16 | }, 17 | ghostMode: { 18 | links: false 19 | } 20 | }); 21 | 22 | return function() { 23 | browser.init(options, () => { 24 | notifier.notify({ 25 | title: config.notify.title, 26 | message: message 27 | }); 28 | }); 29 | }; 30 | } 31 | 32 | export function reload(message = 'Browser reloaded') { 33 | return function(done) { 34 | browser.reload(); 35 | done(); 36 | 37 | notifier.notify({ 38 | title: config.notify.title, 39 | message: message 40 | }); 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /gulp/tasks/clean.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import config from '../../config'; 4 | import del from 'del'; 5 | 6 | export default function (src = config.paths.destDir) { 7 | return function (done) { 8 | del.sync([src]); 9 | 10 | done(); 11 | }; 12 | } 13 | -------------------------------------------------------------------------------- /gulp/tasks/deploy.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import config from '../../config'; 4 | import gulp from 'gulp'; 5 | import inquirer from 'inquirer'; 6 | import replace from 'gulp-replace'; 7 | import { execSync as exec, spawnSync as spawn } from 'child_process'; 8 | import semver from 'semver'; 9 | import gutil from 'gulp-util'; 10 | 11 | const CURRENT_VERSION = config.version; 12 | let NEXT_VERSION; 13 | let NEXT_MESSAGE; 14 | 15 | export function prompt(done) { 16 | inquirer.prompt([{ 17 | type: 'input', 18 | name: 'version', 19 | message: `What version are we moving to? (Current version is ${CURRENT_VERSION})`, 20 | validate: function (input) { 21 | if(input === '') { 22 | input = CURRENT_VERSION; 23 | } 24 | return /^\d*[\d.]*\d*$/.test(input); 25 | } 26 | }]).then((answers) => { 27 | if (answers.version === '') { 28 | NEXT_VERSION = semver.inc(CURRENT_VERSION, config.deploy.increment); 29 | gutil.log(gutil.colors.green(`No version inputted, bump to version ${NEXT_VERSION}`)); 30 | } else { 31 | NEXT_VERSION = answers.version; 32 | } 33 | 34 | done(); 35 | }); 36 | } 37 | 38 | export function message(done) { 39 | inquirer.prompt([{ 40 | type: 'input', 41 | name: 'message', 42 | message: `What message are we going to commit?`, 43 | validate: function (input) { 44 | if(input === '' && NEXT_VERSION === CURRENT_VERSION) { 45 | return false; 46 | } 47 | return true; 48 | } 49 | }]).then((answers) => { 50 | if (answers.message !== '') { 51 | NEXT_MESSAGE = answers.message; 52 | } else { 53 | NEXT_MESSAGE = ''; 54 | } 55 | done(); 56 | }); 57 | } 58 | 59 | // Bumps the version number in any file that has one 60 | export function version() { 61 | return gulp.src(config.deploy.versionFiles, { 62 | base: process.cwd() 63 | }) 64 | // .pipe(replace(CURRENT_VERSION, NEXT_VERSION)) 65 | .pipe(replace(/Version\s*:\s*([\d.]+)/, `Version: ${NEXT_VERSION}`)) 66 | .pipe(replace(/("|')version\1\s*:\s*("|')([\d.]+)\2/, `$1version$1: $2${NEXT_VERSION}$2`)) 67 | .pipe(gulp.dest('.')); 68 | } 69 | 70 | export function init(done) { 71 | config.production = true; 72 | config.init(); 73 | 74 | done(); 75 | } 76 | 77 | // Writes a commit with the changes to the version numbers 78 | export function commit(done) { 79 | let message = `Release ${NEXT_VERSION}`; 80 | 81 | if (NEXT_VERSION === CURRENT_VERSION) { 82 | message = NEXT_MESSAGE; 83 | } else if(NEXT_MESSAGE !== '') { 84 | message = `${message}; ${NEXT_MESSAGE}`; 85 | } 86 | exec('git add .'); 87 | exec(`git commit -am "${message}"`); 88 | 89 | if (NEXT_VERSION !== CURRENT_VERSION) { 90 | exec(`git tag v${NEXT_VERSION}`); 91 | } 92 | 93 | exec('git push origin master --follow-tags'); 94 | done(); 95 | } 96 | -------------------------------------------------------------------------------- /gulp/tasks/lint-scripts.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import config from '../../config'; 4 | import gulp from 'gulp'; 5 | import eslint from 'gulp-eslint'; 6 | import getSrcFiles from '../util/getSrcFiles'; 7 | 8 | export function es(src = config.scripts.src, options = {}, files = ['**/*.js', '!**/*.min.js']) { 9 | return function() { 10 | let srcFiles = getSrcFiles(src, files); 11 | 12 | options = Object.assign({ 13 | useEslintrc: true, 14 | configFile: '.eslintrc.json', 15 | fix: true 16 | }, options); 17 | 18 | return gulp.src(srcFiles, { 19 | base: './' 20 | }) 21 | .pipe(eslint(options)) 22 | .pipe(eslint.format()) 23 | .pipe(eslint.failAfterError()); 24 | }; 25 | } 26 | 27 | -------------------------------------------------------------------------------- /gulp/tasks/lint-styles.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import config from '../../config'; 4 | import gulp from 'gulp'; 5 | import stylelint from 'gulp-stylelint'; 6 | import getSrcFiles from '../util/getSrcFiles'; 7 | 8 | export function style(src = config.styles.src, files = '**/*.scss') { 9 | return function() { 10 | let srcFiles = getSrcFiles(src, files); 11 | 12 | return gulp.src(srcFiles) 13 | .pipe(stylelint({ 14 | reporters: [{ 15 | formatter: 'string', 16 | console: true 17 | }] 18 | })); 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /gulp/tasks/release.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import config from '../../config'; 4 | import releaseIt from 'release-it'; 5 | import handleErrors from '../util/handleErrors'; 6 | import {argv} from 'yargs'; 7 | 8 | export default function release() { 9 | let options = {}; 10 | options.increment = argv.increment || "patch"; 11 | options.verbose = argv.verbose || true; 12 | options.debug = argv.debug || false; 13 | options.force = argv.force || false; 14 | options['dry-run'] = argv['dry-run'] || false; 15 | 16 | config.setEnv('production'); 17 | 18 | return function(done) { 19 | releaseIt.execute(options).catch(handleErrors).finally(done); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /gulp/tasks/scripts.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import config from '../../config'; 4 | import gulp from 'gulp'; 5 | import babel from 'gulp-babel'; 6 | import gulpif from 'gulp-if'; 7 | import sourcemaps from 'gulp-sourcemaps'; 8 | import handleErrors from '../util/handleErrors'; 9 | import getSrcFiles from '../util/getSrcFiles'; 10 | import browser from 'browser-sync'; 11 | import header from 'gulp-header'; 12 | import rename from 'gulp-rename'; 13 | import rollup from 'gulp-rollup'; 14 | import uglify from 'gulp-uglify'; 15 | import size from 'gulp-size'; 16 | import plumber from 'gulp-plumber'; 17 | import prettier from 'gulp-nf-prettier'; 18 | import path from 'path'; 19 | import notify from 'gulp-notify'; 20 | import replace from 'gulp-replace'; 21 | 22 | export function bundler(src = config.scripts.src, dest = config.scripts.dest, input = config.scripts.input, files = config.scripts.files, message = 'Bundler task complete') { 23 | return function () { 24 | let srcFiles = getSrcFiles(src, files); 25 | 26 | return gulp.src(srcFiles) 27 | .on('error', handleErrors) 28 | .pipe(plumber({errorHandler: handleErrors})) 29 | .pipe(rollup({ 30 | input: `${src}/${input}`, 31 | format: 'es', 32 | globals: { 33 | jquery: 'jQuery', 34 | 'password_strength': 'PasswordStrength' 35 | } 36 | })) 37 | .pipe(header(config.banner)) 38 | .pipe(rename({ 39 | basename: config.name, 40 | suffix: '.es' 41 | })) 42 | .pipe(gulp.dest(dest)) 43 | .pipe(notify({ 44 | title: config.notify.title, 45 | message: message, 46 | onLast: true 47 | })); 48 | }; 49 | } 50 | 51 | export function scripts(src = config.scripts.src, dest = config.scripts.dest, input = config.scripts.input, files = config.scripts.files, message = 'Scripts task complete') { 52 | const createSourcemap = config.deploy || config.scripts.prodSourcemap; 53 | 54 | return function () { 55 | let srcFiles = getSrcFiles(src, files); 56 | 57 | return gulp.src(`${dest}/${config.name}.es.js`) 58 | .on('error', handleErrors) 59 | .pipe(plumber({errorHandler: handleErrors})) 60 | // .pipe(rollup({ 61 | // input: `${src}/${input}`, 62 | // })) 63 | .pipe(babel({ 64 | "presets": ["es2015"], 65 | "plugins": [ 66 | ["transform-es2015-modules-umd", { 67 | "globals": { 68 | "jquery": "jQuery", 69 | 'password_strength': 'PasswordStrength' 70 | } 71 | }] 72 | ] 73 | })) 74 | .pipe(header(config.banner)) 75 | .pipe( 76 | prettier({ 77 | parser: 'flow', 78 | tabWidth: 2, 79 | useTabs: false, 80 | semi: true, 81 | singleQuote: true, 82 | bracketSpacing: true, 83 | }) 84 | ) 85 | .pipe(rename({ 86 | basename: config.name 87 | })) 88 | .pipe(gulp.dest(dest)) 89 | .pipe(size({ 90 | title: 'scripts', 91 | showFiles: true 92 | })) 93 | .pipe(rename({ 94 | suffix: '.min' 95 | })) 96 | .pipe(gulpif(createSourcemap, sourcemaps.init())) 97 | .pipe(uglify()) 98 | .pipe(header(config.banner)) 99 | .pipe(gulpif( 100 | createSourcemap, 101 | sourcemaps.write(config.deploy ? './' : null)) 102 | ) 103 | .pipe(gulp.dest(dest)) 104 | .pipe(size({ 105 | title: 'minified scripts', 106 | showFiles: true 107 | })) 108 | .pipe(browser.stream()) 109 | .pipe(notify({ 110 | title: config.notify.title, 111 | message: message, 112 | onLast: true 113 | })); 114 | }; 115 | } 116 | 117 | export function version(src = config.scripts.src, file = config.scripts.version) { 118 | return function() { 119 | return gulp.src(path.join(src, file), {base: "./"}) 120 | .pipe(replace(/("{0,1}|'{0,1})version\1\s*:\s*("|')([\d.]+)\2/, `$1version$1:$2${config.version}$2`)) 121 | .pipe(gulp.dest("./", {overwrite: true})); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /gulp/tasks/styles.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import config from '../../config'; 4 | import gulp from 'gulp'; 5 | import gulpif from 'gulp-if'; 6 | import sourcemaps from 'gulp-sourcemaps'; 7 | import sass from 'gulp-sass'; 8 | import sassUnicode from 'gulp-sass-unicode'; 9 | import handleErrors from '../util/handleErrors'; 10 | import browser from 'browser-sync'; 11 | import autoprefixer from 'gulp-autoprefixer'; 12 | import cssnano from 'gulp-cssnano'; 13 | import csscomb from 'gulp-csscomb'; 14 | import rename from 'gulp-rename'; 15 | import header from 'gulp-header'; 16 | import size from 'gulp-size'; 17 | import plumber from 'gulp-plumber'; 18 | import notify from 'gulp-notify'; 19 | import getSrcFiles from '../util/getSrcFiles'; 20 | 21 | export default function (src = config.styles.src, dest = config.styles.dest, files = config.styles.files, message = 'Styles task complete') { 22 | const createSourcemap = config.deploy || config.styles.prodSourcemap; 23 | 24 | return function() { 25 | let srcFiles = getSrcFiles(src, files); 26 | 27 | return gulp.src(srcFiles) 28 | .on('error', handleErrors) 29 | .pipe(plumber({errorHandler: handleErrors})) 30 | .pipe(sass({ 31 | outputStyle: 'nested', 32 | includePaths: config.styles.sassIncludePaths 33 | })) 34 | .pipe(sassUnicode()) 35 | .pipe(csscomb('.csscomb.json')) 36 | .pipe(autoprefixer(config.styles.autoprefixer)) 37 | .pipe(header(config.banner)) 38 | .pipe(gulp.dest(dest)) 39 | .pipe(size({ 40 | title: 'styles', 41 | showFiles: true 42 | })) 43 | .pipe(rename({ 44 | suffix: '.min' 45 | })) 46 | .pipe(gulpif(createSourcemap, sourcemaps.init())) 47 | .pipe(cssnano({ 48 | safe: true, 49 | autoprefixer: false 50 | })) 51 | .pipe(header(config.banner)) 52 | .pipe(gulpif( 53 | createSourcemap, 54 | sourcemaps.write(config.deploy ? './' : null)) 55 | ) 56 | .pipe(gulp.dest(dest)) 57 | .pipe(size({ 58 | title: 'minified styles', 59 | showFiles: true 60 | })) 61 | .pipe(browser.stream()) 62 | .pipe(notify({ 63 | title: config.notify.title, 64 | message: message, 65 | onLast: true 66 | })); 67 | }; 68 | } 69 | -------------------------------------------------------------------------------- /gulp/tasks/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { Server as KarmaServer } from 'karma'; 4 | 5 | export default function (options = {}) { 6 | return function(done) { 7 | options = Object.assign({ 8 | configFile: `${__dirname}/../../karma.conf.js`, 9 | }, options); 10 | 11 | let karma = new KarmaServer(options, done); 12 | 13 | karma.start(); 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /gulp/util/getFolders.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import fs from 'graceful-fs'; 4 | import path from 'path'; 5 | 6 | export default function(dir) { 7 | return fs.readdirSync(dir).filter((file) => { 8 | return fs.statSync(path.join(dir, file)).isDirectory(); 9 | }); 10 | } 11 | -------------------------------------------------------------------------------- /gulp/util/getSrcFiles.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import {argv} from 'yargs'; 4 | import path from 'path'; 5 | import pathExists from 'path-exists'; 6 | 7 | export default function(src, files, argName = 'file') { 8 | let srcFiles = ''; 9 | 10 | if(argv[argName] && pathExists.sync(path.join(src, argv[argName]))) { 11 | let arg = argv[argName]; 12 | srcFiles = `${src}/${arg}`; 13 | } else if(Array.isArray(files)) { 14 | srcFiles = files.map((file) => { 15 | if(file.indexOf('!') === 0) { 16 | file = file.substr(1); 17 | return `!${src}/${file}`; 18 | } 19 | 20 | return `${src}/${file}`; 21 | }); 22 | } else { 23 | srcFiles = `${src}/${files}`; 24 | } 25 | 26 | return srcFiles; 27 | } 28 | -------------------------------------------------------------------------------- /gulp/util/handleErrors.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import gutil from 'gulp-util'; 4 | import config from '../../config'; 5 | import notifier from 'node-notifier'; 6 | 7 | export default function(error) { 8 | if (!config.deploy) { 9 | // Send error to notification center with gulp-notify 10 | notifier.notify({ 11 | title: config.notify.title, 12 | subtitle: 'Failure!', 13 | message: error.message, 14 | }); 15 | gutil.log(gutil.colors.red(error)); 16 | // Keep gulp from hanging on this task 17 | this.emit('end'); 18 | } else { 19 | // Log the error and stop the process 20 | // to prevent broken code from building 21 | gutil.log(gutil.colors.red(error)); 22 | process.exit(1); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /gulpfile.babel.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import config from './config'; 4 | import fs from 'graceful-fs'; 5 | import gulp from 'gulp'; 6 | import gutil from 'gulp-util'; 7 | 8 | // Tasks 9 | import clean from './gulp/tasks/clean'; 10 | import styles from './gulp/tasks/styles'; 11 | import {version,bundler,scripts} from './gulp/tasks/scripts'; 12 | import * as lintScripts from './gulp/tasks/lint-scripts'; 13 | import * as lintStyles from './gulp/tasks/lint-styles'; 14 | import test from './gulp/tasks/test'; 15 | import * as deploy from './gulp/tasks/deploy'; 16 | import * as browser from './gulp/tasks/browser'; 17 | import * as assets from './gulp/tasks/assets'; 18 | import archive from './gulp/tasks/archive'; 19 | import release from './gulp/tasks/release'; 20 | 21 | if (config.production) { 22 | gutil.log(gutil.colors.bold.green('� Production Mode')); 23 | } else { 24 | gutil.log(gutil.colors.bold.green('� Development Mode')); 25 | } 26 | 27 | gulp.task('version', version()); 28 | gulp.task('bundler', bundler()); 29 | gulp.task('scripts', scripts()); 30 | gulp.task('clean', clean(config.scripts.dest)); 31 | 32 | // Styles 33 | gulp.task('styles', styles()); 34 | gulp.task('clean:styles', clean(config.styles.dest)); 35 | 36 | // Build the files 37 | gulp.task('build', gulp.series('clean', 'version', 'bundler', 'scripts', 'styles')); 38 | 39 | // Assets 40 | gulp.task('assets', assets.copy()); 41 | gulp.task('clean:assets', assets.clean()); 42 | 43 | // Lint Styles 44 | gulp.task('lint:style', lintStyles.style()); 45 | 46 | // Lint Scripts 47 | gulp.task('lint:script:src', lintScripts.es(config.scripts.src)); 48 | gulp.task('lint:script:dest', lintScripts.es(config.scripts.dest)); 49 | gulp.task('lint:script:test', lintScripts.es(config.scripts.test)); 50 | gulp.task('lint:script:gulp', lintScripts.es(config.scripts.gulp, {rules: {'no-console': 'off'}})); 51 | gulp.task('lint:script', gulp.series('lint:script:src', 'lint:script:dest', 'lint:script:test', 'lint:script:gulp')); 52 | 53 | // Run karma for development, will watch and reload 54 | gulp.task('tdd', test()); 55 | 56 | // Run tests and report for ci 57 | gulp.task('test', test({ 58 | singleRun: true, 59 | browsers: ['PhantomJS'], 60 | reporters: ['mocha'] 61 | })); 62 | 63 | gulp.task('coverage', test({ 64 | singleRun: true, 65 | browsers: ['PhantomJS'], 66 | reporters: ['coverage'], 67 | })); 68 | 69 | // Deploy 70 | gulp.task('deploy:prompt', deploy.prompt); 71 | gulp.task('deploy:version', deploy.version); 72 | gulp.task('deploy:message', deploy.message); 73 | gulp.task('deploy:init', deploy.init); 74 | gulp.task('deploy:commit', deploy.commit); 75 | 76 | // Generates compiled CSS and JS files and puts them in the dist/ folder 77 | gulp.task('deploy:dist', gulp.series('build')); 78 | gulp.task('deploy:prepare', gulp.series('deploy:prompt', 'deploy:version', 'deploy:init', 'deploy:dist')); 79 | gulp.task('deploy', gulp.series('deploy:prompt', 'deploy:version', 'deploy:message', 'deploy:dist', 'deploy:commit')); 80 | 81 | // Archive the distrubution files into package 82 | gulp.task('archive', archive()); 83 | 84 | // Starts a BrowerSync instance 85 | gulp.task('serve', browser.init()); 86 | 87 | // Reload browser 88 | gulp.task('reload', browser.reload()); 89 | 90 | // Watch files for changes 91 | gulp.task('watch', () => { 92 | gulp.watch(config.scripts.src, gulp.series('scripts', 'reload')); 93 | gulp.watch(config.styles.src, gulp.series('styles', 'reload')); 94 | }); 95 | 96 | // Release task 97 | gulp.task('release', release()); 98 | 99 | // Dev task 100 | gulp.task('dev', gulp.series('build', 'serve')); 101 | 102 | // Register default task 103 | gulp.task('default', gulp.series('lint:script:src', 'serve')); 104 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | import babel from 'rollup-plugin-babel'; 3 | import babelrc from 'babelrc-rollup'; 4 | import istanbul from 'rollup-plugin-istanbul'; 5 | import babel_istanbul from 'babel-istanbul'; 6 | 7 | module.exports = function(config) { 8 | config.set({ 9 | 10 | // base path that will be used to resolve all patterns (eg. files, exclude) 11 | basePath: '', 12 | 13 | // frameworks to use 14 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 15 | frameworks: ['mocha', 'sinon-chai'], 16 | 17 | // list of files / patterns to load in the browser 18 | files: [ 19 | 'test/spec/*.js' 20 | ], 21 | 22 | // preprocess matching files before serving them to the browser 23 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 24 | preprocessors: { 25 | // "dist/**/*.es.js": ["rollup"], 26 | "src/**/*.js": ["rollup", "coverage"], 27 | "test/spec/**/*.spec.js": ["rollup"] 28 | }, 29 | 30 | // optionally, configure the reporter 31 | // text displays it within the console (alternative: text-summary) 32 | // lcov creates a codecov compatible report 33 | coverageReporter: { 34 | reporters: [ 35 | {'type': 'text'}, 36 | {'type': 'html', dir: 'coverage'}, 37 | {'type': 'lcov'} 38 | ] 39 | }, 40 | 41 | // list of files to exclude 42 | exclude: [ 43 | ], 44 | 45 | 46 | // test results reporter to use 47 | // possible values: 'dots', 'progress' 48 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 49 | // coverage is from karma-coverage and provides Istanbul code coverage report 50 | reporters: ['mocha', 'coverage'], 51 | 52 | 53 | // web server port 54 | port: 9876, 55 | 56 | 57 | // enable / disable colors in the output (reporters and logs) 58 | colors: true, 59 | 60 | 61 | // level of logging 62 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 63 | logLevel: config.LOG_INFO, 64 | 65 | 66 | // enable / disable watching file and executing tests whenever any file changes 67 | autoWatch: true, 68 | 69 | 70 | // start these browsers 71 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 72 | // Currently available: 73 | // - Chrome 74 | // - ChromeCanary 75 | // - Firefox 76 | // - Opera 77 | // - Safari (only Mac) 78 | // - PhantomJS 79 | // - IE (only Windows) 80 | browsers: ['Firefox'], 81 | 82 | // Which plugins to enable 83 | plugins: [ 84 | 'karma-mocha', 85 | 'karma-sinon-chai', 86 | 'karma-chrome-launcher', 87 | 'karma-firefox-launcher', 88 | 'karma-phantomjs-launcher', 89 | 'karma-mocha-reporter', 90 | 'karma-coverage', 91 | 'karma-rollup-plugin' 92 | ], 93 | 94 | rollupPreprocessor: { 95 | plugins: [ 96 | babel(babelrc()), 97 | istanbul({ 98 | include: ['src/**/*.js'], 99 | exclude: ['test/spec/**/*.spec.js', 'node_modules/**'], 100 | // instrumenter: babel_istanbul 101 | }), 102 | ], 103 | sourceMap: 'inline' 104 | }, 105 | 106 | // Continuous Integration mode 107 | // if true, Karma captures browsers, runs the tests and exits 108 | singleRun: false, 109 | 110 | // Concurrency level 111 | // how many browser should be started simultaneous 112 | concurrency: Infinity 113 | }) 114 | } 115 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "dest": "examples", 3 | "packages": { 4 | "npm:jquery": true, 5 | "npm:normalize.css": true, 6 | "npm:@fnando/password_strength": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-strength", 3 | "title": "jQuery strength", 4 | "description": "A jquery plugin that do amazing things.", 5 | "version": "0.2.5", 6 | "homepage": "https://github.com/amazingSurge/jquery-strength", 7 | "author": { 8 | "name": "amazingSurge", 9 | "email": "amazingSurge@gmail.com", 10 | "url": "amazingSurge.com" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git@github.com:amazingSurge/jquery-strength.git" 15 | }, 16 | "keywords": [ 17 | "jquery", 18 | "jquery-plugin", 19 | "ecosystem:jquery", 20 | "ui", 21 | "es6", 22 | "strength" 23 | ], 24 | "bugs": "https://github.com/amazingSurge/jquery-strength/issues", 25 | "licenses": [ 26 | { 27 | "type": "LGPL-3.0", 28 | "url": "https://github.com/amazingSurge/jquery-strength/blob/master/LICENSE" 29 | } 30 | ], 31 | "license": "LGPL-3.0", 32 | "main": "dist/jquery-strength.js", 33 | "module": "dist/jquery-strength.es.js", 34 | "files": [ 35 | "dist", 36 | "src" 37 | ], 38 | "scripts": { 39 | "prestart": "npm install", 40 | "start": "gulp serve", 41 | "build": "npm run prestart && gulp build", 42 | "deploy": "gulp deploy", 43 | "deploy:prepare": "gulp deploy:prepare", 44 | "release": "gulp release", 45 | "test": "gulp test" 46 | }, 47 | "devDependencies": { 48 | "yargs": "*", 49 | "assets-manager": "*", 50 | "babel-core": "*", 51 | "babel-eslint": "*", 52 | "babel-istanbul": "*", 53 | "babel-plugin-transform-es2015-modules-umd": "*", 54 | "babel-preset-es2015": "*", 55 | "babel-preset-es2015-rollup": "*", 56 | "babelrc-rollup": "*", 57 | "browser-sync": "*", 58 | "chai": "*", 59 | "del": "*", 60 | "prettier": "*", 61 | "gulp-nf-prettier": "*", 62 | "graceful-fs": "*", 63 | "gulp": "github:gulpjs/gulp#4.0", 64 | "gulp-autoprefixer": "*", 65 | "gulp-babel": "*", 66 | "gulp-changed": "*", 67 | "gulp-csscomb": "*", 68 | "gulp-cssnano": "*", 69 | "gulp-eslint": "*", 70 | "gulp-extname": "*", 71 | "gulp-filter": "*", 72 | "gulp-header": "*", 73 | "gulp-iconfont-css": "*", 74 | "gulp-if": "*", 75 | "gulp-notify": "*", 76 | "gulp-plumber": "*", 77 | "gulp-rename": "*", 78 | "gulp-replace": "*", 79 | "gulp-rollup": "*", 80 | "gulp-sass": "*", 81 | "gulp-sass-unicode": "*", 82 | "gulp-size": "*", 83 | "gulp-sourcemaps": "*", 84 | "gulp-stylelint": "*", 85 | "gulp-uglify": "*", 86 | "gulp-util": "*", 87 | "gulp-zip": "*", 88 | "inquirer": "*", 89 | "semver": "*", 90 | "karma": "*", 91 | "karma-babel-preprocessor": "*", 92 | "karma-chrome-launcher": "*", 93 | "karma-commonjs": "*", 94 | "karma-coverage": "*", 95 | "karma-firefox-launcher": "*", 96 | "karma-mocha": "*", 97 | "karma-mocha-reporter": "*", 98 | "karma-phantomjs-launcher": "*", 99 | "karma-rollup-plugin": "*", 100 | "karma-sinon-chai": "*", 101 | "merge-stream": "*", 102 | "mkdirp": "*", 103 | "mocha": "*", 104 | "node-notifier": "*", 105 | "normalize.css": "^7.0.0", 106 | "path-exists": "*", 107 | "phantomjs-prebuilt": "*", 108 | "release-it": "*", 109 | "rollup-plugin-babel": "*", 110 | "rollup-plugin-istanbul": "*", 111 | "sinon": "*", 112 | "sinon-chai": "*", 113 | "stylelint-config-bootstrap": "*", 114 | "stylelint-scss": "*", 115 | "through2": "*" 116 | }, 117 | "engines": { 118 | "node": ">= 6.2.2", 119 | "npm": ">= 3" 120 | }, 121 | "dependencies": { 122 | "@fnando/password_strength": "^1.1.4", 123 | "jquery": ">=2.2.0" 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/defaults.js: -------------------------------------------------------------------------------- 1 | export default { 2 | namespace: 'strength', 3 | skin: null, 4 | 5 | showMeter: true, 6 | showToggle: true, 7 | 8 | usernameField: '', 9 | 10 | templates: { 11 | toggle: '', 12 | meter: '
{score}
', 13 | score: '', 14 | main: '
{input}{toggle}
{meter}
' 15 | }, 16 | 17 | classes: { 18 | container: 'strength-container', 19 | status: 'strength-{status}', 20 | input: 'strength-input', 21 | toggle: 'strength-toggle', 22 | meter: 'strength-meter', 23 | score: 'strength-score', 24 | shown: 'strength-shown' 25 | }, 26 | 27 | scoreLables: { 28 | empty: 'Empty', 29 | invalid: 'Invalid', 30 | weak: 'Weak', 31 | good: 'Good', 32 | strong: 'Strong' 33 | }, 34 | 35 | scoreClasses: { 36 | empty: '', 37 | invalid: 'label-danger', 38 | weak: 'label-warning', 39 | good: 'label-info', 40 | strong: 'label-success' 41 | }, 42 | 43 | emptyStatus: true, 44 | 45 | scoreCallback: null, 46 | statusCallback: null 47 | }; 48 | -------------------------------------------------------------------------------- /src/info.js: -------------------------------------------------------------------------------- 1 | export default { 2 | version:'0.2.5' 3 | }; 4 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import $ from 'jquery'; 2 | import Strength from './strength'; 3 | import info from './info'; 4 | 5 | const NAMESPACE = 'strength'; 6 | const OtherStrength = $.fn.strength; 7 | 8 | const jQueryStrength = function(options, ...args) { 9 | if (typeof options === 'string') { 10 | const method = options; 11 | 12 | if (/^_/.test(method)) { 13 | return false; 14 | } else if ((/^(get)/.test(method))) { 15 | const instance = this.first().data(NAMESPACE); 16 | if (instance && typeof instance[method] === 'function') { 17 | return instance[method](...args); 18 | } 19 | } else { 20 | return this.each(function() { 21 | const instance = $.data(this, NAMESPACE); 22 | if (instance && typeof instance[method] === 'function') { 23 | instance[method](...args); 24 | } 25 | }); 26 | } 27 | } 28 | 29 | return this.each(function() { 30 | if (!$(this).data(NAMESPACE)) { 31 | $(this).data(NAMESPACE, new Strength(this, options)); 32 | } 33 | }); 34 | }; 35 | 36 | $.fn.strength = jQueryStrength; 37 | 38 | $.strength = $.extend({ 39 | setDefaults: Strength.setDefaults, 40 | noConflict: function() { 41 | $.fn.strength = OtherStrength; 42 | return jQueryStrength; 43 | } 44 | }, info); 45 | -------------------------------------------------------------------------------- /src/scss/strength.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecreation/jquery-strength/4a8098ca2b9385496c6740a136776f5db291c607/src/scss/strength.scss -------------------------------------------------------------------------------- /src/strength.js: -------------------------------------------------------------------------------- 1 | import $ from 'jquery'; 2 | import PasswordStrength from 'password_strength'; 3 | import DEFAULTS from './defaults'; 4 | 5 | const NAMESPACE = 'strength'; 6 | 7 | /** 8 | * Plugin constructor 9 | **/ 10 | class Strength { 11 | constructor(element, options = {}) { 12 | this.element = element; 13 | this.$element = $(element); 14 | 15 | this.options = $.extend(true, {}, DEFAULTS, options, this.$element.data()); 16 | this.classes = this.options.classes; 17 | 18 | this.$username = $(this.options.usernameField); 19 | 20 | this.score = 0; 21 | this.status = null; 22 | 23 | this.shown = false; 24 | 25 | this.trigger('init'); 26 | this.init(); 27 | } 28 | 29 | init() { 30 | this.createHtml(); 31 | 32 | this.$element.addClass(this.classes.input); 33 | 34 | this.$toggle = this.$container.find(`.${this.classes.toggle}`); 35 | this.$meter = this.$container.find(`.${this.classes.meter}`); 36 | 37 | this.$score = this.$container.find(`.${this.classes.score}`); 38 | this.$input = this.$container.find(`.${this.classes.input}`); 39 | 40 | this.bindEvents(); 41 | 42 | this.initialized = true; 43 | this.trigger('ready'); 44 | } 45 | 46 | bindEvents() { 47 | if(this.$toggle.is(':checkbox')){ 48 | this.$toggle.on('change', () => { 49 | this.toggle(); 50 | }); 51 | } else { 52 | this.$toggle.on('click', () => { 53 | this.toggle(); 54 | }); 55 | } 56 | 57 | 58 | this.$input.bind('keyup.strength keydown.strength', () => { 59 | this.check(); 60 | }); 61 | 62 | this.$element.on(`${NAMESPACE}::check`, (e, api, score, status) => { 63 | this.$score.html(this.options.scoreLables[status]); 64 | 65 | if (status !== this.status) { 66 | const newClass = this.options.scoreClasses[status]; 67 | const oldClass = this.options.scoreClasses[this.status]; 68 | this.$score.removeClass(oldClass).addClass(newClass); 69 | 70 | this.trigger('statusChange', status, this.status); 71 | } 72 | 73 | this.status = status; 74 | this.score = score; 75 | }); 76 | 77 | this.$element.on(`${NAMESPACE}::statusChange`, (e, api, current, old) => { 78 | this.$container.removeClass(this.getStatusClass(old)).addClass(this.getStatusClass(current)); 79 | }); 80 | } 81 | 82 | getStatusClass(status) { 83 | return this.options.classes.status.replace('{status}', status); 84 | } 85 | 86 | createHtml() { 87 | let output = this.options.templates.main; 88 | 89 | output = output.replace('{containerClass}', this.classes.container); 90 | output = output.replace('{toggle}', this.generateToggle()); 91 | output = output.replace('{meter}', this.generateMeter()); 92 | output = output.replace('{score}', this.generateScore()); 93 | output = output.replace('{input}', `
`); 94 | this.$container = $(output); 95 | 96 | if (this.options.skin) { 97 | this.$container.addClass(this.options.skin); 98 | } 99 | 100 | this.$element.before(this.$container); 101 | const $holder = this.$container.find(`.${this.classes.input}`); 102 | const el = this.$element.detach(); 103 | $holder.before(el); 104 | $holder.remove(); 105 | } 106 | 107 | generateToggle() { 108 | if (this.options.showToggle) { 109 | let output = this.options.templates.toggle; 110 | 111 | output = output.replace('{toggleClass}', this.classes.toggle); 112 | return output; 113 | } 114 | return ''; 115 | } 116 | 117 | generateMeter() { 118 | if (this.options.showMeter) { 119 | let output = this.options.templates.meter; 120 | 121 | output = output.replace('{meterClass}', this.classes.meter); 122 | return output; 123 | } 124 | return ''; 125 | } 126 | 127 | generateScore() { 128 | let output = this.options.templates.score; 129 | 130 | output = output.replace('{scoreClass}', this.classes.score); 131 | return output; 132 | } 133 | 134 | check() { 135 | let score = 0; 136 | let status = null; 137 | 138 | if ($.isFunction(this.options.scoreCallback)) { 139 | score = this.options.scoreCallback.call(this); 140 | 141 | if ($.isFunction(this.options.statusCallback)) { 142 | status = this.options.statusCallback.call(this, score); 143 | } 144 | } else { 145 | const check = new PasswordStrength(); 146 | check.username = this.$username.val() || null; 147 | check.password = this.$input.val(); 148 | 149 | score = check.test(); 150 | status = check.status; 151 | } 152 | 153 | if (this.options.emptyStatus && status !== 'invalid' && this.$input.val() === '') { 154 | status = 'empty'; 155 | } 156 | 157 | this.trigger('check', score, status); 158 | } 159 | 160 | getScore() { 161 | if (!this.score) { 162 | this.check(); 163 | } 164 | return this.score; 165 | } 166 | 167 | getStatus() { 168 | if (!this.status) { 169 | this.check(); 170 | } 171 | return this.status; 172 | } 173 | 174 | toggle() { 175 | let type; 176 | 177 | if(this.$toggle.is(':checkbox')) { 178 | type = this.$toggle.is(":checked")? "text" : "password"; 179 | } else { 180 | type = this.shown === false?"text" : "password"; 181 | } 182 | 183 | this.shown = type === "text"; 184 | 185 | if(this.shown) { 186 | this.$container.addClass(this.classes.shown); 187 | } else { 188 | this.$container.removeClass(this.classes.shown); 189 | } 190 | this.$input.attr('type', type); 191 | 192 | this.trigger('toggle', type); 193 | } 194 | 195 | trigger(eventType, ...params) { 196 | let data = [this].concat(params); 197 | 198 | // event 199 | this.$element.trigger(`${NAMESPACE}::${eventType}`, data); 200 | 201 | // callback 202 | eventType = eventType.replace(/\b\w+\b/g, (word) => { 203 | return word.substring(0, 1).toUpperCase() + word.substring(1); 204 | }); 205 | let onFunction = `on${eventType}`; 206 | 207 | if (typeof this.options[onFunction] === 'function') { 208 | this.options[onFunction].apply(this, params); 209 | } 210 | } 211 | 212 | destroy() { 213 | this.$element.data(NAMESPACE, null); 214 | this.trigger('destroy'); 215 | } 216 | 217 | static setDefaults(options) { 218 | $.extend(true, DEFAULTS, $.isPlainObject(options) && options); 219 | } 220 | } 221 | 222 | export default Strength; 223 | --------------------------------------------------------------------------------