login.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. /**
  2. * Script for login.ejs
  3. */
  4. // Validation Regexes.
  5. const validUsername = /^[a-zA-Z0-9_]{1,16}$/
  6. const basicEmail = /^\S+@\S+\.\S+$/
  7. //const validEmail = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i
  8. // Login Elements
  9. const loginEmailError = document.getElementById('loginEmailError')
  10. const loginUsername = document.getElementById('loginUsername')
  11. const loginPasswordError = document.getElementById('loginPasswordError')
  12. const loginPassword = document.getElementById('loginPassword')
  13. const checkmarkContainer = document.getElementById('checkmarkContainer')
  14. const loginRememberOption = document.getElementById('loginRememberOption')
  15. const loginButton = document.getElementById('loginButton')
  16. // Control variables.
  17. let lu = false, lp = false
  18. /**
  19. * Show a login error.
  20. *
  21. * @param {HTMLElement} element The element on which to display the error.
  22. * @param {string} value The error text.
  23. */
  24. function showError(element, value){
  25. element.innerHTML = value
  26. element.style.opacity = 1
  27. }
  28. /**
  29. * Shake a login error to add emphasis.
  30. *
  31. * @param {HTMLElement} element The element to shake.
  32. */
  33. function shakeError(element){
  34. if(element.style.opacity == 1){
  35. element.classList.remove('shake')
  36. void element.offsetWidth
  37. element.classList.add('shake')
  38. }
  39. }
  40. /**
  41. * Validate that an email field is neither empty nor invalid.
  42. *
  43. * @param {string} value The email value.
  44. */
  45. function validateEmail(value){
  46. if(value){
  47. if(!basicEmail.test(value) && !validUsername.test(value)){
  48. showError(loginEmailError, '* Invalid Value')
  49. loginDisabled(true)
  50. lu = false
  51. } else {
  52. loginEmailError.style.opacity = 0
  53. lu = true
  54. if(lp){
  55. loginDisabled(false)
  56. }
  57. }
  58. } else {
  59. lu = false
  60. showError(loginEmailError, '* Required')
  61. loginDisabled(true)
  62. }
  63. }
  64. /**
  65. * Validate that the password field is not empty.
  66. *
  67. * @param {string} value The password value.
  68. */
  69. function validatePassword(value){
  70. if(value){
  71. loginPasswordError.style.opacity = 0
  72. lp = true
  73. if(lu){
  74. loginDisabled(false)
  75. }
  76. } else {
  77. lp = false
  78. showError(loginPasswordError, '* Required')
  79. loginDisabled(true)
  80. }
  81. }
  82. // Emphasize errors with shake when focus is lost.
  83. loginUsername.addEventListener('focusout', (e) => {
  84. validateEmail(e.target.value)
  85. shakeError(loginEmailError)
  86. })
  87. loginPassword.addEventListener('focusout', (e) => {
  88. validatePassword(e.target.value)
  89. shakeError(loginPasswordError)
  90. })
  91. // Validate input for each field.
  92. loginUsername.addEventListener('input', (e) => {
  93. validateEmail(e.target.value)
  94. })
  95. loginPassword.addEventListener('input', (e) => {
  96. validatePassword(e.target.value)
  97. })
  98. /**
  99. * Enable or disable the login button.
  100. *
  101. * @param {boolean} v True to enable, false to disable.
  102. */
  103. function loginDisabled(v){
  104. if(loginButton.disabled !== v){
  105. loginButton.disabled = v
  106. }
  107. }
  108. /**
  109. * Enable or disable loading elements.
  110. *
  111. * @param {boolean} v True to enable, false to disable.
  112. */
  113. function loginLoading(v){
  114. if(v){
  115. loginButton.setAttribute('loading', v)
  116. loginButton.innerHTML = loginButton.innerHTML.replace('LOGIN', 'LOGGING IN')
  117. } else {
  118. loginButton.removeAttribute('loading')
  119. loginButton.innerHTML = loginButton.innerHTML.replace('LOGGING IN', 'LOGIN')
  120. }
  121. }
  122. /**
  123. * Enable or disable login form.
  124. *
  125. * @param {boolean} v True to enable, false to disable.
  126. */
  127. function formDisabled(v){
  128. loginDisabled(v)
  129. loginUsername.disabled = v
  130. loginPassword.disabled = v
  131. if(v){
  132. checkmarkContainer.setAttribute('disabled', v)
  133. } else {
  134. checkmarkContainer.removeAttribute('disabled')
  135. }
  136. loginRememberOption.disabled = v
  137. }
  138. /**
  139. * Parses an error and returns a user-friendly title and description
  140. * for our error overlay.
  141. *
  142. * @param {Error | {cause: string, error: string, errorMessage: string}} err A Node.js
  143. * error or Mojang error response.
  144. */
  145. function resolveError(err){
  146. // Mojang Response => err.cause | err.error | err.errorMessage
  147. // Node error => err.code | err.message
  148. if(err.cause != null && err.cause === 'UserMigratedException') {
  149. return {
  150. title: 'Error During Login:<br>Invalid Credentials',
  151. desc: 'You\'ve attempted to login with a migrated account. Try again using the account email as the username.'
  152. }
  153. } else {
  154. if(err.error != null){
  155. if(err.error === 'ForbiddenOperationException'){
  156. if(err.errorMessage != null){
  157. if(err.errorMessage === 'Invalid credentials. Invalid username or password.'){
  158. return {
  159. title: 'Error During Login:<br>Invalid Credentials',
  160. desc: 'The email or password you\'ve entered is incorrect. Please try again.'
  161. }
  162. } else if(err.errorMessage === 'Invalid credentials.'){
  163. return {
  164. title: 'Error During Login:<br>Too Many Attempts',
  165. desc: 'There have been too many login attempts with this account recently. Please try again later.'
  166. }
  167. }
  168. }
  169. }
  170. } else {
  171. // Request errors (from Node).
  172. if(err.code != null){
  173. if(err.code === 'ENOENT'){
  174. // No Internet.
  175. return {
  176. title: 'Error During Login:<br>No Internet Connection',
  177. desc: 'You must be connected to the internet in order to login. Please connect and try again.'
  178. }
  179. } else if(err.code === 'ENOTFOUND'){
  180. // Could not reach server.
  181. return {
  182. title: 'Error During Login:<br>Authentication Server Offline',
  183. desc: 'Mojang\'s authentication server is currently offline or unreachable. Please wait a bit and try again. You can check the status of the server on <a href="https://help.mojang.com/">Mojang\'s help portal</a>.'
  184. }
  185. }
  186. }
  187. }
  188. }
  189. if(err.message != null){
  190. // Unknown error with request.
  191. return {
  192. title: 'Error During Login:<br>Unknown Error',
  193. desc: err.message
  194. }
  195. } else {
  196. // Unknown Mojang error.
  197. return {
  198. title: err.error,
  199. desc: err.errorMessage
  200. }
  201. }
  202. }
  203. // Bind login button behavior.
  204. loginButton.addEventListener('click', () => {
  205. // Disable form.
  206. formDisabled(true)
  207. // Show loading stuff.
  208. loginLoading(true)
  209. AuthManager.addAccount(loginUsername.value, loginPassword.value).then((value) => {
  210. loginButton.innerHTML = loginButton.innerHTML.replace('LOGGING IN', 'SUCCESS')
  211. $('.circle-loader').toggleClass('load-complete')
  212. $('.checkmark').toggle()
  213. //console.log(value)
  214. setTimeout(() => {
  215. $('#loginContainer').fadeOut(500, () => {
  216. $('#landingContainer').fadeIn(500)
  217. })
  218. }, 1000)
  219. }).catch((err) => {
  220. loginLoading(false)
  221. const errF = resolveError(err)
  222. setOverlayContent(errF.title, errF.desc, 'Try Again')
  223. setOverlayHandler(() => {
  224. formDisabled(false)
  225. toggleOverlay(false)
  226. })
  227. toggleOverlay(true)
  228. console.log(err)
  229. })
  230. })