CINXE.COM
Ketabnak.com | ورود / ثبت نام
<!doctype html> <html lang="fa"> <head> <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1"> <meta http-equiv="x-ua-compatible" content="ie=edge"> <meta charset="utf-8"> <title>Ketabnak.com | ورود / ثبت نام</title> <meta name="description" content=""> <link rel="canonical" href=""> <script async src="https://www.googletagmanager.com/gtag/js?id=UA-59462517-1"></script> <script> window.dataLayer = window.dataLayer || []; function gtag() { dataLayer.push(arguments); } gtag('js', new Date()); gtag('config', 'UA-59462517-1', { 'user_id': '' }); </script> <link rel="preload" href="https://ketabnak.com/templates/default/assets/font/IRANSansX/IRANSansXV.woff2" as="font" type="font/woff2" crossorigin> <link rel="stylesheet" href="https://ketabnak.com/templates/default/assets/css/main.min.css?ver=1.0161"> <!-- <link rel="stylesheet" href="https://ketabnak.com/templates/default/assets/css/jquery.skeleton.css"> --> <!-- <link rel="stylesheet" href="assets/css/animate.css"> --> <!-- <link rel="stylesheet" href="vendors/jquery-bar-rating/dist/themes/css-stars-o.css"> --> <!-- <link rel="stylesheet" href="vendors/tags-input/tags-input.css"> --> <!-- <link rel="stylesheet" href="vendors/trumbowyg/dist/ui_rtl/trumbowyg.css"> --> <!-- <link rel="stylesheet" href="vendors/malihu-custom-scrollbar-plugin/jquery.mCustomScrollbar.min.css"> --> <link rel="alternate" type="application/rss+xml" title="RSS" href="https://ketabnak.com/rss"> <link rel="shortcut icon" href="https://ketabnak.com/images/favicon/favicon.ico"> <link rel="icon" sizes="16x16 32x32 64x64" href="https://ketabnak.com/favicon.ico"> <link rel="icon" type="image/png" sizes="196x196" href="https://ketabnak.com/images/favicon/favicon-192.png"> <link rel="icon" type="image/png" sizes="160x160" href="https://ketabnak.com/images/favicon/favicon-160.png"> <link rel="icon" type="image/png" sizes="96x96" href="https://ketabnak.com/images/favicon/favicon-96.png"> <link rel="icon" type="image/png" sizes="64x64" href="https://ketabnak.com/images/favicon/favicon-64.png"> <link rel="icon" type="image/png" sizes="32x32" href="https://ketabnak.com/images/favicon/favicon-32.png"> <link rel="icon" type="image/png" sizes="16x16" href="https://ketabnak.com/images/favicon/favicon-16.png"> <link rel="apple-touch-icon" href="https://ketabnak.com/images/favicon/favicon-57.png"> <link rel="apple-touch-icon" sizes="114x114" href="https://ketabnak.com/images/favicon/favicon-114.png"> <link rel="apple-touch-icon" sizes="72x72" href="https://ketabnak.com/images/favicon/favicon-72.png"> <link rel="apple-touch-icon" sizes="144x144" href="https://ketabnak.com/images/favicon/favicon-144.png"> <link rel="apple-touch-icon" sizes="60x60" href="https://ketabnak.com/images/favicon/favicon-60.png"> <link rel="apple-touch-icon" sizes="120x120" href="https://ketabnak.com/images/favicon/favicon-120.png"> <link rel="apple-touch-icon" sizes="76x76" href="https://ketabnak.com/images/favicon/favicon-76.png"> <link rel="apple-touch-icon" sizes="152x152" href="https://ketabnak.com/images/favicon/favicon-152.png"> <link rel="apple-touch-icon" sizes="180x180" href="https://ketabnak.com/images/favicon/favicon-180.png"> <meta name="msapplication-TileColor" content="#FFFFFF"> <meta name="msapplication-TileImage" content="https://ketabnak.com/images/favicon/favicon-144.png"> <meta name="msapplication-config" content="https://ketabnak.com/images/favicon/browserconfig.xml"> </head> <body class="login isolated"> <section class="isolated-adjuster"> <div class="logo-container"> <a href="https://ketabnak.com"><img class="svg-img" src="https://ketabnak.com/templates/default/assets/symbol/ketabnak.svg" alt="کتابناک" /></a> </div> <!-- step1 - enter email, mobile or username --> <div class="step step-1 "> <div class="content"> <h1>ورود / ثبتنام</h1> <form id="send-verification-code" class="minimal-form"> <h4>شماره تلفن همراه، ایمیل یا نام کاربری خود را وارد کنید.</h4> <input id="username" name="username" type="text" autocomplete="username" maxlength="36" style="direction: ltr" /> <input name="password" type="password" autocomplete="current-password" hidden="hidden" class="hidden" /><!--only for form fillers--> <button id="continue-btn" class="loading button button-orange" type="submit" disabled="disabled" data-reserved-text="ادامه" > </button> </form> <div class="line"><span>یا</span></div> <div class="socialloginbtn"> <a href="https://ketabnak.com/login?action=sso-google" class="button button-outline button-register-google" > <svg class="svg-img" viewBox="0 0 100 100"> <use xlink:href="https://ketabnak.com/templates/default/assets/symbol/sprite.svg?1.0161#google-color" ></use> </svg> <span>با گوگل وارد شوید</span> </a> </div> <!--<div class="socialloginbtn"> <a href="https://ketabnak.com/misc.php?action=login&sso=google" class="button button-outline button-register-google" > <svg class="svg-img" viewBox="0 0 100 100"> <use xlink:href="https://ketabnak.com/templates/default/assets/symbol/sprite.svg?1.0161facebook" ></use> </svg> <span>با فیس بوک وارد شوید</span> </a> </div>--> <div class="policy"> <p class="rules"> <a href="https://ketabnak.com/policies/terms-of-use" target="_blank">قوانین استفاده از کتابناک</a> را خوانده و قبول دارم. </p> <p class="recaptcha"> This site is protected by reCAPTCHA and the Google <a href="https://policies.google.com/privacy" rel="nofollow" target="_blank">Privacy Policy </a>and <a href="https://policies.google.com/terms" rel="nofollow" target="_blank">Terms of Service </a>apply. </p> </div> </div> </div> <!-- step2 Verify OTP --> <div class="step step-2 growable hidden"> <div class="content flex flex-col items-center"><!--justify-center --> <span class="back-btn e-change-user" title="بازگشت"> <svg class="svg-img" viewBox="0 0 100 100" width="24" height="24"> <use xlink:href="https://ketabnak.com/templates/default/assets/symbol/sprite.svg?1.0161#right-arrow"></use> </svg> </span> <div class="avatar-container"> <img class="svg-img" src="https://ketabnak.com/avatar/no_avatar.svg" /> </div> <h1>ورود با کد یکبار مصرف</h1> <div class="show-inner-input"> <a href="#" class="flex-space-between e-change-user" title="ویرایش"> <span class="mobile"></span> <svg class="svg-img" viewBox="0 0 100 100"> <use xlink:href="https://ketabnak.com/templates/default/assets/symbol/sprite.svg?1.0161#edit" ></use> </svg> </a> </div> <form id="verify-otp" class="otp minimal-form" method="post"> <h4>لطفا کدی که به شما ارسال شده است را وارد کنید.</h4> <div class="input-field flex-space-between otp-inputs" dir="ltr"> <input type="tel" class="otp-input" maxlength="6" /> <input type="tel" class="otp-input" maxlength="1" /> <input type="tel" class="otp-input" maxlength="1" /> <input type="tel" class="otp-input" maxlength="1" /> <input type="tel" class="otp-input" maxlength="1" /> <input type="tel" class="otp-input" maxlength="1" /> </div> <div id="otp-error" class="error otp-error hidden"></div> <button class="button button-orange" id="verify-btn" type="submit" disabled > تایید </button> </form> <div class="flex-grow"></div> <div id="resend-code-btn" class="timer-container hidden"> <a href="#"> ارسال مجدد کد </a> </div> <p id="timer-container" class="timer-container"> <span>زمان باقی مانده:</span> <strong class="timer has-digit" id="timer">2:00</strong> </p> </div> </div> <!-- step3 Complete Profile --> <div class="step step-3 hidden"> <div class="content"><!--flex flex-col justify-center items-center--> <div class="avatar-container"> <div title="انتخاب آواتار"> <svg viewBox="0 0 100 100"> <use xlink:href="https://ketabnak.com/templates/default/assets/symbol/sprite.svg?1.0161#add-a-photo" ></use> </svg> </div> <img id="profile-avatar" src="" /> </div> <h1>تکمیل ثبتنام</h1> <form id="complete-information" class="minimal-form flex flex-col" method="post" > <input id="avatar" class="hidden" name="avatar" type="file" accept=".jpg, .jpeg, .png" /> <input type="hidden" name="action" value="complete-profile"/> <input id="profile_otp" type="hidden" name="otp" value=""/> <input id="profile_userid" type="hidden" name="userid" value=""/> <div class="input-container"> <label for="full_name">نام خود را وارد کنید</label> <input id="full_name" class="input-validation full_name" name="full_name" type="text" maxlength="25" autocomplete="name" value="" /> <div class="validation-error full_name"></div> </div> <div class="input-container"> <label for="username">نام کاربری خود را به انگلیسی وارد کنید</label> <input id="_username" class="input-validation user_name" name="user_name" type="text" maxlength="15" autocomplete="username" value="" style="direction: ltr" /> <div class="validation-error user_name"></div> </div> <div class="input-container"> <div class="position-relative"> <label for="password">گذرواژه خود را تعیین کنید</label> <input id="_password" class="input-validation password" name="password" type="password" maxlength="30" autocomplete="new-password" style="direction: ltr" /> <a class="toggle-password" href="#" title="نمایش گذرواژه"> <svg class="svg-img" viewBox="0 0 100 100"> <use xlink:href="https://ketabnak.com/templates/default/assets/symbol/sprite.svg?1.0161#visibility-off"></use> </svg> </a> </div> <div class="validation-error password"></div> </div> <button class="button button-orange margin-top" type="submit"> ورود به کتابناک </button> </form> </div> </div> <!-- step4 Login by Password --> <div class="step step-4 hidden"> <div class="content flex flex-col items-center"><!--justify-center--> <span class="back-btn e-change-user" title="بازگشت"> <svg class="svg-img" viewBox="0 0 100 100" width="24" height="24"> <use xlink:href="https://ketabnak.com/templates/default/assets/symbol/sprite.svg?1.0161#right-arrow" ></use> </svg> </span> <div class="avatar-container"> <img src="https://ketabnak.com/avatar/no_avatar.svg" alt="" class="svg-img" /> </div> <h1>ورود با گذرواژه</h1> <div class="show-inner-input"> <a href="#" class="flex-space-between e-change-user" title="ویرایش"> <span class="mobile"></span> <svg class="svg-img" viewBox="0 0 100 100"> <use xlink:href="https://ketabnak.com/templates/default/assets/symbol/sprite.svg?1.0161#edit" ></use> </svg> </a> </div> <form id="login" class="minimal-form" method="post" ><!-- flex flex-col--> <h4 for="pass-user">گذرواژه خود را وارد کنید.</h4> <div class="input-container"> <input name="username" type="text" autocomplete="username" hidden="hidden" class="hidden" /><!--only for form fillers--> <input id="password" class="password" name="password" type="password" autocomplete="current-password" /> <a class="toggle-password no-label" href="#" title="نمایش گذرواژه" > <svg class="svg-img" viewBox="0 0 100 100"><use xlink:href="https://ketabnak.com/templates/default/assets/symbol/sprite.svg?1.0161#visibility-off"></use> </svg> </a> </div> <div id="pass-error" class="pass-error hidden error"></div> <div class="actions"> <a href="#" id="otp-login-btn"> ورود با کد یکبار مصرف </a> <a href="#" id="password-reset-btn"> گذرواژه خود را فراموش کردهاید؟ </a> </div> <button class="button button-orange margin-top" type="submit"> ورود </button> </form> </div> </div> <!-- step5 Password Reset --> <div class="step step-5 hidden"> <div class="content flex flex-col justify-center items-center"> <span class="back-btn e-change-user" title="بازگشت"> <svg class="svg-img" viewBox="0 0 100 100" width="24" height="24"> <use xlink:href="https://ketabnak.com/templates/default/assets/symbol/sprite.svg?1.0161#right-arrow"></use> </svg> </span> <div class="avatar-container"> <img src="https://ketabnak.com/avatar/no_avatar.svg" alt="" class="svg-img" /> </div> <h1>تغییر گذرواژه</h1> <form id="passwordReset" class="minimal-form flex flex-col" method="post" > <h4 for="pass-user">گذرواژه جدید خود را وارد کنید.</h4> <div class="input-container"> <div class="position-relative"> <input type="hidden" name="action" value="reset-password" /> <input type="hidden" name="userid" /> <input type="hidden" name="otp" /> <input name="username" type="text" autocomplete="username" hidden="hidden" class="hidden" /><!--only for form fillers--> <input id="new-password" class="input-validation password" name="password" type="password" autocomplete="new-password" /> <a class="toggle-password no-label" href="#" title="نمایش گذرواژه" > <svg class="svg-img" viewBox="0 0 100 100"><use xlink:href="https://ketabnak.com/templates/default/assets/symbol/sprite.svg?1.0161#visibility-off"></use> </svg> </a> </div> <div class="validation-error password"></div> </div> <button class="button button-orange margin-top" type="submit"> تغییر گذرواژه </button> </form> </div> </div> <!-- step6 Password Reset result --> <div class="step step-6 hidden growable"> <div class="content"> <div class="flex-grow"></div> <div class="status-img"> <img src="https://ketabnak.com/templates/default/assets/symbol/verify.svg?1" alt="✓"/> </div> <h2 class="align-center margin-bottom">گذرواژه شما با موفقیت تغییر کرد.</h2> <div class="flex-grow"></div> <a href="my" class="button button-orange margin-top grid-col-xs-48">ورود به سایت</a> </div> </div> </section> <script> const URL_SITE = "https://ketabnak.com/"; const kConfigs = { user_id: Number("") }; </script> <script src="https://ketabnak.com/templates/default/vendors/modernizr/modernizr-custom.js"></script> <script src="https://ketabnak.com/templates/default/vendors/jquery/dist/jquery.min.js?3.7.1"></script> <script src="https://ketabnak.com/templates/default/assets/js/main.js?ver=1.0161"></script> <script src="https://www.google.com/recaptcha/api.js?render=6LdgeZAUAAAAAPDx2702bFoMLLvrivE4DO3Udxvt&hl=fa"></script> <script> // Recaptcha grecaptcha.ready(function() { grecaptcha.execute('6LdgeZAUAAAAAPDx2702bFoMLLvrivE4DO3Udxvt', {action: 'login'}).then(function (token) { $('#send-verification-code').prepend('<input id="g-recaptcha-response" type="hidden" name="g-recaptcha-response" value="' + token + '">'); atJS.tools.hideLoadingState($('#send-verification-code button[type="submit"]')); }); }); // Callback after user completes reCAPTCHA - should be out of `ready` function recaptchaCallback() { let continueBtn = document.getElementById('continue-btn'); let reservedText = continueBtn.getAttribute('data-reserved-text'); continueBtn.textContent = reservedText; continueBtn.classList.remove('loading'); } $(document).ready(function() { // Force enable `continue` button if captcha service is not loaded yet setTimeout(function() { atJS.tools.hideLoadingState($('#send-verification-code button[type="submit"]')); }, 7000); // Toggle password visibility $('.toggle-password').click(function() { const passwordInput = $(this).siblings('input.password'); const type = passwordInput.attr('type') === 'password' ? 'text' : 'password'; passwordInput.attr('type', type); // Change the image source based on the password visibility if (type === 'password') { $(this).find('use').attr('xlink:href', 'https://ketabnak.com/templates/default/assets/symbol/sprite.svg?1.0161#visibility-off'); $(this).attr('title', 'نمایش گذرواژه'); } else { $(this).find('use').attr('xlink:href', 'https://ketabnak.com/templates/default/assets/symbol/sprite.svg?1.0161#visibility'); $(this).attr('title', 'پنهان کردن گذرواژه'); } }); ///// const $usernameInput = $('#username'); const $passwordInput = $('#password'); const $otpInputs = $('.otp-input'); const $continueBtn = $('#continue-btn'); const $verifyBtn = $('#verify-btn'); const $resendCodeBtn = $('#resend-code-btn'); const $passwordResetBtn = $('#password-reset-btn'); const $timerContainer = $('#timer-container'); const $timer = $('#timer'); const $otpError = $('#otp-error'); const $passError = $('#pass-error'); const $profileAvatar = $('#profile-avatar'); const $noAvatarURL = URL_SITE + 'avatar/no_avatar.svg'; let countdownInterval; // Switch between steps function switchStep(stepID) { const length = $('.step').length; [...Array(length)].forEach((e, i) => { i = i + 1; if(i === stepID) { $('.step-' + i).removeClass('hidden'); } else { $('.step-' + i).addClass('hidden'); } }); } // Send verification code function sendCode(type = null, context = null) { const username = $usernameInput.val().trim(); var recaptchaResponseV3 = $('input[name="g-recaptcha-response"]').val(); var recaptchaResponseV2 = recaptchaV2Loaded ? grecaptcha.getResponse() : null; return $.ajax({ url: URL_AUTH, method: 'POST', data: { action: 'send-otp', username: username, type: type, context: context, 'g-recaptcha-response': recaptchaResponseV2 || recaptchaResponseV3, 'captcha-version': recaptchaV2Loaded ? 'v2' : 'v3' }, success: function(response) { try { response = JSON.parse(response); if(response.success) { // If user entered `username` in the first step, avatar will be returned by the server // It should be placed in all steps if(response.avatar) $('.avatar-container img').attr('src', URL_SITE+'avatar/'+response.avatar); else $('.avatar-container img').attr('src', $noAvatarURL); if (!response.isRegistered || type === 'forceOTP' || type === 'resendOTP') { webOtpReader(); // Not registered or the request was `forceOTP` or `resendOTP` $('.step-2 .e-change-user .mobile').text($usernameInput.val()); $('form#verify-otp').data('context', context); // Switch Step: Verify OTP switchStep(2); $otpInputs.first().focus(); startCountdownTimer(); } else { // is registered and the request was not `forceOTP` or `resendOTP` let enteredUsername = $usernameInput.val().trim(); $('.step-4 .e-change-user .mobile').text(enteredUsername); $('.step-4 input[name="username"]').val(enteredUsername); // Switch Step: Login Password switchStep(4); } } else if(response.captchaNeeded) { // recaptchaV2 is needed atJS.tools.hideLoadingState($continueBtn); loadReCaptchaV2(); } else { atJS.tools.showSnackBar(response.message); } } catch (exceptionVar) { console.log(exceptionVar); } }, error: function() { atJS.tools.showSnackBar('لطفا دوباره تلاش کنید.'); } }); } // Wait for OTP using Web OTP API async function webOtpReader() { if ('OTPCredential' in window) { const abortController = new AbortController(); setTimeout(() => abortController.abort(), 2 * 60 * 1000); // 2 minutes timeout try { const otp = await navigator.credentials.get({ otp: { transport: ['sms'] }, signal: abortController.signal }); placeCode(otp.code); } catch (error) { console.error('Error obtaining OTP:', error); } } } //Form Submit: Send Verification Code $('#send-verification-code').submit(function(e) { e.preventDefault(); let formFillerPassword = $(this).find('input[type="password"]').val(); if(formFillerPassword) $passwordInput.val(formFillerPassword); const submitButton = $(this).find('button[type="submit"]'); atJS.tools.showLoadingState(submitButton); sendCode().always(function() { atJS.tools.hideLoadingState(submitButton); }); }); //Form Submit: verify OTP $('form#verify-otp').submit(function(e) { e.preventDefault(); const submitButton = $(this).find('button[type="submit"]'); const username = $usernameInput.val(); const otp = [...$otpInputs].map(input => input.value).join(''); const context = $(this).data('context'); atJS.tools.showLoadingState(submitButton); $.ajax({ url: URL_AUTH, method: 'POST', data: { action: 'verify-otp', otp: otp, username: username, context: context }, success: function(response) { try { response = JSON.parse(response); if (response.success) { if(response.need_update_profile) { // Switch Step: Complete profile $("#profile_userid").val(response.userid); $("#profile_otp").val(response.register_otp); switchStep(3); } else { if(context == 'passwordReset') { // password reset context $('form#passwordReset input[name="userid"]').val(response.userid); $('form#passwordReset input[name="otp"]').val(response.password_reset_otp); switchStep(5); } else { // login context window.location.replace(URL_SITE + 'my') } } } else if(response.captchaNeeded) { // Captcha is invalid. we don't load recaptcha in this stage atJS.tools.showMessage('error', 'ورود / ثبتنام', 'متاسفانه کپچا منقضی شده شده است. لطفاً دوباره تلاش کنید.', function() { window.location.replace(URL_SITE + 'login'); }); } else { $otpError.text('کد وارد شده اشتباه است. لطفاً دوباره وارد کنید.').removeClass('hidden'); $otpInputs.val(''); $otpInputs.addClass('invalid'); $otpInputs.first().focus(); $verifyBtn.prop('disabled', true); } } catch (exceptionVar) { console.log(exceptionVar); } }, error: function() { atJS.tools.showSnackBar('لطفا دوباره تلاش کنید.'); } }).always(function() { atJS.tools.hideLoadingState(submitButton); }); }); //Form Submit: Complete Information $('#complete-information').submit(function(e) { e.preventDefault(); const form = $(this); // Reset Validation form.find('.input-validation').removeClass('invalid'); form.find('.validation-error').text(''); let formData = new FormData(); $.each(form.serializeArray(), function(i, field) { formData.append(field.name, field.value); }); form.find('input[type="file"]').each(function() { var files = $(this)[0].files; if (files.length > 0) { formData.append($(this).attr('name'), files[0]); } }); let isValid = true; let errors = {}; // Extract values from FormData for validation const fullName = formData.get('full_name') ? atJS.tools.customTrim(formData.get('full_name')) : ''; const userName = formData.get('user_name') ? atJS.tools.customTrim(formData.get('user_name')) : ''; const password = formData.get('password') ? formData.get('password').trim() : ''; const englishPersianArabicChars = /^[a-zA-Z\u0621-\u064A\u0670-\u06D3\u06D5\u06EE-\u06FC\u06FF\s\u200C]+$/; if (fullName === '') { isValid = false; errors.full_name = 'وارد کردن نام ضروری است.'; } else if (fullName.length < 2) { isValid = false; errors.full_name = 'حداقل دو حرف برای نام وارد کنید.'; } else if (!englishPersianArabicChars.test(fullName)) { isValid = false; errors.full_name = 'فقط حروف فارسی و انگلیسی مجاز است'; } const regex = /[^A-Za-z0-9_.]/; if (userName === '') { isValid = false; errors.user_name = 'تعیین نام کاربری ضروری است.'; } else if (userName.length < 3) { isValid = false; errors.user_name = 'حداقل سه حرف برای نام کاربری وارد کنید.'; } else if (userName.length > 15) { isValid = false; errors.user_name = 'حداکثر طول مجاز ۱۵ کاراکتر است.'; } else if (regex.test(userName)) { isValid = false; errors.user_name = 'فقط حروف و اعداد انگلیسی و کاراکترهای _ و . مجاز است.'; } if (password === '') { isValid = false; errors.password = 'تعیین گذرواژه ضروری است.'; } else if (password.length < 6) { isValid = false; errors.password = 'گذرواژه شما باید بیش از شش کاراکتر باشد.'; } if (isValid) { const submitButton = $(this).find('button[type="submit"]'); atJS.tools.showLoadingState(submitButton); $.ajax({ url: URL_AUTH, method: 'POST', data: formData, contentType: false, processData: false, cache: false, success: function(response) { try { response = JSON.parse(response); if (response.success) { window.location.replace('/sponsors'); } else if(response.captchaNeeded) { // recaptcha is invalid. we don't load recaptcha in this stage atJS.tools.showMessage('error', 'ورود / ثبتنام', 'متاسفانه کپچا منقضی شده شده است. لطفاً دوباره تلاش کنید.', function() { window.location.replace(URL_SITE + 'login'); }); } else { if(response.errors) { for (const [key, value] of Object.entries(response.errors)) { form.find('.validation-error.' + key).text(value); form.find('.input-validation.' + key).addClass('invalid'); } } else { errors = {}; atJS.tools.showSnackBar(response.message); } } } catch (exceptionVar) { console.log(exceptionVar); } }, error: function() { atJS.tools.showSnackBar('لطفا دوباره تلاش کنید.'); } }).always(function () { atJS.tools.hideLoadingState(submitButton); }); } for (const [key, value] of Object.entries(errors)) { form.find('.validation-error.' + key).text(value); form.find('.input-validation.' + key).addClass('invalid'); } }); //Form Submit: Login $('#login').submit(function(e) { e.preventDefault(); const username = $(this).find('input[name="username"]').val(); const passowrd = $passwordInput.val(); const submitButton = $(this).find('button[type="submit"]'); atJS.tools.showLoadingState(submitButton); // Check with the server $.ajax({ url: URL_AUTH, method: 'POST', data: { action: 'verify-login', username: username, password: passowrd}, cache: false, success: function(response) { try { response = JSON.parse(response); if(response.success) { $passwordInput.removeClass('invalid'); window.location.replace(URL_SITE+'my'); } else if(response.captchaNeeded) { // recaptcha is invalid. we don't load recaptcha in this stage atJS.tools.showMessage('error', 'ورود / ثبتنام', 'متاسفانه کپچا منقضی شده شده است. لطفاً دوباره تلاش کنید.', function() { window.location.replace(URL_SITE + 'login'); }); } else { $passError.text('گذرواژه وارد شده اشتباه است.'); $passError.removeClass('hidden') $passwordInput.addClass('invalid'); } } catch (exceptionVar) { console.log(exceptionVar); } }, error: function() { atJS.tools.showSnackBar('لطفا دوباره تلاش کنید.'); } }).always(function () { atJS.tools.hideLoadingState(submitButton); }); }); //Form Submit: Password Reset $('form#passwordReset').submit(function(e) { e.preventDefault(); // Reset Validation $(this).find('.input-validation').removeClass('invalid'); $(this).find('.validation-error').text(''); let formData = new FormData(); $.each($(this).serializeArray(), function(i, field) { formData.append(field.name, field.value); }); let isValid = true; let errors = {}; // Extract value from FormData for validation const password = formData.get('password') ? formData.get('password').trim() : ''; if (password === '') { isValid = false; errors.password = 'تعیین گذرواژه ضروری است.'; } else if (password.length < 6) { isValid = false; errors.password = 'گذرواژه شما باید بیش از شش کاراکتر باشد.'; } if (isValid) { const submitButton = $(this).find('button[type="submit"]'); atJS.tools.showLoadingState(submitButton); // Check with the server $.ajax({ url: URL_AUTH, method: 'POST', data: formData, contentType: false, processData: false, cache: false, success: function(response) { try { response = JSON.parse(response); if (response.success) { switchStep(6); } else if(response.captchaNeeded) { // recaptcha is invalid. we don't load recaptcha in this stage atJS.tools.showMessage('error', 'ورود / ثبتنام', 'متاسفانه کپچا منقضی شده شده است. لطفاً دوباره تلاش کنید.', function() { window.location.replace(URL_SITE + 'login'); }); } else { if(response.errors) { for (const [key, value] of Object.entries(response.errors)) { $(this).find('.validation-error.' + key).text(value); $(this).find('.input-validation.' + key).addClass('invalid'); } } else { errors = {}; atJS.tools.showSnackBar(response.message); } } } catch (exceptionVar) { console.log(exceptionVar); } }, error: function() { atJS.tools.showSnackBar('لطفا دوباره تلاش کنید.'); } }).always(function () { atJS.tools.hideLoadingState(submitButton); }); } for (const [key, value] of Object.entries(errors)) { $(this).find('.validation-error.' + key).text(value); $(this).find('.input-validation.' + key).addClass('invalid'); } }); // Password Reset $passwordResetBtn.click(function(e) { e.preventDefault(); const submitButton = $(this).parents('form').find('button[type="submit"]'); atJS.tools.showLoadingState(submitButton); sendCode('forceOTP', 'passwordReset').always(function() { atJS.tools.hideLoadingState(submitButton); $('.step-2 h1').text('تغییر گذرواژه'); }); }); // Resend Verification Code $resendCodeBtn.click(function(e) { e.preventDefault(); const submitButton = $('form#verify-otp').find('button[type="submit"]'); const context = $('form#verify-otp').data('context'); atJS.tools.showLoadingState(submitButton); $otpInputs.val(''); $verifyBtn.prop('disabled', true); sendCode('resendOTP', context).always(function() { atJS.tools.hideLoadingState(submitButton); }); $resendCodeBtn.addClass('hidden'); $timerContainer.removeClass('hidden') startCountdownTimer(); }); // Countdown timer function function startCountdownTimer() { clearInterval(countdownInterval); let timeLeft = 120; // 2 minutes in seconds $resendCodeBtn.addClass('hidden'); $timerContainer.removeClass('hidden'); const proccessTimer = () => { if (timeLeft <= 0) { clearInterval(countdownInterval); $timerContainer.addClass('hidden'); $resendCodeBtn.removeClass('hidden'); } else { const minutes = Math.floor(timeLeft / 60); let seconds = (timeLeft % 60); if(seconds < 10) { seconds = '0' + seconds } $timer.text(minutes + ':' + seconds); timeLeft--; } } proccessTimer() countdownInterval = setInterval(proccessTimer, 1000); } // Enable/disable continue button based on input $usernameInput.on('input', function() { enableContinueButton($(this)); }); enableContinueButton($usernameInput); function enableContinueButton(field) { const isValidate = field.val().trim() ? true : false; $continueBtn.prop('disabled', !isValidate); } // Handle backspace in OTP inputs $otpInputs.on('keydown', function(e) { $otpInputs.removeClass('invalid'); if (e.key === 'Backspace' && !this.value) { $(this).prev('.otp-input').focus(); } }); // Enable/disable verify OTP button based on OTP inputs function placeCode(value, currentIndex = 0) { // Allow only Persian, Arabic, and English numbers if (!/^\d*$/.test(value)) { if (!/[\u0660-\u0669\u06f0-\u06f9]/g.test(value)) { value = value.replace(/\D/g, ''); } } if (value.length > 1) { const characters = value.split(''); $otpInputs.each((index, input) => { if (index >= currentIndex && characters[index - currentIndex]) { $(input).val(characters[index - currentIndex]); } else if (index >= currentIndex) { $(input).val(''); } }); // Focus on the next input field based on the number of characters const nextIndex = currentIndex + characters.length; if (nextIndex < $otpInputs.length) { $otpInputs.eq(nextIndex).focus(); } else { $otpInputs.last().focus(); } } else if (value.length === 1) { $otpInputs.eq(currentIndex).next('.otp-input').focus(); } // Enable verify button if all inputs are filled const allFilled = [...$otpInputs].every(input => input.value.length === 1); if (allFilled) { $verifyBtn.prop('disabled', !allFilled); $verifyBtn.click(); } } // Handle Gboard paste in OTP inputs $otpInputs.on('input', function(e) { const $this = $(this); let value = $this.val(); placeCode(value, $otpInputs.index(this)); }); // Handle Paste Code in OTP inputs $otpInputs.on('paste', function(e) { e.preventDefault(); const pastedData = e.originalEvent.clipboardData.getData('text'); if(!pastedData) return; placeCode(pastedData, $otpInputs.index(this)); }); //Click Input Avatar And Open Gallery $('.step-3 .avatar-container').click(function(){ $('#avatar').click() }); // Set Avatar Image And Show Preview Image $('#avatar').on('input', function(e) { const fileData = e.target.files[0]; const reader = new FileReader(); const validExtensions = ['image/png', 'image/jpg', 'image/jpeg']; if (validExtensions.includes(fileData.type)) { reader.readAsDataURL(fileData); reader.onloadend = () => { $profileAvatar.attr('src', reader.result); }; } else { atJS.tools.showSnackBar('قالب تصویر انتخاب شده مورد پذیرش نیست.'); } }); // Reset Forms function resetForms() { $('form#verify-otp').trigger('reset'); $('#complete-information').trigger('reset'); $('#login').trigger('reset'); $('form#passwordReset').trigger('reset'); $('.avatar-container img').attr('src', $noAvatarURL); } // Reset Validations function resetValidations() { $passwordInput.removeClass('invalid'); $otpInputs.removeClass('invalid'); $otpError.addClass('hidden'); $passError.addClass('hidden'); } // Reset Countdown Timer function resetCountdownTimer() { clearInterval(countdownInterval); $timerContainer.addClass('hidden'); $resendCodeBtn.addClass('hidden'); $timer.text('2:00'); } // Load recaptcha var recaptchaV2Loaded = false; function loadReCaptchaV2() { atJS.tools.showLoadingState($continueBtn); //$('#g-recaptcha-response').remove(); $('input[name="g-recaptcha-response"]').remove(); $('.g-recaptcha').remove(); // previous recaptchaV2 $("<div style='width: fit-content' class='g-recaptcha' data-callback=\"recaptchaCallback\" data-sitekey='6LfndZkUAAAAANH3G4fsCE8HsFSaFp-iwaRUNnX9'>لطفا صبر کنید...</div>").insertBefore($continueBtn); /*$("<input class='captcha' type='hidden' name='captcha' value='v2'>").insertBefore($continueBtn);*/ $('#send-verification-code').prepend("<input class='captcha' type='hidden' name='captcha' value='v2'>"); const script = document.createElement('script'); script.src = 'https://www.google.com/recaptcha/api.js?hl=fa"'; document.body.appendChild(script); recaptchaV2Loaded = true; } // Change Username $('.e-change-user').click(function(e) { switchStep(1); // Reset All Form resetForms() // Reset the countdown timer resetCountdownTimer() // Reset Validations resetValidations() }); // Handle Error Pass Input $passwordInput.on('keydown', function(e) { $passwordInput.removeClass('invalid'); }); // Send code and show second form $('#otp-login-btn').click(function(e) { e.preventDefault(); const submitButton = $(this).parents('form').find('button[type="submit"]'); atJS.tools.showLoadingState(submitButton); sendCode('forceOTP').always(function() { atJS.tools.hideLoadingState(submitButton); }); }); }); </script> </body> </html>