asyncForm.coffee 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. define [
  2. "base"
  3. "libs/passfield"
  4. ], (App) ->
  5. App.directive "asyncForm", ($http, validateCaptcha) ->
  6. return {
  7. controller: ['$scope', ($scope) ->
  8. @getEmail = () ->
  9. return $scope.email
  10. return this
  11. ]
  12. link: (scope, element, attrs) ->
  13. formName = attrs.asyncForm
  14. scope[attrs.name].response = response = {}
  15. scope[attrs.name].inflight = false
  16. validateCaptchaIfEnabled = (callback = (response) ->) ->
  17. if attrs.captcha?
  18. validateCaptcha callback
  19. else
  20. callback()
  21. submitRequest = (grecaptchaResponse) ->
  22. formData = {}
  23. for data in element.serializeArray()
  24. formData[data.name] = data.value
  25. if grecaptchaResponse?
  26. formData['g-recaptcha-response'] = grecaptchaResponse
  27. scope[attrs.name].inflight = true
  28. # for asyncForm prevent automatic redirect to /login if
  29. # authentication fails, we will handle it ourselves
  30. $http
  31. .post(element.attr('action'), formData, {disableAutoLoginRedirect: true})
  32. .then (httpResponse) ->
  33. { data, status, headers, config } = httpResponse
  34. scope[attrs.name].inflight = false
  35. response.success = true
  36. response.error = false
  37. onSuccessHandler = scope[attrs.onSuccess]
  38. if onSuccessHandler
  39. onSuccessHandler(httpResponse)
  40. return
  41. if data.redir?
  42. ga('send', 'event', formName, 'success')
  43. window.location = data.redir
  44. else if data.message?
  45. response.message = data.message
  46. if data.message.type == "error"
  47. response.success = false
  48. response.error = true
  49. ga('send', 'event', formName, 'failure', data.message)
  50. else
  51. ga('send', 'event', formName, 'success')
  52. .catch (httpResponse) ->
  53. { data, status, headers, config } = httpResponse
  54. scope[attrs.name].inflight = false
  55. response.success = false
  56. response.error = true
  57. onErrorHandler = scope[attrs.onError]
  58. if onErrorHandler
  59. onErrorHandler(httpResponse)
  60. return
  61. if status == 400 # Bad Request
  62. response.message =
  63. text: "Invalid Request. Please correct the data and try again."
  64. type: 'error'
  65. else if status == 403 # Forbidden
  66. response.message =
  67. text: "Session error. Please check you have cookies enabled. If the problem persists, try clearing your cache and cookies."
  68. type: "error"
  69. else
  70. response.message =
  71. text: data.message?.text or data.message or "Something went wrong talking to the server :(. Please try again."
  72. type: 'error'
  73. ga('send', 'event', formName, 'failure', data.message)
  74. submit = () ->
  75. validateCaptchaIfEnabled (response) ->
  76. submitRequest response
  77. element.on "submit", (e) ->
  78. e.preventDefault()
  79. submit()
  80. if attrs.autoSubmit
  81. submit()
  82. }
  83. App.directive "formMessages", () ->
  84. return {
  85. restrict: "E"
  86. template: """
  87. <div class="alert" ng-class="{
  88. 'alert-danger': form.response.message.type == 'error',
  89. 'alert-success': form.response.message.type != 'error'
  90. }" ng-show="!!form.response.message">
  91. {{form.response.message.text}}
  92. </div>
  93. <div ng-transclude></div>
  94. """
  95. transclude: true
  96. scope: {
  97. form: "=for"
  98. }
  99. }
  100. App.directive 'complexPassword', ->
  101. require: ['^asyncForm', 'ngModel']
  102. link: (scope, element, attrs, ctrl) ->
  103. PassField.Config.blackList = []
  104. defaultPasswordOpts =
  105. pattern: ""
  106. length:
  107. min: 6
  108. max: 128
  109. allowEmpty: false
  110. allowAnyChars: false
  111. isMasked: true
  112. showToggle: false
  113. showGenerate: false
  114. showTip:false
  115. showWarn:false
  116. checkMode : PassField.CheckModes.STRICT
  117. chars:
  118. digits: "1234567890"
  119. letters: "abcdefghijklmnopqrstuvwxyz"
  120. letters_up: "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  121. symbols: "@#$%^&*()-_=+[]{};:<>/?!£€.,"
  122. opts = _.defaults(window.passwordStrengthOptions || {}, defaultPasswordOpts)
  123. if opts.length.min == 1
  124. opts.acceptRate = 0 #this allows basically anything to be a valid password
  125. passField = new PassField.Field("passwordField", opts);
  126. [asyncFormCtrl, ngModelCtrl] = ctrl
  127. ngModelCtrl.$parsers.unshift (modelValue) ->
  128. isValid = passField.validatePass()
  129. email = asyncFormCtrl.getEmail() || window.usersEmail
  130. if !isValid
  131. scope.complexPasswordErrorMessage = passField.getPassValidationMessage()
  132. else if (email? and email != "")
  133. startOfEmail = email?.split("@")?[0]
  134. if modelValue.indexOf(email) != -1 or modelValue.indexOf(startOfEmail) != -1
  135. isValid = false
  136. scope.complexPasswordErrorMessage = "Password can not contain email address"
  137. if opts.length.max? and modelValue.length == opts.length.max
  138. isValid = false
  139. scope.complexPasswordErrorMessage = "Maximum password length #{opts.length.max} reached"
  140. if opts.length.min? and modelValue.length < opts.length.min
  141. isValid = false
  142. scope.complexPasswordErrorMessage = "Password too short, minimum #{opts.length.min}"
  143. ngModelCtrl.$setValidity('complexPassword', isValid)
  144. return modelValue