@iamproperty/components 5.1.0-beta8 → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/css/components/component.native.css +1 -1
- package/assets/css/components/component.native.css.map +1 -1
- package/assets/css/components/component.reset.css +1 -1
- package/assets/css/components/component.reset.css.map +1 -1
- package/assets/css/core.min.css +1 -1
- package/assets/css/core.min.css.map +1 -1
- package/assets/css/style.min.css +1 -1
- package/assets/css/style.min.css.map +1 -1
- package/assets/js/components/accordion/accordion.component.min.js +1 -1
- package/assets/js/components/actionbar/actionbar.component.min.js +1 -1
- package/assets/js/components/address-lookup/address-lookup.component.js +5 -4
- package/assets/js/components/address-lookup/address-lookup.component.min.js +4 -4
- package/assets/js/components/address-lookup/address-lookup.component.min.js.map +1 -1
- package/assets/js/components/applied-filters/applied-filters.component.min.js +1 -1
- package/assets/js/components/card/card.component.min.js +1 -1
- package/assets/js/components/collapsible-side/collapsible-side.component.min.js +1 -1
- package/assets/js/components/fileupload/fileupload.component.min.js +1 -1
- package/assets/js/components/filterlist/filterlist.component.min.js +1 -1
- package/assets/js/components/header/header.component.min.js +1 -1
- package/assets/js/components/nav/nav.component.min.js +1 -1
- package/assets/js/components/notification/notification.component.min.js +1 -1
- package/assets/js/components/pagination/pagination.component.min.js +1 -1
- package/assets/js/components/search/search.component.js +29 -15
- package/assets/js/components/search/search.component.min.js +13 -7
- package/assets/js/components/search/search.component.min.js.map +1 -1
- package/assets/js/components/table/table.component.js +1 -0
- package/assets/js/components/table/table.component.min.js +2 -2
- package/assets/js/components/table/table.component.min.js.map +1 -1
- package/assets/js/components/tabs/tabs.component.min.js +1 -1
- package/assets/js/dynamic.min.js +5 -4
- package/assets/js/dynamic.min.js.map +1 -1
- package/assets/js/modules/helpers.js +5 -1
- package/assets/js/modules/inputs.js +61 -0
- package/assets/js/scripts.bundle.js +32 -31
- package/assets/js/scripts.bundle.js.map +1 -1
- package/assets/js/scripts.bundle.min.js +2 -2
- package/assets/js/scripts.bundle.min.js.map +1 -1
- package/assets/js/vendor/hibp.js +78 -0
- package/assets/sass/_func.scss +1 -0
- package/assets/sass/components/component.native.scss +4 -0
- package/assets/sass/elements/admin-panel.scss +1 -0
- package/assets/sass/elements/dialog.scss +4 -4
- package/assets/sass/elements/forms.scss +35 -4
- package/assets/sass/helpers/max-height.scss +4 -2
- package/assets/ts/components/address-lookup/address-lookup.component.ts +6 -6
- package/assets/ts/components/search/search.component.ts +35 -19
- package/assets/ts/components/table/table.component.ts +1 -0
- package/assets/ts/modules/helpers.ts +6 -1
- package/assets/ts/modules/inputs.ts +84 -0
- package/assets/ts/vendor/hibp.ts +81 -0
- package/dist/components.es.js +13 -13
- package/dist/components.umd.js +17 -17
- package/package.json +1 -1
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* hibp.js
|
|
4
|
+
* @version v1
|
|
5
|
+
* @author Mehdi Bounya
|
|
6
|
+
*
|
|
7
|
+
* Report any bugs here: https://github.com/mehdibo/hibp-js
|
|
8
|
+
*
|
|
9
|
+
* The MIT License (http://www.opensource.org/licenses/mit-license.php)
|
|
10
|
+
*
|
|
11
|
+
* Permission is hereby granted, free of charge, to any person
|
|
12
|
+
* obtaining a copy of this software and associated documentation
|
|
13
|
+
* files (the "Software"), to deal in the Software without
|
|
14
|
+
* restriction, including without limitation the rights to use,
|
|
15
|
+
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
16
|
+
* copies of the Software, and to permit persons to whom the
|
|
17
|
+
* Software is furnished to do so, subject to the following
|
|
18
|
+
* conditions:
|
|
19
|
+
*
|
|
20
|
+
* The above copyright notice and this permission notice shall be
|
|
21
|
+
* included in all copies or substantial portions of the Software.
|
|
22
|
+
*
|
|
23
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
24
|
+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
25
|
+
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
26
|
+
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
27
|
+
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
28
|
+
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
29
|
+
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
30
|
+
* OTHER DEALINGS IN THE SOFTWARE.
|
|
31
|
+
*/
|
|
32
|
+
function sha1(string) {
|
|
33
|
+
var buffer = new TextEncoder("utf-8").encode(string);
|
|
34
|
+
return crypto.subtle.digest("SHA-1", buffer).then(function (buffer) {
|
|
35
|
+
// Get the hex code
|
|
36
|
+
var hexCodes = [];
|
|
37
|
+
var view = new DataView(buffer);
|
|
38
|
+
for (var i = 0; i < view.byteLength; i += 4) {
|
|
39
|
+
// Using getUint32 reduces the number of iterations needed (we process 4 bytes each time)
|
|
40
|
+
var value = view.getUint32(i);
|
|
41
|
+
// toString(16) will give the hex representation of the number without padding
|
|
42
|
+
var stringValue = value.toString(16);
|
|
43
|
+
// We use concatenation and slice for padding
|
|
44
|
+
var padding = '00000000';
|
|
45
|
+
var paddedValue = (padding + stringValue).slice(-padding.length);
|
|
46
|
+
hexCodes.push(paddedValue);
|
|
47
|
+
}
|
|
48
|
+
// Join all the hex strings into one
|
|
49
|
+
return hexCodes.join("");
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
const hibpCheck = (pwd, input) => {
|
|
53
|
+
// We hash the pwd first
|
|
54
|
+
sha1(pwd).then(function (hash) {
|
|
55
|
+
// We send the first 5 chars of the hash to hibp's API
|
|
56
|
+
const req = new XMLHttpRequest();
|
|
57
|
+
req.addEventListener("load", function () {
|
|
58
|
+
// When we get back a response from the server
|
|
59
|
+
// We create an array of lines and loop through them
|
|
60
|
+
const resp = this.responseText.split('\n');
|
|
61
|
+
const hashSub = hash.slice(5).toUpperCase();
|
|
62
|
+
var result = false;
|
|
63
|
+
for (let index in resp) {
|
|
64
|
+
// Check if the line matches the rest of the hash
|
|
65
|
+
if (resp[index].substring(0, 35) == hashSub) {
|
|
66
|
+
result = true;
|
|
67
|
+
break; // If found no need to continue the loop
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Trigger an event with the result
|
|
71
|
+
const event = new CustomEvent('hibpCheck', { detail: result });
|
|
72
|
+
input.dispatchEvent(event);
|
|
73
|
+
});
|
|
74
|
+
req.open('GET', 'https://api.pwnedpasswords.com/range/' + hash.substr(0, 5));
|
|
75
|
+
req.send();
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
export default hibpCheck;
|
package/assets/sass/_func.scss
CHANGED
|
@@ -178,7 +178,7 @@ dialog::backdrop {
|
|
|
178
178
|
// #endregion
|
|
179
179
|
|
|
180
180
|
// #region Transactional
|
|
181
|
-
*:not(.dialog__wrapper) > dialog[open]:not(.dialog--multi):is(:has(button:last-child),:has(form:last-child > button:last-child)) {
|
|
181
|
+
*:not(.dialog__wrapper) > dialog[open]:not(.dialog--multi):is(:has(:is(button,a):last-child),:has(form:last-child > :is(button,a):last-child)):not(:has(.youtube-embed)) {
|
|
182
182
|
|
|
183
183
|
text-align: center;
|
|
184
184
|
|
|
@@ -252,14 +252,14 @@ dialog::backdrop {
|
|
|
252
252
|
}
|
|
253
253
|
}
|
|
254
254
|
|
|
255
|
-
button {
|
|
255
|
+
form > :is(button,a) {
|
|
256
256
|
margin-bottom: 0;
|
|
257
257
|
}
|
|
258
258
|
|
|
259
259
|
@include media-breakpoint-up(sm) {
|
|
260
260
|
|
|
261
|
-
button:has(
|
|
262
|
-
button:last-child {
|
|
261
|
+
:is(button, .btn):has(~ :is(button, .btn):last-child),
|
|
262
|
+
:is(button, .btn):last-child {
|
|
263
263
|
min-width: calc(50% - 1rem);
|
|
264
264
|
text-align: center;
|
|
265
265
|
}
|
|
@@ -12,11 +12,23 @@ $icon-tick: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' view
|
|
|
12
12
|
font-weight: normal;
|
|
13
13
|
color: var(--colour-heading);
|
|
14
14
|
display: block;
|
|
15
|
+
}
|
|
16
|
+
// #endregion
|
|
17
|
+
|
|
18
|
+
// #region Optional text
|
|
19
|
+
:is(label,.label) {
|
|
15
20
|
|
|
16
|
-
|
|
21
|
+
@if $optionalText == "true" {
|
|
22
|
+
&:has(+ input:not(:disabled):not([readonly]):not(:required):not([type='radio']):not([type='checkbox']):not([type='file'])):after {
|
|
23
|
+
content: " (Optional)";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
&:has(input:not(:disabled):not([readonly]):not(:required):not([type='radio']):not([type='checkbox']):not([type='file'])) .optional-text:before {
|
|
17
28
|
content: " (Optional)";
|
|
18
29
|
}
|
|
19
30
|
}
|
|
31
|
+
|
|
20
32
|
// #endregion
|
|
21
33
|
|
|
22
34
|
// #region input field
|
|
@@ -375,7 +387,7 @@ input[maxlength] + span {
|
|
|
375
387
|
border-color: var(--colour-complete);
|
|
376
388
|
}
|
|
377
389
|
|
|
378
|
-
.was-validated input:is(:valid, .is-valid) {
|
|
390
|
+
.was-validated input:is(:valid, .is-valid):not(:is(:invalid, .is-invalid)) {
|
|
379
391
|
|
|
380
392
|
background-image: escape-svg($icon-tick);
|
|
381
393
|
background-repeat: no-repeat;
|
|
@@ -410,6 +422,25 @@ iam-address-lookup:has([required]){
|
|
|
410
422
|
--error-display: block;
|
|
411
423
|
}
|
|
412
424
|
|
|
425
|
+
// Password checker
|
|
426
|
+
.pwd-checker {
|
|
427
|
+
|
|
428
|
+
display: block;
|
|
429
|
+
background-repeat: no-repeat!important;
|
|
430
|
+
background-position: left center;
|
|
431
|
+
background-size: 1em 1em;
|
|
432
|
+
padding-left: 1.5rem!important;
|
|
433
|
+
}
|
|
434
|
+
.pwd-checker.invalid-feedback {
|
|
435
|
+
background-image: escape-svg($icon-error);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
.pwd-checker:not(.invalid-feedback) {
|
|
439
|
+
background-image: escape-svg($icon-tick)!important;
|
|
440
|
+
}
|
|
441
|
+
label:has(.pwd-checker.invalid-feedback):after {
|
|
442
|
+
display: none!important;
|
|
443
|
+
}
|
|
413
444
|
// #endregion
|
|
414
445
|
|
|
415
446
|
// #region radio/checkbox field
|
|
@@ -868,7 +899,7 @@ iam-fileupload {
|
|
|
868
899
|
|
|
869
900
|
margin-bottom: 1.5rem;
|
|
870
901
|
|
|
871
|
-
&:has(:invalid):after {
|
|
902
|
+
&:has(:is(:invalid,.is-invalid)):after {
|
|
872
903
|
content: "This field is required";
|
|
873
904
|
color: var(--colour-danger);
|
|
874
905
|
margin-top: rem(8);
|
|
@@ -883,7 +914,7 @@ iam-fileupload {
|
|
|
883
914
|
line-height: 1.2;
|
|
884
915
|
}
|
|
885
916
|
|
|
886
|
-
&[data-error]:has(:invalid):after {
|
|
917
|
+
&[data-error]:has(:is(:invalid,.is-invalid)):after {
|
|
887
918
|
content: attr(data-error)!important;
|
|
888
919
|
}
|
|
889
920
|
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
:is(.mh-sm,.mh-md,.mh-lg,.mh-sm-sm,.mh-sm-md,.mh-sm-lg,.mh-md-sm,.mh-md-md,.mh-md-lg){
|
|
2
2
|
|
|
3
|
+
&:not(iam-table):not(.table__wrapper){
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
padding-inline: var(--mh-padding-inline,0);
|
|
6
|
+
margin-inline: calc(var(--mh-padding-inline,0) * -1);
|
|
7
|
+
}
|
|
6
8
|
|
|
7
9
|
&::before {
|
|
8
10
|
top: calc(100% - 1.5rem);
|
|
@@ -84,15 +84,15 @@ class iamAddressLookup extends HTMLElement {
|
|
|
84
84
|
preFilledAddress.innerHTML = "";
|
|
85
85
|
|
|
86
86
|
Array.from(component.querySelectorAll('input[required],input[data-required],select[required],select[data-required]')).forEach((input, index) => {
|
|
87
|
+
const value = input.value;
|
|
87
88
|
|
|
88
|
-
if(!
|
|
89
|
+
if(!value)
|
|
89
90
|
preFilled = false;
|
|
90
91
|
else
|
|
91
|
-
preFilledAddress.innerHTML += ', '
|
|
92
|
-
|
|
92
|
+
preFilledAddress.innerHTML += value+(/^-?\d+$/.test(value) ? ' ' : ', ');
|
|
93
93
|
});
|
|
94
94
|
|
|
95
|
-
preFilledAddress.innerHTML = preFilledAddress.innerHTML.slice(
|
|
95
|
+
preFilledAddress.innerHTML = preFilledAddress.innerHTML.slice(0, -2);
|
|
96
96
|
|
|
97
97
|
if(preFilled){
|
|
98
98
|
preFilledWrapper.classList.remove('js-hide');
|
|
@@ -266,9 +266,9 @@ class iamAddressLookup extends HTMLElement {
|
|
|
266
266
|
if(key == "address_number_name")
|
|
267
267
|
itemString += `${value} `;
|
|
268
268
|
else if(key != "postcode" && key != "address_title")
|
|
269
|
-
itemString += `${value}, `;
|
|
269
|
+
itemString += `${value}${(/^-?\d+$/.test(value)?'':',')} `;
|
|
270
270
|
}
|
|
271
|
-
|
|
271
|
+
|
|
272
272
|
listString += `<option value="${itemString}${postcode}" data-values='${values}'></option>`;
|
|
273
273
|
}
|
|
274
274
|
|
|
@@ -22,7 +22,13 @@ class iamSearch extends HTMLElement {
|
|
|
22
22
|
template.innerHTML = `
|
|
23
23
|
<style>
|
|
24
24
|
@import "${coreCSS}";
|
|
25
|
-
|
|
25
|
+
input {
|
|
26
|
+
background: red;
|
|
27
|
+
}
|
|
28
|
+
input:not(.is-invalid):not(:invalid) {
|
|
29
|
+
background: none!important;
|
|
30
|
+
}
|
|
31
|
+
|
|
26
32
|
</style>
|
|
27
33
|
<link rel="stylesheet" href="https://kit.fontawesome.com/26fdbf0179.css" crossorigin="anonymous" />
|
|
28
34
|
<slot></slot>
|
|
@@ -62,36 +68,43 @@ class iamSearch extends HTMLElement {
|
|
|
62
68
|
displayInputField.setAttribute('list',listID);
|
|
63
69
|
}
|
|
64
70
|
|
|
65
|
-
// on change update oringinal field with the actual value and use displayed input for the nice display text
|
|
66
|
-
displayInputField.addEventListener('change', (event) => {
|
|
67
|
-
|
|
68
|
-
let match = datalist.querySelector(`option[value="${displayInputField.value}"]`);
|
|
69
|
-
|
|
70
|
-
if(match){
|
|
71
|
-
inputField.value = match.getAttribute('data-value');
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
|
|
75
71
|
// Search the endpoint when 3 characters has been added
|
|
76
72
|
if(searchWrapper.hasAttribute('data-url')){
|
|
77
73
|
|
|
78
|
-
displayInputField.addEventListener('
|
|
74
|
+
displayInputField.addEventListener('input', (event) => {
|
|
79
75
|
|
|
80
76
|
if(displayInputField.value.length == 3 && !searched.includes(displayInputField.value)){
|
|
81
77
|
search(displayInputField.value);
|
|
82
78
|
searched.push(displayInputField.value);
|
|
83
79
|
}
|
|
84
80
|
});
|
|
81
|
+
}
|
|
85
82
|
|
|
86
|
-
|
|
83
|
+
function checkMatch(){
|
|
84
|
+
|
|
85
|
+
let match = datalist.querySelector(`option[value="${displayInputField.value}"]`);
|
|
86
|
+
let subMatch = datalist.querySelector(`option[value*='${displayInputField.value}' i]`);
|
|
87
87
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
88
|
+
if(match){
|
|
89
|
+
inputField.value = match.getAttribute('data-value');
|
|
90
|
+
}
|
|
91
|
+
else if (displayInputField.value.length > 0 && !subMatch){
|
|
92
|
+
displayInputField.classList.add('is-invalid');
|
|
93
|
+
displayInputField.closest('label').setAttribute('data-error','No results returned');
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
displayInputField.classList.remove('is-invalid');
|
|
97
|
+
displayInputField.closest('label').removeAttribute('data-error');
|
|
98
|
+
}
|
|
93
99
|
}
|
|
94
|
-
|
|
100
|
+
|
|
101
|
+
// on change update oringinal field with the actual value and use displayed input for the nice display text
|
|
102
|
+
displayInputField.addEventListener('input', (event) => {
|
|
103
|
+
|
|
104
|
+
checkMatch();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
|
|
95
108
|
const search = async (searchterm) => {
|
|
96
109
|
|
|
97
110
|
let ajaxURL = searchWrapper.getAttribute('data-url');
|
|
@@ -154,6 +167,9 @@ class iamSearch extends HTMLElement {
|
|
|
154
167
|
|
|
155
168
|
datalist.innerHTML += listString;
|
|
156
169
|
|
|
170
|
+
displayInputField.closest('form').classList.add('was-validated');
|
|
171
|
+
checkMatch();
|
|
172
|
+
|
|
157
173
|
return response;
|
|
158
174
|
});
|
|
159
175
|
|
|
@@ -65,6 +65,7 @@ class iamTable extends HTMLElement {
|
|
|
65
65
|
|
|
66
66
|
classList = classList.replace('table--cta','');
|
|
67
67
|
classList = classList.replace('table--loading','');
|
|
68
|
+
classList = classList.replace('mh-md','');
|
|
68
69
|
this.shadowRoot.querySelector('.table__wrapper').className += ` ${classList}`;
|
|
69
70
|
|
|
70
71
|
// set actionbar class if needed
|
|
@@ -66,7 +66,12 @@ export const addGlobalEvents = (body) => {
|
|
|
66
66
|
|
|
67
67
|
let form = event.target;
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
// Reset password types
|
|
70
|
+
Array.from(form.querySelectorAll('[data-password-type]')).forEach((input,index) => {
|
|
71
|
+
input.setAttribute('type','password');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
if(form.querySelector(':invalid') || form.querySelector('.pwd-checker[data-strength="1"]') || form.querySelector('.pwd-checker[data-strength="2"]')){
|
|
70
75
|
|
|
71
76
|
form.classList.add('was-validated');
|
|
72
77
|
event.preventDefault();
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
|
+
import hibpCheck from '../vendor/hibp'
|
|
3
|
+
|
|
2
4
|
const extendInputs = (body) => {
|
|
3
5
|
|
|
4
6
|
function loadInput(){
|
|
@@ -8,6 +10,11 @@ const extendInputs = (body) => {
|
|
|
8
10
|
setMaxlengthVars(input,wrapper);
|
|
9
11
|
});
|
|
10
12
|
|
|
13
|
+
Array.from(document.querySelectorAll('label input')).forEach((input,index) => {
|
|
14
|
+
if(!input.closest('label').querySelector('.optional-text') && !input.hasAttribute('required'))
|
|
15
|
+
input.insertAdjacentHTML("beforebegin", `<span class="optional-text"></span>`);
|
|
16
|
+
});
|
|
17
|
+
|
|
11
18
|
// Date restrictions
|
|
12
19
|
if(document.querySelector('input[type="date"]')){
|
|
13
20
|
|
|
@@ -79,6 +86,10 @@ const extendInputs = (body) => {
|
|
|
79
86
|
|
|
80
87
|
if(input.hasAttribute('maxlength') && input.nextElementSibling)
|
|
81
88
|
input.nextElementSibling.setAttribute("data-count", input.value.length);
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
if(input.hasAttribute('data-strength-checker'))
|
|
92
|
+
checkPWDStrength(input);
|
|
82
93
|
}
|
|
83
94
|
});
|
|
84
95
|
|
|
@@ -151,7 +162,80 @@ export const setMaxlengthVars = (input) => {
|
|
|
151
162
|
|
|
152
163
|
export const changeType = (input,type) => {
|
|
153
164
|
|
|
165
|
+
if(input.hasAttribute('type') && input.getAttribute('type') == 'password')
|
|
166
|
+
input.setAttribute('data-password-type',true);
|
|
167
|
+
|
|
154
168
|
input.setAttribute('type',type);
|
|
155
169
|
}
|
|
156
170
|
|
|
171
|
+
export const checkPWDStrength = (input, check = 'no') => {
|
|
172
|
+
|
|
173
|
+
const pwdChecker = document.getElementById(input.getAttribute('data-strength-checker'))
|
|
174
|
+
const password = input.value;
|
|
175
|
+
const minChars = input.hasAttribute('minlength') ? input.getAttribute('minlength') : 12;
|
|
176
|
+
|
|
177
|
+
let strength = 1;
|
|
178
|
+
let strengthName = ['Very weak', 'Weak', 'Average', 'Strong', 'Very strong'];
|
|
179
|
+
let extraMsg = '';
|
|
180
|
+
|
|
181
|
+
//has number
|
|
182
|
+
if (password.match(/(?=.*[0-9])/))
|
|
183
|
+
strength += 1;
|
|
184
|
+
// has special character
|
|
185
|
+
if (password.match(/(?=.*[!,%,&,#,$,^,*,?,_,~,<,>,])/))
|
|
186
|
+
strength += 1;
|
|
187
|
+
// has lowercase alpha
|
|
188
|
+
if (password.match(/(?=.*[a-z])/))
|
|
189
|
+
strength += 1;
|
|
190
|
+
// has uppercase alpha
|
|
191
|
+
if (password.match(/(?=.*[A-Z])/))
|
|
192
|
+
strength += 1;
|
|
193
|
+
|
|
194
|
+
if (password.length < minChars){
|
|
195
|
+
strength = 1;
|
|
196
|
+
extraMsg = `(must be at least ${minChars} characters.)`;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
// if the strength is above weak and above the minimum length do some kind of api call to check if its in a list of passwords
|
|
201
|
+
|
|
202
|
+
if(strength >= 3 && check == 'no'){
|
|
203
|
+
hibpCheck(password,input);
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
input.addEventListener('hibpCheck', function (event) {
|
|
207
|
+
checkhibpCheck(event, input)
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
function checkhibpCheck(event, input){
|
|
211
|
+
|
|
212
|
+
if(event.detail){ // found
|
|
213
|
+
checkPWDStrength(input,'danger');
|
|
214
|
+
} else { // not found
|
|
215
|
+
checkPWDStrength(input,'success');
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
input.removeEventListener("hibpCheck", checkhibpCheck); // Succeeds
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
}
|
|
222
|
+
else if(strength >= 3 && check == 'danger'){
|
|
223
|
+
strength = 3;
|
|
224
|
+
extraMsg = `(this password is very common)`;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
if(pwdChecker){
|
|
229
|
+
if(strength <= 3)
|
|
230
|
+
pwdChecker.classList.add('invalid-feedback');
|
|
231
|
+
else
|
|
232
|
+
pwdChecker.classList.remove('invalid-feedback');
|
|
233
|
+
|
|
234
|
+
pwdChecker.setAttribute('data-strength',strength)
|
|
235
|
+
pwdChecker.innerHTML = `Password strength: ${strengthName[strength-1]} ${extraMsg}`;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
|
|
157
241
|
export default extendInputs;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* hibp.js
|
|
5
|
+
* @version v1
|
|
6
|
+
* @author Mehdi Bounya
|
|
7
|
+
*
|
|
8
|
+
* Report any bugs here: https://github.com/mehdibo/hibp-js
|
|
9
|
+
*
|
|
10
|
+
* The MIT License (http://www.opensource.org/licenses/mit-license.php)
|
|
11
|
+
*
|
|
12
|
+
* Permission is hereby granted, free of charge, to any person
|
|
13
|
+
* obtaining a copy of this software and associated documentation
|
|
14
|
+
* files (the "Software"), to deal in the Software without
|
|
15
|
+
* restriction, including without limitation the rights to use,
|
|
16
|
+
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
17
|
+
* copies of the Software, and to permit persons to whom the
|
|
18
|
+
* Software is furnished to do so, subject to the following
|
|
19
|
+
* conditions:
|
|
20
|
+
*
|
|
21
|
+
* The above copyright notice and this permission notice shall be
|
|
22
|
+
* included in all copies or substantial portions of the Software.
|
|
23
|
+
*
|
|
24
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
25
|
+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
26
|
+
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
27
|
+
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
28
|
+
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
29
|
+
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
30
|
+
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
31
|
+
* OTHER DEALINGS IN THE SOFTWARE.
|
|
32
|
+
*/
|
|
33
|
+
function sha1(string){
|
|
34
|
+
var buffer = new TextEncoder("utf-8").encode(string);
|
|
35
|
+
return crypto.subtle.digest("SHA-1", buffer).then(function (buffer) {
|
|
36
|
+
// Get the hex code
|
|
37
|
+
var hexCodes = [];
|
|
38
|
+
var view = new DataView(buffer);
|
|
39
|
+
for (var i = 0; i < view.byteLength; i += 4) {
|
|
40
|
+
// Using getUint32 reduces the number of iterations needed (we process 4 bytes each time)
|
|
41
|
+
var value = view.getUint32(i)
|
|
42
|
+
// toString(16) will give the hex representation of the number without padding
|
|
43
|
+
var stringValue = value.toString(16)
|
|
44
|
+
// We use concatenation and slice for padding
|
|
45
|
+
var padding = '00000000'
|
|
46
|
+
var paddedValue = (padding + stringValue).slice(-padding.length)
|
|
47
|
+
hexCodes.push(paddedValue);
|
|
48
|
+
}
|
|
49
|
+
// Join all the hex strings into one
|
|
50
|
+
return hexCodes.join("");
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const hibpCheck = (pwd,input) => {
|
|
55
|
+
// We hash the pwd first
|
|
56
|
+
sha1(pwd).then(function(hash){
|
|
57
|
+
// We send the first 5 chars of the hash to hibp's API
|
|
58
|
+
const req = new XMLHttpRequest();
|
|
59
|
+
req.addEventListener("load", function(){
|
|
60
|
+
// When we get back a response from the server
|
|
61
|
+
// We create an array of lines and loop through them
|
|
62
|
+
const resp = this.responseText.split('\n');
|
|
63
|
+
const hashSub = hash.slice(5).toUpperCase();
|
|
64
|
+
var result = false;
|
|
65
|
+
for(let index in resp){
|
|
66
|
+
// Check if the line matches the rest of the hash
|
|
67
|
+
if(resp[index].substring(0, 35) == hashSub){
|
|
68
|
+
result = true;
|
|
69
|
+
break; // If found no need to continue the loop
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// Trigger an event with the result
|
|
73
|
+
const event = new CustomEvent('hibpCheck', {detail: result});
|
|
74
|
+
input.dispatchEvent(event);
|
|
75
|
+
});
|
|
76
|
+
req.open('GET', 'https://api.pwnedpasswords.com/range/'+hash.substr(0, 5));
|
|
77
|
+
req.send();
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export default hibpCheck;
|