private.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. /**
  2. * Private methods
  3. */
  4. module.exports = {
  5. _init: function() {
  6. var self = this;
  7. this.$form = $(this.el);
  8. this.$fields = $();
  9. this.$inputs = $();
  10. this._extend($.idealforms.extensions);
  11. this._i18n();
  12. this._inject('_init');
  13. this._addMarkupRules();
  14. this.addRules(this.opts.rules || {});
  15. this.$form.submit(function(e) {
  16. self._validateAll();
  17. self.focusFirstInvalid();
  18. self.opts.onSubmit.call(self, self.getInvalid().length, e);
  19. });
  20. if (! this.opts.silentLoad) {
  21. // 1ms timeout to make sure error shows up
  22. setTimeout($.proxy(this.focusFirstInvalid, this), 1);
  23. }
  24. },
  25. _addMarkupRules: function() {
  26. var rules = {};
  27. this.$form.find('input, select, textarea').each(function() {
  28. var rule = $(this).data('idealforms-rules');
  29. if (rule && ! rules[this.name]) rules[this.name] = rule;
  30. });
  31. this.addRules(rules);
  32. },
  33. _i18n: function() {
  34. var self = this;
  35. $.each($.idealforms.i18n, function(locale, lang) {
  36. var errors = lang.errors
  37. , options = {};
  38. delete lang.errors;
  39. for (var ext in lang) options[ext] = { i18n: lang[ext] };
  40. $.extend($.idealforms.errors, errors);
  41. $.extend(true, self.opts, options);
  42. });
  43. },
  44. _buildField: function(input) {
  45. var self = this
  46. , $field = this._getField(input)
  47. , $icon;
  48. $icon = $(this.opts.iconHtml, {
  49. class: this.opts.iconClass,
  50. click: function(){ $(input).focus() }
  51. });
  52. if (! this.$fields.filter($field).length) {
  53. this.$fields = this.$fields.add($field);
  54. if (this.opts.iconHtml) $field.append($icon);
  55. $field.addClass('idealforms-field idealforms-field-'+ input.type);
  56. }
  57. this._addEvents(input);
  58. this._inject('_buildField', input);
  59. },
  60. _addEvents: function(input) {
  61. var self = this
  62. , $field = this._getField(input);
  63. $(input)
  64. .on('change keyup', function(e) {
  65. if (e.which == 9 || e.which == 16) return;
  66. self._validate(this, true, true);
  67. })
  68. .focus(function() {
  69. if (! self.isValid(this.name)) {
  70. $field.find(self.opts.error).show();
  71. }
  72. })
  73. .blur(function() {
  74. $field.find(self.opts.error).hide();
  75. });
  76. },
  77. _isRequired: function(input) {
  78. // We assume non-text inputs with rules are required
  79. if ($(input).is(':checkbox, :radio, select')) return true;
  80. return this.opts.rules[input.name].indexOf('required') > -1;
  81. },
  82. _getRelated: function(input) {
  83. return this._getField(input).find('[name="'+ input.name +'"]');
  84. },
  85. _getField: function(input) {
  86. return $(input).closest(this.opts.field);
  87. },
  88. _getFirstInvalid: function() {
  89. return this.getInvalid().first().find('input:first, textarea, select');
  90. },
  91. _handleError: function(input, error, valid) {
  92. valid = valid || this.isValid(input.name);
  93. var $error = this._getField(input).find(this.opts.error);
  94. this.$form.find(this.opts.error).hide();
  95. if (error) $error.text(error);
  96. $error.toggle(!valid);
  97. },
  98. _handleStyle: function(input, valid) {
  99. valid = valid || this.isValid(input.name);
  100. this._getField(input)
  101. .removeClass(this.opts.validClass +' '+ this.opts.invalidClass)
  102. .addClass(valid ? this.opts.validClass : this.opts.invalidClass)
  103. .find('.'+ this.opts.iconClass).show();
  104. },
  105. _fresh: function(input) {
  106. this._getField(input)
  107. .removeClass(this.opts.validClass +' '+ this.opts.invalidClass)
  108. .find(this.opts.error).hide()
  109. .end()
  110. .find('.'+ this.opts.iconClass).toggle(this._isRequired(input));
  111. },
  112. _validate: function(input, handleError, handleStyle) {
  113. var self = this
  114. , $field = this._getField(input)
  115. , userRules = this.opts.rules[input.name].split($.idealforms.ruleSeparator)
  116. , oldValue = $field.data('idealforms-value')
  117. , valid = true
  118. , rule;
  119. // Don't validate input if value hasn't changed
  120. if (! $(input).is(':checkbox, :radio') && oldValue == input.value) {
  121. return $field.data('idealforms-valid');
  122. }
  123. $field.data('idealforms-value', input.value);
  124. // Non-required input with empty value must pass validation
  125. if (! input.value && ! this._isRequired(input)) {
  126. $field.removeData('idealforms-valid');
  127. this._fresh(input);
  128. // Inputs with value or required
  129. } else {
  130. $.each(userRules, function(i, userRule) {
  131. userRule = userRule.split($.idealforms.argSeparator);
  132. rule = userRule[0];
  133. var theRule = $.idealforms.rules[rule]
  134. , args = userRule.slice(1)
  135. , error;
  136. error = $.idealforms._format.apply(null, [
  137. $.idealforms._getKey('errors.'+ input.name +'.'+ rule, self.opts) ||
  138. $.idealforms.errors[rule]
  139. ].concat(args));
  140. valid = typeof theRule == 'function'
  141. ? theRule.apply(self, [input, input.value].concat(args))
  142. : theRule.test(input.value);
  143. $field.data('idealforms-valid', valid);
  144. if (handleError) self._handleError(input, error, valid);
  145. if (handleStyle) self._handleStyle(input, valid);
  146. self.opts.onValidate.call(self, input, rule, valid);
  147. return valid;
  148. });
  149. }
  150. this._inject('_validate', input, rule, valid);
  151. return valid;
  152. },
  153. _validateAll: function() {
  154. var self = this;
  155. this.$inputs.each(function(){ self._validate(this, true); });
  156. }
  157. };