GUI: unlock/changepassword page update + Various fixes. (#313)

* Better unlock page + Various fixes.

* Better changepassword page + Various fixes.

* Small styling fix.
This commit is contained in:
Gerlof van Ek
2025-06-13 12:10:32 +02:00
committed by GitHub
parent aa9b1c0eb9
commit 4055b7d6c8
2 changed files with 629 additions and 331 deletions

View File

@@ -1,185 +1,447 @@
{% include 'header.html' %} {% include 'header.html' %}
{% from 'style.html' import breadcrumb_line_svg %} {% from 'style.html' import breadcrumb_line_svg %}
<div class="container mx-auto"> <div class="container mx-auto">
<section class="p-5 mt-5"> <!-- Breadcrumb -->
<div class="flex flex-wrap items-center -m-2"> <section class="p-5 mt-5">
<div class="w-full md:w-1/2 p-2"> <div class="flex flex-wrap items-center -m-2">
<ul class="flex flex-wrap items-center gap-x-3 mb-2"> <div class="w-full md:w-1/2 p-2">
<li>{{ breadcrumb_line_svg | safe }}</li> <ul class="flex flex-wrap items-center gap-x-3 mb-2">
<li> <li>{{ breadcrumb_line_svg | safe }}</li>
<a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/"> <li>
<p>Home</p> <a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/">
</a> <p>Home</p>
</li> </a>
<li>{{ breadcrumb_line_svg | safe }}</li> </li>
<li> <li>{{ breadcrumb_line_svg | safe }}</li>
<a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/changepassword">Change Password</a> <li>
</li> <a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/changepassword">Change Password</a>
<li> </li>
<svg width="6" height="15" viewBox="0 0 6 15" fill="none" xmlns="http://www.w3.org/2000/svg"> <li>
<path d="M5.34 0.671999L2.076 14.1H0.732L3.984 0.671999H5.34Z" fill="#BBC3CF"></path> <svg width="6" height="15" viewBox="0 0 6 15" fill="none" xmlns="http://www.w3.org/2000/svg">
</svg> <path d="M5.34 0.671999L2.076 14.1H0.732L3.984 0.671999H5.34Z" fill="#BBC3CF"></path>
</li> </svg>
</ul> </li>
</div> </ul>
</div>
</section>
<section class="py-4">
<div class="container px-4 mx-auto">
<div class="relative py-11 px-16 bg-coolGray-900 dark:bg-blue-500 rounded-md overflow-hidden">
<img class="absolute z-10 left-4 top-4" src="/static/images/elements/dots-red.svg" alt="">
<img class="absolute z-10 right-4 bottom-4" src="/static/images/elements/dots-red.svg" alt="">
<img class="absolute h-64 left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 object-cover" src="/static/images/elements/wave.svg" alt="">
<div class="relative z-20 flex flex-wrap items-center -m-3">
<div class="w-full md:w-1/2 p-3">
<h2 class="mb-6 text-4xl font-bold text-white tracking-tighter">Change/Set your Password</h2>
<p class="font-normal text-coolGray-200 dark:text-white">Change or Set your BasicSwap / Wallets password.</p>
</div>
</div>
</div>
</div>
</section>
{% include 'inc_messages.html' %}
<section>
<div class="pl-6 pr-6 pt-0 pb-0 h-full overflow-hidden">
<div class="border-coolGray-100">
<div class="flex flex-wrap items-center justify-between -m-2">
<div class="w-full pt-2">
<div class="container mt-5 mx-auto">
<div class="pt-6 pb-8 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
<div class="px-6">
<div class="w-full mt-6 pb-6 overflow-x-auto">
<table class="w-full min-w-max text-sm">
<thead class="uppercase">
<tr class="text-left">
<th class="p-0">
<div class="py-3 px-6 rounded-tl-xl bg-coolGray-200 dark:bg-gray-600">
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Password</span>
</div>
</th>
<th class="p-0">
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold py-3 px-6"></span>
</div>
</th>
</tr>
</thead>
<form method="post" autocomplete="off">
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
<td class="py-3 px-6 bold">Old Password</td>
<td td class="py-3 px-6">
<div class="relative w-full">
<div class="absolute inset-y-0 right-0 flex items-center px-2">
<input class="hidden js-password-toggle" id="toggle-old" type="checkbox" />
<label class="px-2 py-1 text-sm text-gray-600 font-mono cursor-pointer js-password-label" for="toggle-old" id="input-old-label">
<svg xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24">
<g fill="#8896ab">
<path d="M23.444,10.239C21.905,8.062,17.708,3,12,3S2.1,8.062.555,10.24a3.058,3.058,0,0,0,0,3.52h0C2.1,15.938,6.292,21,12,21s9.905-5.062,11.445-7.24A3.058,3.058,0,0,0,23.444,10.239ZM12,17a5,5,0,1,1,5-5A5,5,0,0,1,12,17Z" fill="#8896ab"></path>
</g>
</svg>
</label>
</div>
<input class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" type="password" name="oldpassword" id="input-old">
</div>
</td>
</tr>
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
<td class="py-3 px-6 bold">New Password</td>
<td td class="py-3 px-6">
<div class="relative w-full">
<div class="absolute inset-y-0 right-0 flex items-center px-2">
<input class="hidden js-password-toggle" id="toggle-new" type="checkbox" />
<label class="px-2 py-1 text-sm text-gray-600 font-mono cursor-pointer js-password-label" for="toggle-new" id="input-new-label">
<svg xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24">
<g fill="#8896ab">
<path d="M23.444,10.239C21.905,8.062,17.708,3,12,3S2.1,8.062.555,10.24a3.058,3.058,0,0,0,0,3.52h0C2.1,15.938,6.292,21,12,21s9.905-5.062,11.445-7.24A3.058,3.058,0,0,0,23.444,10.239ZM12,17a5,5,0,1,1,5-5A5,5,0,0,1,12,17Z" fill="#8896ab"></path>
</g>
</svg>
</label>
</div>
<input class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" type="password" name="newpassword" id="input-new">
</div>
</td>
</tr>
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
<td class="py-3 px-6 bold">Confirm Password</td>
<td td class="py-3 px-6">
<div class="relative w-full">
<div class="absolute inset-y-0 right-0 flex items-center px-2">
<input class="hidden js-password-toggle" id="toggle-conf" type="checkbox" />
<label class="px-2 py-1 text-sm text-gray-600 font-mono cursor-pointer js-password-label" for="toggle-conf" id="input-confirm-label">
<svg xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24">
<g fill="#8896ab">
<path d="M23.444,10.239C21.905,8.062,17.708,3,12,3S2.1,8.062.555,10.24a3.058,3.058,0,0,0,0,3.52h0C2.1,15.938,6.292,21,12,21s9.905-5.062,11.445-7.24A3.058,3.058,0,0,0,23.444,10.239ZM12,17a5,5,0,1,1,5-5A5,5,0,0,1,12,17Z" fill="#8896ab"></path>
</g>
</svg>
</label>
</div>
<input class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" type="password" name="confirmpassword" id="input-confirm">
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
</div> </div>
</div>
</div> </div>
</div> </section>
</div>
</section> <section class="py-4">
<section> <div class="container px-4 mx-auto">
<div class="pl-6 pr-6 pt-0 pb-0 h-full overflow-hidden "> <div class="relative py-11 px-16 bg-coolGray-900 dark:bg-blue-500 rounded-md overflow-hidden">
<div class="pb-6 "> <img class="absolute z-10 left-4 top-4" src="/static/images/elements/dots-red.svg" alt="">
<div class="flex flex-wrap items-center justify-between -m-2"> <img class="absolute z-10 right-4 bottom-4" src="/static/images/elements/dots-red.svg" alt="">
<div class="w-full pt-2"> <img class="absolute h-64 left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 object-cover" src="/static/images/elements/wave.svg" alt="">
<div class="container mx-auto"> <div class="relative z-20 flex flex-wrap items-center -m-3">
<div class="pt-6 pb-6 bg-coolGray-100 border-t border-gray-100 dark:border-gray-400 dark:bg-gray-500 rounded-bl-xl rounded-br-xl"> <div class="w-full md:w-1/2 p-3">
<div class="px-6"> <h2 class="mb-6 text-4xl font-bold text-white tracking-tighter">Change/Set your Password</h2>
<div class="flex flex-wrap justify-end"> <p class="font-normal text-coolGray-200 dark:text-white">Change or Set your BasicSwap / Wallets password.</p>
<div class="w-full md:w-auto p-1.5 ml-2">
<button type="submit" name="unlock" value="Unlock" class="flex flex-wrap justify-center w-full px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">Apply</button>
</div> </div>
</div>
</div> </div>
</div>
</div> </div>
</div>
</div> </div>
</div> </section>
</div>
</section> {% include 'inc_messages.html' %}
<input type="hidden" name="formid" value="{{ form_id }}"> <section>
</form> <div class="pl-6 pr-6 pt-0 pb-0 h-full overflow-hidden">
<div class="border-coolGray-100">
<div class="flex flex-wrap items-center justify-between -m-2">
<div class="w-full pt-2">
<div class="container mt-5 mx-auto">
<div class="pt-6 pb-8 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
<div class="px-6">
<form method="post" autocomplete="off" id="change-password-form">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
<div class="space-y-6">
<div>
<label for="oldpassword" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Current Password
</label>
<div class="relative">
<input
type="password"
id="oldpassword"
name="oldpassword"
class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0"
placeholder="Enter your current password"
required
/>
<button
type="button"
id="toggle-old-password"
class="absolute inset-y-0 right-0 flex items-center pr-3 text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300 transition-colors"
aria-label="Toggle password visibility"
>
<svg id="eye-open-old" class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path>
</svg>
<svg id="eye-closed-old" class="w-5 h-5 hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.878 9.878L3 3m6.878 6.878L21 21"></path>
</svg>
</button>
</div>
</div>
<div>
<label for="newpassword" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
New Password
</label>
<div class="relative">
<input
type="password"
id="newpassword"
name="newpassword"
class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0"
placeholder="Enter your new password"
required
/>
<button
type="button"
id="toggle-new-password"
class="absolute inset-y-0 right-0 flex items-center pr-3 text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300 transition-colors"
aria-label="Toggle password visibility"
>
<svg id="eye-open-new" class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path>
</svg>
<svg id="eye-closed-new" class="w-5 h-5 hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.878 9.878L3 3m6.878 6.878L21 21"></path>
</svg>
</button>
</div>
<div id="caps-warning-new" class="hidden mt-2 text-sm text-amber-700 dark:text-amber-300 flex items-center">
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd"></path>
</svg>
Caps Lock is on
</div>
<div class="mt-3">
<div class="flex items-center justify-between mb-2">
<span class="text-sm text-gray-600 dark:text-gray-400">Password Strength:</span>
<span id="strength-text" class="text-sm font-medium text-gray-500 dark:text-gray-400">Enter password</span>
</div>
<div class="w-full bg-gray-200 dark:bg-gray-600 rounded-full h-2">
<div id="strength-bar" class="h-2 rounded-full transition-all duration-300 bg-gray-300 dark:bg-gray-500" style="width: 0%"></div>
</div>
</div>
</div>
<div>
<label for="confirmpassword" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Confirm New Password
</label>
<div class="relative">
<input
type="password"
id="confirmpassword"
name="confirmpassword"
class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0"
placeholder="Confirm your new password"
required
/>
<button
type="button"
id="toggle-confirm-password"
class="absolute inset-y-0 right-0 flex items-center pr-3 text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300 transition-colors"
aria-label="Toggle password visibility"
>
<svg id="eye-open-confirm" class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path>
</svg>
<svg id="eye-closed-confirm" class="w-5 h-5 hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.878 9.878L3 3m6.878 6.878L21 21"></path>
</svg>
</button>
</div>
<div id="password-match" class="mt-2 text-sm hidden">
<div id="match-success" class="text-green-600 dark:text-green-400 flex items-center hidden">
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
</svg>
Passwords match
</div>
<div id="match-error" class="text-red-600 dark:text-red-400 flex items-center hidden">
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L10 10.586l1.293-1.293a1 1 0 001.414 1.414L10 13.414l-1.293-1.293a1 1 0 00-1.414-1.414z" clip-rule="evenodd"></path>
</svg>
Passwords do not match
</div>
</div>
</div>
</div>
<div class="space-y-6">
<div class="bg-gray-50 dark:bg-gray-600 rounded-lg p-6">
<h3 class="text-lg font-medium text-gray-900 dark:text-white mb-4">Password Requirements</h3>
<div class="space-y-3">
<div id="req-length" class="flex items-center text-gray-500 dark:text-gray-400">
<svg class="w-5 h-5 mr-3" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
</svg>
At least 8 characters
</div>
<div id="req-uppercase" class="flex items-center text-gray-500 dark:text-gray-400">
<svg class="w-5 h-5 mr-3" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
</svg>
Uppercase letter (A-Z)
</div>
<div id="req-lowercase" class="flex items-center text-gray-500 dark:text-gray-400">
<svg class="w-5 h-5 mr-3" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
</svg>
Lowercase letter (a-z)
</div>
<div id="req-number" class="flex items-center text-gray-500 dark:text-gray-400">
<svg class="w-5 h-5 mr-3" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
</svg>
Number (0-9)
</div>
</div>
</div>
</div>
</div>
<div class="mt-8 flex justify-end">
<button
type="submit"
id="submit-btn"
class="bg-blue-600 hover:bg-blue-700 disabled:bg-blue-400 text-white font-medium py-3 px-8 rounded-lg transition-colors focus:outline-none disabled:cursor-not-allowed"
>
<span id="submit-text">Change Password</span>
<svg id="submit-spinner" class="hidden animate-spin ml-2 h-5 w-5 text-white inline" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
</button>
</div>
<input type="hidden" name="formid" value="{{ form_id }}">
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</div> </div>
</section> </section>
{% include 'footer.html' %} <script>
<script> document.addEventListener('DOMContentLoaded', function() {
function togglePassword(event) { const oldPasswordInput = document.getElementById('oldpassword');
let input_name = 'input-new'; const newPasswordInput = document.getElementById('newpassword');
if (event.target.id == 'toggle-old') { const confirmPasswordInput = document.getElementById('confirmpassword');
input_name = 'input-old'; const form = document.getElementById('change-password-form');
} else const submitBtn = document.getElementById('submit-btn');
if (event.target.id == 'toggle-conf') { const submitText = document.getElementById('submit-text');
input_name = 'input-confirm'; const submitSpinner = document.getElementById('submit-spinner');
}
const password = document.getElementById(input_name),
passwordLabel = document.getElementById(input_name + '-label');
if (password.type === 'password') {
password.type = 'text';
passwordLabel.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24"><g fill="#8896ab"><path d="M23.444,10.239a22.936,22.936,0,0,0-2.492-2.948l-4.021,4.021A5.026,5.026,0,0,1,17,12a5,5,0,0,1-5,5,5.026,5.026,0,0,1-.688-.069L8.055,20.188A10.286,10.286,0,0,0,12,21c5.708,0,9.905-5.062,11.445-7.24A3.058,3.058,0,0,0,23.444,10.239Z" fill="#8896ab"></path><path d="M12,3C6.292,3,2.1,8.062.555,10.24a3.058,3.058,0,0,0,0,3.52h0a21.272,21.272,0,0,0,4.784,4.9l3.124-3.124a5,5,0,0,1,7.071-7.072L8.464,15.536l10.2-10.2A11.484,11.484,0,0,0,12,3Z" fill="#8896ab"></path><path data-color="color-2" d="M1,24a1,1,0,0,1-.707-1.707l22-22a1,1,0,0,1,1.414,1.414l-22,22A1,1,0,0,1,1,24Z"></path></g></svg>';
} else {
password.type = 'password';
passwordLabel.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24"><g fill="#8896ab" ><path d="M23.444,10.239C21.905,8.062,17.708,3,12,3S2.1,8.062.555,10.24a3.058,3.058,0,0,0,0,3.52h0C2.1,15.938,6.292,21,12,21s9.905-5.062,11.445-7.24A3.058,3.058,0,0,0,23.444,10.239ZM12,17a5,5,0,1,1,5-5A5,5,0,0,1,12,17Z" fill="#8896ab"></path></g></svg>';
}
password.focus();
}
const toggles = ["toggle-old", "toggle-new", "toggle-conf"] setupPasswordToggle('old');
toggles.forEach(function (toggle_id, index) { setupPasswordToggle('new');
document.getElementById(toggle_id).addEventListener('change', togglePassword, false); setupPasswordToggle('confirm');
});
</script> function setupPasswordToggle(type) {
</body> const toggleBtn = document.getElementById(`toggle-${type}-password`);
</html> const passwordInput = document.getElementById(`${type}password`);
const eyeOpen = document.getElementById(`eye-open-${type}`);
const eyeClosed = document.getElementById(`eye-closed-${type}`);
if (toggleBtn && passwordInput) {
toggleBtn.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
const isPassword = passwordInput.type === 'password';
const cursorPosition = passwordInput.selectionStart;
const inputValue = passwordInput.value;
passwordInput.type = isPassword ? 'text' : 'password';
passwordInput.value = inputValue;
setTimeout(() => {
passwordInput.setSelectionRange(cursorPosition, cursorPosition);
}, 0);
if (isPassword) {
eyeOpen.classList.add('hidden');
eyeClosed.classList.remove('hidden');
} else {
eyeOpen.classList.remove('hidden');
eyeClosed.classList.add('hidden');
}
});
toggleBtn.addEventListener('mousedown', function(e) {
e.preventDefault();
});
}
}
if (newPasswordInput) {
const capsWarning = document.getElementById('caps-warning-new');
newPasswordInput.addEventListener('keydown', function(e) {
const capsLockOn = e.getModifierState && e.getModifierState('CapsLock');
if (capsLockOn && capsWarning) {
capsWarning.classList.remove('hidden');
} else if (capsWarning) {
capsWarning.classList.add('hidden');
}
});
newPasswordInput.addEventListener('keyup', function(e) {
const capsLockOn = e.getModifierState && e.getModifierState('CapsLock');
if (!capsLockOn && capsWarning) {
capsWarning.classList.add('hidden');
}
});
}
function calculatePasswordStrength(password) {
let score = 0;
const requirements = {
length: password.length >= 8,
uppercase: /[A-Z]/.test(password),
lowercase: /[a-z]/.test(password),
number: /\d/.test(password)
};
if (requirements.length) score += 25;
if (requirements.uppercase) score += 25;
if (requirements.lowercase) score += 25;
if (requirements.number) score += 25;
if (/[!@#$%^&*(),.?":{}|<>]/.test(password)) score += 10;
if (password.length >= 12) score += 10;
return { score: Math.min(score, 100), requirements };
}
function updatePasswordStrength(password) {
const { score, requirements } = calculatePasswordStrength(password);
const strengthBar = document.getElementById('strength-bar');
const strengthText = document.getElementById('strength-text');
if (strengthBar) {
strengthBar.style.width = `${score}%`;
if (score === 0) {
strengthBar.className = 'h-2 rounded-full transition-all duration-300 bg-gray-300 dark:bg-gray-500';
strengthText.textContent = 'Enter password';
strengthText.className = 'text-sm font-medium text-gray-500 dark:text-gray-400';
} else if (score < 40) {
strengthBar.className = 'h-2 rounded-full transition-all duration-300 bg-red-500';
strengthText.textContent = 'Weak';
strengthText.className = 'text-sm font-medium text-red-600 dark:text-red-400';
} else if (score < 70) {
strengthBar.className = 'h-2 rounded-full transition-all duration-300 bg-yellow-500';
strengthText.textContent = 'Fair';
strengthText.className = 'text-sm font-medium text-yellow-600 dark:text-yellow-400';
} else if (score < 90) {
strengthBar.className = 'h-2 rounded-full transition-all duration-300 bg-blue-500';
strengthText.textContent = 'Good';
strengthText.className = 'text-sm font-medium text-blue-600 dark:text-blue-400';
} else {
strengthBar.className = 'h-2 rounded-full transition-all duration-300 bg-green-500';
strengthText.textContent = 'Strong';
strengthText.className = 'text-sm font-medium text-green-600 dark:text-green-400';
}
}
updateRequirement('length', requirements.length);
updateRequirement('uppercase', requirements.uppercase);
updateRequirement('lowercase', requirements.lowercase);
updateRequirement('number', requirements.number);
return score >= 60;
}
function updateRequirement(type, met) {
const element = document.getElementById(`req-${type}`);
if (element) {
if (met) {
element.className = 'flex items-center text-green-600 dark:text-green-400';
} else {
element.className = 'flex items-center text-gray-500 dark:text-gray-400';
}
}
}
function checkPasswordMatch() {
const newPassword = newPasswordInput.value;
const confirmPassword = confirmPasswordInput.value;
const matchContainer = document.getElementById('password-match');
const matchSuccess = document.getElementById('match-success');
const matchError = document.getElementById('match-error');
if (confirmPassword.length === 0) {
matchContainer.classList.add('hidden');
return false;
}
matchContainer.classList.remove('hidden');
if (newPassword === confirmPassword) {
matchSuccess.classList.remove('hidden');
matchError.classList.add('hidden');
return true;
} else {
matchSuccess.classList.add('hidden');
matchError.classList.remove('hidden');
return false;
}
}
if (newPasswordInput) {
newPasswordInput.addEventListener('input', function() {
updatePasswordStrength(this.value);
if (confirmPasswordInput.value) {
checkPasswordMatch();
}
});
}
if (confirmPasswordInput) {
confirmPasswordInput.addEventListener('input', checkPasswordMatch);
}
if (form) {
form.addEventListener('submit', function(e) {
const newPassword = newPasswordInput.value;
const isStrongEnough = updatePasswordStrength(newPassword);
const passwordsMatch = checkPasswordMatch();
if (!isStrongEnough) {
e.preventDefault();
alert('Please choose a stronger password.');
return;
}
if (!passwordsMatch) {
e.preventDefault();
alert('Passwords do not match.');
return;
}
if (submitBtn && submitText && submitSpinner) {
submitBtn.disabled = true;
submitText.textContent = 'Changing Password...';
submitSpinner.classList.remove('hidden');
}
});
}
});
</script>
{% include 'footer.html' %}

View File

@@ -3,39 +3,16 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% if refresh %} {% if refresh %}
<meta http-equiv="refresh" content="{{ refresh }}"> <meta http-equiv="refresh" content="{{ refresh }}">
{% endif %} {% endif %}
<title>(BSX) BasicSwap - v{{ version }}</title> <title>(BSX) BasicSwap - v{{ version }}</title>
<link rel="icon" sizes="32x32" type="image/png" href="/static/images/favicon/favicon-32.png"> <link rel="icon" sizes="32x32" type="image/png" href="/static/images/favicon/favicon-32.png">
<!-- CSS Stylesheets -->
<link type="text/css" media="all" href="/static/css/libs/flowbite.min.css" rel="stylesheet"> <link type="text/css" media="all" href="/static/css/libs/flowbite.min.css" rel="stylesheet">
<link type="text/css" media="all" href="/static/css/libs/tailwind.min.css" rel="stylesheet"> <link type="text/css" media="all" href="/static/css/libs/tailwind.min.css" rel="stylesheet">
<!-- Custom styles -->
<link type="text/css" media="all" href="/static/css/style.css" rel="stylesheet"> <link type="text/css" media="all" href="/static/css/style.css" rel="stylesheet">
<script> <script>
function getAPIKeys() {
return {
cryptoCompare: "{{ chart_api_key|safe }}",
coinGecko: "{{ coingecko_api_key|safe }}"
};
}
(function() {
Object.defineProperty(window, 'ws_port', {
value: "{{ ws_port|safe }}",
writable: false,
configurable: false,
enumerable: true
});
window.getWebSocketConfig = window.getWebSocketConfig || function() {
return {
port: window.ws_port || '11700',
fallbackPort: '11700'
};
};
})();
(function() { (function() {
const isDarkMode = localStorage.getItem('color-theme') === 'dark' || const isDarkMode = localStorage.getItem('color-theme') === 'dark' ||
(!localStorage.getItem('color-theme') && window.matchMedia('(prefers-color-scheme: dark)').matches); (!localStorage.getItem('color-theme') && window.matchMedia('(prefers-color-scheme: dark)').matches);
@@ -46,146 +23,205 @@
document.documentElement.classList.toggle('dark', isDarkMode); document.documentElement.classList.toggle('dark', isDarkMode);
})(); })();
</script> </script>
<!-- Third-party Libraries -->
<script src="/static/js/libs/chart.js"></script>
<script src="/static/js/libs/chartjs-adapter-date-fns.bundle.min.js"></script>
<script src="/static/js/libs/popper.js"></script>
<script src="/static/js/libs/tippy.js"></script>
<!-- UI Components -->
<script src="/static/js/ui/tabs.js"></script>
<script src="/static/js/ui/dropdown.js"></script>
<!-- Core functionality -->
<script src="/static/js/modules/coin-manager.js"></script>
<script src="/static/js/modules/config-manager.js"></script>
<script src="/static/js/modules/cache-manager.js"></script>
<script src="/static/js/modules/cleanup-manager.js"></script>
<script src="/static/js/modules/websocket-manager.js"></script>
<script src="/static/js/modules/network-manager.js"></script>
<script src="/static/js/modules/api-manager.js"></script>
<script src="/static/js/modules/price-manager.js"></script>
<script src="/static/js/modules/tooltips-manager.js"></script>
<script src="/static/js/modules/notification-manager.js"></script>
<script src="/static/js/modules/identity-manager.js"></script>
<script src="/static/js/modules/summary-manager.js"></script>
{% if current_page == 'wallets' or current_page == 'wallet' %}
<script src="/static/js/modules/wallet-manager.js"></script>
{% endif %}
<!-- Memory management -->
<script src="/static/js/modules/memory-manager.js"></script>
<!-- Main application script -->
<script src="/static/js/global.js"></script>
</head> </head>
<script> <body class="bg-gray-50 dark:bg-gray-900 min-h-screen flex items-center justify-center">
(function() { <div class="w-full max-w-md px-6">
const isDarkMode = localStorage.getItem('color-theme') === 'dark' || <div class="bg-white dark:bg-gray-800 rounded-xl dark:shadow-lg p-8">
(!localStorage.getItem('color-theme') && window.matchMedia('(prefers-color-scheme: dark)').matches); <div class="text-center mb-8">
<div class="mb-6">
if (!localStorage.getItem('color-theme')) { <img src="/static/images/logos/basicswap-logo.svg" class="h-16 mx-auto imageshow dark-image">
localStorage.setItem('color-theme', 'dark'); <img src="/static/images/logos/basicswap-logo-dark.svg" class="h-16 mx-auto imageshow light-image">
}
document.documentElement.classList.toggle('dark', isDarkMode);
})();
</script>
<link rel=icon sizes="32x32" type="image/png" href="/static/images/favicon/favicon-32.png">
<title>(BSX) BasicSwap - v{{ version }}</title>
</head>
<body class="dark:bg-gray-700">
<section class="py-24 md:py-32">
<div class="container px-4 mx-auto">
<div class="max-w-sm mx-auto">
<div class="mb-3 text-center">
<a class="inline-block mb-6" href="#">
<img src="/static/images/logos/basicswap-logo.svg" class="h-20 imageshow dark-image">
<img src="/static/images/logos/basicswap-logo-dark.svg" class="h-20 imageshow light-image">
</a>
<p class="text-lg text-coolGray-500 font-medium mb-6 dark:text-white">Unlock your wallets</p>
{% for m in messages %}
<section class="py-4" id="messages_{{ m[0] }}" role="alert">
<div class="container px-4 mx-auto">
<div class="p-6 text-green-800 rounded-lg bg-green-50 border border-green-500 dark:bg-gray-500 dark:text-green-400 rounded-md">
<div class="flex flex-wrap justify-between items-center -m-2">
<div class="flex-1 p-2">
<div class="flex flex-wrap -m-1">
<div class="w-auto p-1"> {{ circular_info_messages_svg | safe }} </div>
<ul class="ml-4 mt-1">
<li class="font-semibold text-sm text-green-500 error_msg text-left"><span class="bold">ALERT:</span></li>
<li class="font-medium text-sm text-green-500 infomsg">This will unlock the system for all users!</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</section>
{% endfor %}
{% for m in err_messages %}
<section class="py-4" id="err_messages_{{ m[0] }}" role="alert">
<div class="container px-4 mx-auto">
<div class="p-6 text-green-800 rounded-lg bg-red-50 border border-red-400 dark:bg-gray-500 dark:text-red-400 rounded-md">
<div class="flex flex-wrap justify-between items-center -m-2">
<div class="flex-1 p-2">
<div class="flex flex-wrap -m-1">
<div class="w-auto p-1"> {{ circular_error_messages_svg | safe }} </div>
<ul class="ml-4 mt-1">
<li class="font-semibold text-sm text-red-500 error_msg text-left"><span class="bold">ERROR:</span></li>
<li class="font-medium text-sm text-red-500 error_msg">{{ m[1] }}</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</section>
{% endfor %}
</div> </div>
<form method="post" autocomplete="off"> <h1 class="text-2xl font-bold text-gray-900 dark:text-white mb-2">Unlock BasicSwap</h1>
<div class="mb-4"> <p class="text-gray-600 dark:text-gray-400">Enter your password to access your wallets</p>
<label class="block mb-2 text-coolGray-800 font-medium dark:text-white" for="">Your Password</label> </div>
<div class="relative w-full">
<div class="absolute inset-y-0 right-0 flex items-center px-2"> {% for m in messages %}
<input class="hidden js-password-toggle" id="toggle" type="checkbox" /> <div class="mb-4 p-4 bg-green-50 border border-green-500 rounded-lg dark:bg-gray-500 dark:text-green-400" role="alert">
<label class="px-2 py-1 text-sm text-gray-600 font-mono cursor-pointer js-password-label" for="toggle"> <div class="flex items-start">
<svg xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24"> <div class="flex-shrink-0">
<g fill="#8896ab"> <svg class="w-5 h-5 text-green-600 dark:text-green-400" fill="currentColor" viewBox="0 0 20 20">
<path d="M23.444,10.239C21.905,8.062,17.708,3,12,3S2.1,8.062.555,10.24a3.058,3.058,0,0,0,0,3.52h0C2.1,15.938,6.292,21,12,21s9.905-5.062,11.445-7.24A3.058,3.058,0,0,0,23.444,10.239ZM12,17a5,5,0,1,1,5-5A5,5,0,0,1,12,17Z" fill="#8896ab"></path> <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"></path>
</g> </svg>
</svg>
</label>
</div>
<input class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0 js-password" name="password" id="password" type="password" autocomplete="off" />
</div>
</div> </div>
<button type="submit" name="unlock" value="Unlock" class="appearance-none focus:outline-none inline-block py-3 px-7 mb-6 w-full text-base text-blue-50 font-medium text-center leading-6 bg-blue-500 hover:bg-blue-600 focus:ring-0 rounded-md shadow-sm">Unlock</button> <div class="ml-3">
<p class="text-center"> <p class="text-sm font-medium text-green-800 dark:text-green-400">
<span class="text-xs font-medium dark:text-white">Need help?</span> This will unlock the system for all users!
<a class="inline-block text-xs font-medium text-blue-500 hover:text-blue-600 hover:underline" href="https://academy.particl.io/en/latest/faq/get_support.html" target="_blank">Help / Tutorials</a> </p>
</p> </div>
<p class="text-center"> </div>
<span class="text-xs font-medium text-coolGray-500 dark:text-gray-500">{{ title }}</span> </div>
</p> {% endfor %}
<input type="hidden" name="formid" value="{{ form_id }}">
</form> {% for m in err_messages %}
<div class="mb-4 p-4 bg-red-50 border border-red-400 rounded-lg dark:bg-gray-500 dark:text-red-400" role="alert">
<div class="flex items-start">
<div class="flex-shrink-0">
<svg class="w-5 h-5 text-red-600 dark:text-red-400" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"></path>
</svg>
</div>
<div class="ml-3">
<p class="text-sm font-medium text-red-800 dark:text-red-400">
{{ m[1] }}
</p>
</div>
</div>
</div>
{% endfor %}
<form method="post" autocomplete="off" id="unlock-form">
<div class="mb-6">
<label for="password" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Password
</label>
<div class="relative">
<input
type="password"
id="password"
name="password"
class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0"
placeholder="Enter your password"
autocomplete="current-password"
required
autofocus
/>
<button
type="button"
id="toggle-password"
class="absolute inset-y-0 right-0 flex items-center pr-3 text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300 transition-colors"
aria-label="Toggle password visibility"
>
<svg id="eye-open" class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path>
</svg>
<svg id="eye-closed" class="w-5 h-5 hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.878 9.878L3 3m6.878 6.878L21 21"></path>
</svg>
</button>
</div>
<div id="caps-warning" class="hidden mt-2 text-sm text-amber-700 dark:text-amber-300 flex items-center">
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd"></path>
</svg>
Caps Lock is on
</div>
</div>
<button
type="submit"
name="unlock"
value="Unlock"
id="unlock-btn"
class="w-full bg-blue-600 hover:bg-blue-700 disabled:bg-blue-400 text-white font-medium py-3 px-4 rounded-lg transition-colors focus:outline-none disabled:cursor-not-allowed"
>
<span id="unlock-text">Unlock</span>
<svg id="unlock-spinner" class="hidden animate-spin ml-2 h-5 w-5 text-white inline" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
</button>
<input type="hidden" name="formid" value="{{ form_id }}">
</form>
<div class="mt-8 text-center space-y-2">
<p class="text-sm text-gray-600 dark:text-gray-400">
Need help?
<a href="https://academy.particl.io/en/latest/faq/get_support.html"
target="_blank"
class="text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300 underline">
View tutorials
</a>
</p>
<p class="text-xs text-gray-500 dark:text-gray-500">
{{ title }}
</p>
</div> </div>
</div> </div>
</section> </div>
<script> <script>
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', function() {
const passwordToggle = document.querySelector('.js-password-toggle'); const passwordInput = document.getElementById('password');
if (passwordToggle) { const toggleButton = document.getElementById('toggle-password');
passwordToggle.addEventListener('change', function() { const eyeOpen = document.getElementById('eye-open');
const password = document.querySelector('.js-password'); const eyeClosed = document.getElementById('eye-closed');
const passwordLabel = document.querySelector('.js-password-label'); const capsWarning = document.getElementById('caps-warning');
if (password && passwordLabel) { const unlockForm = document.getElementById('unlock-form');
if (password.type === 'password') { const unlockBtn = document.getElementById('unlock-btn');
password.type = 'text'; const unlockText = document.getElementById('unlock-text');
passwordLabel.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24"><g fill="#8896ab"><path d="M23.444,10.239a22.936,22.936,0,0,0-2.492-2.948l-4.021,4.021A5.026,5.026,0,0,1,17,12a5,5,0,0,1-5,5,5.026,5.026,0,0,1-.688-.069L8.055,20.188A10.286,10.286,0,0,0,12,21c5.708,0,9.905-5.062,11.445-7.24A3.058,3.058,0,0,0,23.444,10.239Z" fill="#8896ab"></path><path d="M12,3C6.292,3,2.1,8.062.555,10.24a3.058,3.058,0,0,0,0,3.52h0a21.272,21.272,0,0,0,4.784,4.9l3.124-3.124a5,5,0,0,1,7.071-7.072L8.464,15.536l10.2-10.2A11.484,11.484,0,0,0,12,3Z" fill="#8896ab"></path><path data-color="color-2" d="M1,24a1,1,0,0,1-.707-1.707l22-22a1,1,0,0,1,1.414,1.414l-22,22A1,1,0,0,1,1,24Z"></path></g></svg>'; const unlockSpinner = document.getElementById('unlock-spinner');
} else {
password.type = 'password'; if (toggleButton && passwordInput) {
passwordLabel.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24"><g fill="#8896ab" ><path d="M23.444,10.239C21.905,8.062,17.708,3,12,3S2.1,8.062.555,10.24a3.058,3.058,0,0,0,0,3.52h0C2.1,15.938,6.292,21,12,21s9.905-5.062,11.445-7.24A3.058,3.058,0,0,0,23.444,10.239ZM12,17a5,5,0,1,1,5-5A5,5,0,0,1,12,17Z" fill="#8896ab"></path></g></svg>'; toggleButton.addEventListener('click', function(e) {
} e.preventDefault();
password.focus(); e.stopPropagation();
const isPassword = passwordInput.type === 'password';
const cursorPosition = passwordInput.selectionStart;
const inputValue = passwordInput.value;
passwordInput.type = isPassword ? 'text' : 'password';
passwordInput.value = inputValue;
setTimeout(() => {
passwordInput.setSelectionRange(cursorPosition, cursorPosition);
}, 0);
if (isPassword) {
eyeOpen.classList.add('hidden');
eyeClosed.classList.remove('hidden');
} else {
eyeOpen.classList.remove('hidden');
eyeClosed.classList.add('hidden');
}
});
toggleButton.addEventListener('mousedown', function(e) {
e.preventDefault();
});
}
if (passwordInput && capsWarning) {
passwordInput.addEventListener('keydown', function(e) {
const capsLockOn = e.getModifierState && e.getModifierState('CapsLock');
if (capsLockOn) {
capsWarning.classList.remove('hidden');
} else {
capsWarning.classList.add('hidden');
}
});
passwordInput.addEventListener('keyup', function(e) {
const capsLockOn = e.getModifierState && e.getModifierState('CapsLock');
if (!capsLockOn) {
capsWarning.classList.add('hidden');
}
});
}
if (unlockForm) {
unlockForm.addEventListener('submit', function(e) {
if (unlockBtn && unlockText && unlockSpinner) {
unlockBtn.disabled = true;
unlockText.textContent = 'Unlocking...';
unlockSpinner.classList.remove('hidden');
}
});
}
const errorMessages = document.querySelectorAll('[role="alert"]');
if (errorMessages.length > 0 && passwordInput) {
passwordInput.value = '';
passwordInput.focus();
}
if (passwordInput) {
passwordInput.addEventListener('keydown', function(e) {
if (e.key === 'Enter' && unlockForm) {
unlockForm.submit();
} }
}); });
} }