login.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  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 loginCancelContainer = document.getElementById('loginCancelContainer')
  10. const loginCancelButton = document.getElementById('loginCancelButton')
  11. const loginEmailError = document.getElementById('loginEmailError')
  12. const loginUsername = document.getElementById('loginUsername')
  13. const loginPasswordError = document.getElementById('loginPasswordError')
  14. const loginPassword = document.getElementById('loginPassword')
  15. const checkmarkContainer = document.getElementById('checkmarkContainer')
  16. const loginRememberOption = document.getElementById('loginRememberOption')
  17. const loginButton = document.getElementById('loginButton')
  18. const loginForm = document.getElementById('loginForm')
  19. // Control variables.
  20. let lu = false, lp = false
  21. const loggerLogin = LoggerUtil1('%c[Login]', 'color: #000668; font-weight: bold')
  22. /**
  23. * Show a login error.
  24. *
  25. * @param {HTMLElement} element The element on which to display the error.
  26. * @param {string} value The error text.
  27. */
  28. function showError(element, value){
  29. element.innerHTML = value
  30. element.style.opacity = 1
  31. }
  32. /**
  33. * Shake a login error to add emphasis.
  34. *
  35. * @param {HTMLElement} element The element to shake.
  36. */
  37. function shakeError(element){
  38. if(element.style.opacity == 1){
  39. element.classList.remove('shake')
  40. void element.offsetWidth
  41. element.classList.add('shake')
  42. }
  43. }
  44. /**
  45. * Validate that an email field is neither empty nor invalid.
  46. *
  47. * @param {string} value The email value.
  48. */
  49. function validateEmail(value){
  50. if(value){
  51. if(!basicEmail.test(value) && !validUsername.test(value)){
  52. showError(loginEmailError, Lang.queryJS('login.error.invalidValue'))
  53. loginDisabled(true)
  54. lu = false
  55. } else {
  56. loginEmailError.style.opacity = 0
  57. lu = true
  58. if(lp){
  59. loginDisabled(false)
  60. }
  61. }
  62. } else {
  63. lu = false
  64. showError(loginEmailError, Lang.queryJS('login.error.requiredValue'))
  65. loginDisabled(true)
  66. }
  67. }
  68. /**
  69. * Validate that the password field is not empty.
  70. *
  71. * @param {string} value The password value.
  72. */
  73. function validatePassword(value){
  74. if(value){
  75. loginPasswordError.style.opacity = 0
  76. lp = true
  77. if(lu){
  78. loginDisabled(false)
  79. }
  80. } else {
  81. lp = false
  82. showError(loginPasswordError, Lang.queryJS('login.error.invalidValue'))
  83. loginDisabled(true)
  84. }
  85. }
  86. // Emphasize errors with shake when focus is lost.
  87. loginUsername.addEventListener('focusout', (e) => {
  88. validateEmail(e.target.value)
  89. shakeError(loginEmailError)
  90. })
  91. loginPassword.addEventListener('focusout', (e) => {
  92. validatePassword(e.target.value)
  93. shakeError(loginPasswordError)
  94. })
  95. // Validate input for each field.
  96. loginUsername.addEventListener('input', (e) => {
  97. validateEmail(e.target.value)
  98. })
  99. loginPassword.addEventListener('input', (e) => {
  100. validatePassword(e.target.value)
  101. })
  102. /**
  103. * Enable or disable the login button.
  104. *
  105. * @param {boolean} v True to enable, false to disable.
  106. */
  107. function loginDisabled(v){
  108. if(loginButton.disabled !== v){
  109. loginButton.disabled = v
  110. }
  111. }
  112. /**
  113. * Enable or disable loading elements.
  114. *
  115. * @param {boolean} v True to enable, false to disable.
  116. */
  117. function loginLoading(v){
  118. if(v){
  119. loginButton.setAttribute('loading', v)
  120. loginButton.innerHTML = loginButton.innerHTML.replace(Lang.queryJS('login.login'), Lang.queryJS('login.loggingIn'))
  121. } else {
  122. loginButton.removeAttribute('loading')
  123. loginButton.innerHTML = loginButton.innerHTML.replace(Lang.queryJS('login.loggingIn'), Lang.queryJS('login.login'))
  124. }
  125. }
  126. /**
  127. * Enable or disable login form.
  128. *
  129. * @param {boolean} v True to enable, false to disable.
  130. */
  131. function formDisabled(v){
  132. loginDisabled(v)
  133. loginCancelButton.disabled = v
  134. loginUsername.disabled = v
  135. loginPassword.disabled = v
  136. if(v){
  137. checkmarkContainer.setAttribute('disabled', v)
  138. } else {
  139. checkmarkContainer.removeAttribute('disabled')
  140. }
  141. loginRememberOption.disabled = v
  142. }
  143. let loginViewOnSuccess = VIEWS.landing
  144. let loginViewOnCancel = VIEWS.settings
  145. let loginViewCancelHandler
  146. function loginCancelEnabled(val){
  147. if(val){
  148. $(loginCancelContainer).show()
  149. } else {
  150. $(loginCancelContainer).hide()
  151. }
  152. }
  153. loginCancelButton.onclick = (e) => {
  154. switchView(getCurrentView(), loginViewOnCancel, 500, 500, () => {
  155. loginUsername.value = ''
  156. loginPassword.value = ''
  157. loginCancelEnabled(false)
  158. if(loginViewCancelHandler != null){
  159. loginViewCancelHandler()
  160. loginViewCancelHandler = null
  161. }
  162. })
  163. }
  164. // Disable default form behavior.
  165. loginForm.onsubmit = () => { return false }
  166. // Bind login button behavior.
  167. loginButton.addEventListener('click', () => {
  168. // Disable form.
  169. formDisabled(true)
  170. // Show loading stuff.
  171. loginLoading(true)
  172. AuthManager.addMojangAccount(loginUsername.value, loginPassword.value).then((value) => {
  173. updateSelectedAccount(value)
  174. loginButton.innerHTML = loginButton.innerHTML.replace(Lang.queryJS('login.loggingIn'), Lang.queryJS('login.success'))
  175. $('.circle-loader').toggleClass('load-complete')
  176. $('.checkmark').toggle()
  177. setTimeout(() => {
  178. switchView(VIEWS.login, loginViewOnSuccess, 500, 500, () => {
  179. // Temporary workaround
  180. if(loginViewOnSuccess === VIEWS.settings){
  181. prepareSettings()
  182. }
  183. loginViewOnSuccess = VIEWS.landing // Reset this for good measure.
  184. loginCancelEnabled(false) // Reset this for good measure.
  185. loginViewCancelHandler = null // Reset this for good measure.
  186. loginUsername.value = ''
  187. loginPassword.value = ''
  188. $('.circle-loader').toggleClass('load-complete')
  189. $('.checkmark').toggle()
  190. loginLoading(false)
  191. loginButton.innerHTML = loginButton.innerHTML.replace(Lang.queryJS('login.success'), Lang.queryJS('login.login'))
  192. formDisabled(false)
  193. })
  194. }, 1000)
  195. }).catch((displayableError) => {
  196. loginLoading(false)
  197. let actualDisplayableError
  198. if(isDisplayableError(displayableError)) {
  199. msftLoginLogger.error('Error while logging in.', displayableError)
  200. actualDisplayableError = displayableError
  201. } else {
  202. // Uh oh.
  203. msftLoginLogger.error('Unhandled error during login.', displayableError)
  204. actualDisplayableError = {
  205. title: 'Unknown Error During Login',
  206. desc: 'An unknown error has occurred. Please see the console for details.'
  207. }
  208. }
  209. setOverlayContent(actualDisplayableError.title, actualDisplayableError.desc, Lang.queryJS('login.tryAgain'))
  210. setOverlayHandler(() => {
  211. formDisabled(false)
  212. toggleOverlay(false)
  213. })
  214. toggleOverlay(true)
  215. })
  216. })