1class OtpInput { 2 constructor(container, options = {}) { 3 this.container = container; 4 this.length = options.length || parseInt(container.dataset.length) || 4; 5 this.nextFocusElement = options.nextFocusElement || null; 6 this.resendButtonElement = options.resendButtonElement || null; 7 this.displayElement = options.displayElement || null; 8 9 this.inputs = []; 10 this.init(); 11 } 12 13 init() { 14 this.container.innerHTML = ""; 15 this.container.classList.add("otp-container"); 16 17 for (let i = 0; i < this.length; i++) { 18 const input = document.createElement("input"); 19 input.id = `otpInput_${i}`; 20 input.type = "text"; 21 input.maxLength = 1; 22 input.autocomplete = "off"; 23 input.inputMode = "numeric"; 24 input.classList.add("otp-box"); 25 26 input.addEventListener("input", (e) => this.onInput(e, i)); 27 input.addEventListener("keydown", (e) => this.onKeyDown(e, i)); 28 input.addEventListener("paste", (e) => this.onPaste(e)); 29 input.addEventListener("blur", (e) => this.onBlur(e, i)); 30 //input.addEventListener("focus", () => {input.select()}); uncomment if we would like to each element would be editable on focus 31 32 this.container.appendChild(input); 33 this.inputs.push(input); 34 } 35 36 this.inputs[0].focus(); 37 } 38 39 onInput(e, index) { 40 const value = e.target.value.replace(/\D/g, ""); 41 e.target.value = value; 42 43 if (!value) return; 44 45 if (index < this.length - 1) { 46 this.inputs[index + 1].focus(); 47 this.inputs[index + 1].select(); 48 } else { 49 this.blurAll(); 50 51 if (this.nextFocusElement) { 52 this.nextFocusElement.focus(); 53 } 54 } 55 } 56 57 onKeyDown(e, index) { 58 if (e.key === "Backspace" && !this.inputs[index].value && index > 0) { 59 this.inputs[index - 1].focus(); 60 } 61 } 62 63 onPaste(e) { 64 e.preventDefault(); 65 66 const text = (e.clipboardData || window.clipboardData).getData("text"); 67 const digits = text.replace(/\D/g, "").slice(0, this.length); 68 69 digits.split("").forEach((digit, i) => { 70 this.inputs[i].value = digit; 71 this.inputs[i].classList.remove("invalid"); 72 }); 73 74 if (digits.length === this.length && this.nextFocusElement) { 75 this.nextFocusElement.focus(); 76 } 77 } 78 79 onBlur(e, index) { 80 const value = e.target.value; 81 82 if (value) { 83 e.target.classList.remove("invalid"); 84 } else { 85 e.target.classList.add("invalid"); 86 } 87 } 88 89 blurAll() { 90 this.inputs.forEach(i => i.blur()); 91 } 92 93 getValue() { 94 return this.inputs.map(i => i.value).join(""); 95 } 96 97 isValid() { 98 let allValid = true; 99 100 this.inputs.forEach(input => { 101 const val = input.value; 102 const isDigit = /^\d$/.test(val); 103 104 if (isDigit) { 105 input.classList.remove("invalid"); 106 } else { 107 input.classList.add("invalid"); 108 allValid = false; 109 } 110 }); 111 112 return allValid; 113 } 114 115 clear() { 116 this.inputs.forEach(i => i.value = ""); 117 this.inputs[0].focus(); 118 } 119 120 startTimer(duration) { 121 var timer = duration, minutes, seconds; 122 var IntervalID = setInterval(() => { 123 minutes = parseInt(timer / 60, 10) 124 seconds = parseInt(timer % 60, 10); 125 126 minutes = minutes < 10 ? "0" + minutes : minutes; 127 seconds = seconds < 10 ? "0" + seconds : seconds; 128 129 //display.textContent = minutes + ":" + seconds; 130 this.displayElement.textContent = seconds; 131 132 if (--timer < 0) { 133 //timer = duration; 134 clearInterval(IntervalID); 135 this.resendButtonElement.removeAttribute('disabled'); 136 this.resendButtonElement.classList.remove("disabled"); 137 } 138 }, 1000); 139 } 140 141 resendClicked() { 142 this.resendButtonElement.setAttribute('disabled', 'disabled'); 143 this.resendButtonElement.classList.add("disabled"); 144 var oneMinutes = 59; 145 this.inputs[0].focus(); 146 this.inputs[0].select(); 147 startTimer(oneMinutes); 148 } 149} 150 151window.OtpInput = OtpInput;
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.