client_side_validations 3.1.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. data/client_side_validations.gemspec +4 -16
  2. data/lib/client_side_validations/action_view/form_builder.rb +31 -79
  3. data/lib/client_side_validations/action_view/form_helper.rb +59 -24
  4. data/lib/client_side_validations/active_model/exclusion.rb +14 -2
  5. data/lib/client_side_validations/active_model/format.rb +21 -0
  6. data/lib/client_side_validations/active_model/inclusion.rb +13 -2
  7. data/lib/client_side_validations/active_model/length.rb +4 -2
  8. data/lib/client_side_validations/active_model/numericality.rb +12 -1
  9. data/lib/client_side_validations/active_model.rb +100 -14
  10. data/lib/client_side_validations/active_record/middleware.rb +29 -3
  11. data/lib/client_side_validations/active_record/uniqueness.rb +4 -3
  12. data/lib/client_side_validations/active_record.rb +2 -1
  13. data/lib/client_side_validations/config.rb +9 -0
  14. data/lib/client_side_validations/core_ext/regexp.rb +1 -2
  15. data/lib/client_side_validations/generators/rails_validations.rb +15 -0
  16. data/lib/client_side_validations/generators.rb +12 -0
  17. data/lib/client_side_validations/middleware.rb +63 -24
  18. data/lib/client_side_validations/version.rb +1 -1
  19. data/lib/client_side_validations.rb +2 -2
  20. data/lib/generators/client_side_validations/copy_assets_generator.rb +58 -0
  21. data/lib/generators/client_side_validations/install_generator.rb +9 -21
  22. data/lib/generators/templates/client_side_validations/initializer.rb +2 -2
  23. data/vendor/assets/javascripts/rails.validations.js +474 -282
  24. metadata +156 -301
  25. data/lib/client_side_validations/formtastic.rb +0 -21
  26. data/lib/client_side_validations/mongo_mapper/middleware.rb +0 -20
  27. data/lib/client_side_validations/mongo_mapper/uniqueness.rb +0 -28
  28. data/lib/client_side_validations/mongo_mapper.rb +0 -9
  29. data/lib/client_side_validations/mongoid/middleware.rb +0 -20
  30. data/lib/client_side_validations/mongoid/uniqueness.rb +0 -28
  31. data/lib/client_side_validations/mongoid.rb +0 -9
  32. data/lib/client_side_validations/simple_form.rb +0 -24
  33. data/lib/generators/client_side_validations/copy_asset_generator.rb +0 -23
  34. data/lib/generators/templates/client_side_validations/README.rails.3.0 +0 -6
  35. data/lib/generators/templates/client_side_validations/README.rails.3.1 +0 -7
  36. data/test/action_view/cases/helper.rb +0 -176
  37. data/test/action_view/cases/test_helpers.rb +0 -600
  38. data/test/action_view/cases/test_legacy_helpers.rb +0 -217
  39. data/test/action_view/models/comment.rb +0 -35
  40. data/test/action_view/models/post.rb +0 -35
  41. data/test/action_view/models.rb +0 -3
  42. data/test/active_model/cases/helper.rb +0 -4
  43. data/test/active_model/cases/test_acceptance_validator.rb +0 -16
  44. data/test/active_model/cases/test_base.rb +0 -11
  45. data/test/active_model/cases/test_confirmation_validator.rb +0 -16
  46. data/test/active_model/cases/test_exclusion_validator.rb +0 -20
  47. data/test/active_model/cases/test_format_validator.rb +0 -21
  48. data/test/active_model/cases/test_inclusion_validator.rb +0 -21
  49. data/test/active_model/cases/test_length_validator.rb +0 -61
  50. data/test/active_model/cases/test_numericality_validator.rb +0 -46
  51. data/test/active_model/cases/test_presence_validator.rb +0 -16
  52. data/test/active_model/cases/test_validations.rb +0 -175
  53. data/test/active_model/models/person.rb +0 -17
  54. data/test/active_record/cases/helper.rb +0 -12
  55. data/test/active_record/cases/test_base.rb +0 -11
  56. data/test/active_record/cases/test_middleware.rb +0 -175
  57. data/test/active_record/cases/test_uniqueness_validator.rb +0 -50
  58. data/test/active_record/models/guid.rb +0 -7
  59. data/test/active_record/models/user.rb +0 -14
  60. data/test/base_helper.rb +0 -8
  61. data/test/core_ext/cases/test_core_ext.rb +0 -46
  62. data/test/formtastic/cases/helper.rb +0 -7
  63. data/test/formtastic/cases/test_form_builder.rb +0 -11
  64. data/test/formtastic/cases/test_form_helper.rb +0 -21
  65. data/test/generators/cases/test_generators.rb +0 -31
  66. data/test/javascript/config.ru +0 -3
  67. data/test/javascript/public/test/callbacks/elementAfter.js +0 -54
  68. data/test/javascript/public/test/callbacks/elementBefore.js +0 -54
  69. data/test/javascript/public/test/callbacks/elementFail.js +0 -70
  70. data/test/javascript/public/test/callbacks/elementPass.js +0 -70
  71. data/test/javascript/public/test/callbacks/formAfter.js +0 -45
  72. data/test/javascript/public/test/callbacks/formBefore.js +0 -45
  73. data/test/javascript/public/test/callbacks/formFail.js +0 -51
  74. data/test/javascript/public/test/callbacks/formPass.js +0 -50
  75. data/test/javascript/public/test/form_builders/validateForm.js +0 -66
  76. data/test/javascript/public/test/form_builders/validateFormtastic.js +0 -54
  77. data/test/javascript/public/test/form_builders/validateNestedForm.js +0 -66
  78. data/test/javascript/public/test/form_builders/validateSimpleForm.js +0 -57
  79. data/test/javascript/public/test/settings.js +0 -15
  80. data/test/javascript/public/test/validateElement.js +0 -179
  81. data/test/javascript/public/test/validators/acceptance.js +0 -42
  82. data/test/javascript/public/test/validators/confirmation.js +0 -25
  83. data/test/javascript/public/test/validators/exclusion.js +0 -41
  84. data/test/javascript/public/test/validators/format.js +0 -27
  85. data/test/javascript/public/test/validators/inclusion.js +0 -42
  86. data/test/javascript/public/test/validators/length.js +0 -76
  87. data/test/javascript/public/test/validators/numericality.js +0 -142
  88. data/test/javascript/public/test/validators/presence.js +0 -21
  89. data/test/javascript/public/test/validators/uniqueness.js +0 -96
  90. data/test/javascript/public/vendor/jquery.metadata.js +0 -122
  91. data/test/javascript/public/vendor/qunit.css +0 -196
  92. data/test/javascript/public/vendor/qunit.js +0 -1374
  93. data/test/javascript/server.rb +0 -84
  94. data/test/javascript/views/index.erb +0 -20
  95. data/test/javascript/views/layout.erb +0 -21
  96. data/test/middleware/cases/helper.rb +0 -18
  97. data/test/middleware/cases/test_middleware.rb +0 -8
  98. data/test/mongo_mapper/cases/helper.rb +0 -9
  99. data/test/mongo_mapper/cases/test_base.rb +0 -15
  100. data/test/mongo_mapper/cases/test_middleware.rb +0 -77
  101. data/test/mongo_mapper/cases/test_uniqueness_validator.rb +0 -50
  102. data/test/mongo_mapper/models/magazine.rb +0 -11
  103. data/test/mongoid/cases/helper.rb +0 -16
  104. data/test/mongoid/cases/test_base.rb +0 -15
  105. data/test/mongoid/cases/test_middleware.rb +0 -77
  106. data/test/mongoid/cases/test_uniqueness_validator.rb +0 -49
  107. data/test/mongoid/models/book.rb +0 -12
  108. data/test/simple_form/cases/helper.rb +0 -5
  109. data/test/simple_form/cases/test_form_builder.rb +0 -14
  110. data/test/simple_form/cases/test_form_helper.rb +0 -24
  111. data/test/test_loader.rb +0 -6
@@ -1,139 +1,278 @@
1
- /*!
2
- * Rails 3 Client Side Validations - v3.1.0
3
- * https://github.com/bcardarlela/client_side_validations
4
- *
5
- * Copyright (c) 2011 Brian Cardarella
6
- * Licensed under the MIT license
7
- * http://www.opensource.org/licenses/mit-license.php
8
- */
1
+ (function() {
2
+ var $, validateElement, validateForm, validatorsFor,
3
+ __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
9
4
 
10
- (function($) {
11
- $.fn.validate = function() {
12
- return this.filter('form[data-validate]').each(function() {
13
- var form = $(this);
14
- var settings = window[form.attr('id')];
15
-
16
- // Set up the events for the form
17
- form
18
- .submit(function() { return form.isValid(settings.validators); })
19
- .bind('ajax:beforeSend', function() { return form.isValid(settings.validators); })
20
- // Callbacks
21
- .bind('form:validate:after', function(eventData) { clientSideValidations.callbacks.form.after( form, eventData); })
22
- .bind('form:validate:before', function(eventData) { clientSideValidations.callbacks.form.before(form, eventData); })
23
- .bind('form:validate:fail', function(eventData) { clientSideValidations.callbacks.form.fail( form, eventData); })
24
- .bind('form:validate:pass', function(eventData) { clientSideValidations.callbacks.form.pass( form, eventData); })
5
+ $ = jQuery;
25
6
 
26
- // Set up the events for each validatable form element
27
- .find('[data-validate]:input:not(:radio)')
28
- .live('focusout', function() { $(this).isValid(settings.validators); })
29
- .live('change', function() { $(this).data('changed', true); })
30
- // Callbacks
31
- .live('element:validate:after', function(eventData) { clientSideValidations.callbacks.element.after( $(this), eventData); })
32
- .live('element:validate:before', function(eventData) { clientSideValidations.callbacks.element.before($(this), eventData); })
33
- .live('element:validate:fail', function(eventData, message) {
34
- var element = $(this);
35
- clientSideValidations.callbacks.element.fail(element, message, function() {
36
- addError(element, message);
37
- }, eventData) })
38
- .live('element:validate:pass', function(eventData) {
39
- var element = $(this);
40
- clientSideValidations.callbacks.element.pass(element, function() {
41
- removeError(element);
42
- }, eventData) })
43
- // Checkboxes - Live events don't support filter
44
- .end().find('[data-validate]:checkbox')
45
- .live('click', function() { $(this).isValid(settings.validators); })
46
- // Inputs for confirmations
47
- .end().find('[id*=_confirmation]').each(function() {
48
- var confirmationElement = $(this),
49
- element = form.find('#' + this.id.match(/(.+)_confirmation/)[1] + '[data-validate]:input');
7
+ $.fn.disableClientSideValidations = function() {
8
+ return ClientSideValidations.disable(this);
9
+ };
50
10
 
51
- if (element[0]) {
52
- $('#' + confirmationElement.attr('id'))
53
- .live('focusout', function() {
54
- element.data('changed', true).isValid(settings.validators);
55
- })
56
- .live('keyup', function() {
57
- element.data('changed', true).isValid(settings.validators);
58
- })
59
- }
60
- });
11
+ $.fn.enableClientSideValidations = function() {
12
+ this.filter('form[data-validate]').each(function() {
13
+ return ClientSideValidations.enablers.form(this);
14
+ });
15
+ return this.filter(':input:not(button)').each(function() {
16
+ return ClientSideValidations.enablers.input(this);
17
+ });
18
+ };
61
19
 
62
- var addError = function(element, message) {
63
- clientSideValidations.formBuilders[settings.type].add(element, settings, message);
64
- }
20
+ $.fn.resetClientSideValidations = function() {
21
+ return this.filter('form[data-validate]').each(function() {
22
+ return ClientSideValidations.reset(this);
23
+ });
24
+ };
65
25
 
66
- var removeError = function(element) {
67
- clientSideValidations.formBuilders[settings.type].remove(element, settings);
68
- }
26
+ $.fn.validate = function() {
27
+ return this.filter('form[data-validate]').each(function() {
28
+ return $(this).enableClientSideValidations();
69
29
  });
70
- }
30
+ };
71
31
 
72
32
  $.fn.isValid = function(validators) {
73
- if ($(this[0]).is('form')) {
74
- return validateForm($(this[0]), validators);
33
+ var obj;
34
+ obj = $(this[0]);
35
+ if (obj.is('form')) {
36
+ return validateForm(obj, validators);
75
37
  } else {
76
- return validateElement($(this[0]), validators[this[0].name]);
38
+ return validateElement(obj, validatorsFor(this[0].name, validators));
77
39
  }
78
- }
79
-
80
- var validateForm = function(form, validators) {
81
- var valid = true;
82
-
83
- form.trigger('form:validate:before').find('[data-validate]:input').each(function() {
84
- if (!$(this).isValid(validators)) { valid = false; }
40
+ };
41
+
42
+ validatorsFor = function(name, validators) {
43
+ name = name.replace(/_attributes\]\[\d+\]/g, "_attributes][]");
44
+ return validators[name] || {};
45
+ };
46
+
47
+ validateForm = function(form, validators) {
48
+ var valid;
49
+ form.trigger('form:validate:before.ClientSideValidations');
50
+ valid = true;
51
+ form.find(':input:enabled:visible[data-validate]').each(function() {
52
+ if (!$(this).isValid(validators)) {
53
+ valid = false;
54
+ }
55
+ return true;
85
56
  });
86
-
87
57
  if (valid) {
88
- form.trigger('form:validate:pass');
58
+ form.trigger('form:validate:pass.ClientSideValidations');
89
59
  } else {
90
- form.trigger('form:validate:fail');
60
+ form.trigger('form:validate:fail.ClientSideValidations');
91
61
  }
92
-
93
- form.trigger('form:validate:after');
62
+ form.trigger('form:validate:after.ClientSideValidations');
94
63
  return valid;
95
- }
96
-
97
- var validateElement = function(element, validators) {
98
- element.trigger('element:validate:before');
99
-
100
- if (element.data('changed') !== false) {
101
- var valid = true;
102
- element.data('changed', false);
103
-
104
- // Because 'length' is defined on the list of validators we cannot call jQuery.each on
105
- // the clientSideValidations.validators.all() object
106
- for (kind in clientSideValidations.validators.all()) {
107
- if (validators[kind] && (message = clientSideValidations.validators.all()[kind](element, validators[kind]))) {
108
- element.trigger('element:validate:fail', message).data('valid', false);
109
- valid = false;
110
- break;
64
+ };
65
+
66
+ validateElement = function(element, validators) {
67
+ var afterValidate, destroyInputName, executeValidators, failElement, local, passElement, remote;
68
+ element.trigger('element:validate:before.ClientSideValidations');
69
+ passElement = function() {
70
+ return element.trigger('element:validate:pass.ClientSideValidations').data('valid', null);
71
+ };
72
+ failElement = function(message) {
73
+ element.trigger('element:validate:fail.ClientSideValidations', message).data('valid', false);
74
+ return false;
75
+ };
76
+ afterValidate = function() {
77
+ return element.trigger('element:validate:after.ClientSideValidations').data('valid') !== false;
78
+ };
79
+ executeValidators = function(context) {
80
+ var fn, kind, message, valid, validator, _i, _len, _ref;
81
+ valid = true;
82
+ for (kind in context) {
83
+ fn = context[kind];
84
+ if (validators[kind]) {
85
+ _ref = validators[kind];
86
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
87
+ validator = _ref[_i];
88
+ if (message = fn.call(context, element, validator)) {
89
+ valid = failElement(message);
90
+ break;
91
+ }
92
+ }
93
+ if (!valid) {
94
+ break;
95
+ }
111
96
  }
112
97
  }
113
-
114
- if (valid) { element.data('valid', null); element.trigger('element:validate:pass'); }
98
+ return valid;
99
+ };
100
+ destroyInputName = element.attr('name').replace(/\[([^\]]*?)\]$/, '[_destroy]');
101
+ if ($("input[name='" + destroyInputName + "']").val() === "1") {
102
+ passElement();
103
+ return afterValidate();
104
+ }
105
+ if (element.data('changed') === false) {
106
+ return afterValidate();
115
107
  }
108
+ element.data('changed', false);
109
+ local = ClientSideValidations.validators.local;
110
+ remote = ClientSideValidations.validators.remote;
111
+ if (executeValidators(local) && executeValidators(remote)) {
112
+ passElement();
113
+ }
114
+ return afterValidate();
115
+ };
116
+
117
+ $(function() {
118
+ return $('form[data-validate]').validate();
119
+ });
116
120
 
117
- element.trigger('element:validate:after');
118
- return element.data('valid') === false ? false : true;
121
+ if (window.ClientSideValidations === void 0) {
122
+ window.ClientSideValidations = {};
119
123
  }
120
124
 
121
- // Main hook
122
- // If new forms are dynamically introduced into the DOM the .validate() method
123
- // must be invoked on that form
124
- $(function() { $('form[data-validate]').validate(); })
125
- })(jQuery);
125
+ if (window.ClientSideValidations.forms === void 0) {
126
+ window.ClientSideValidations.forms = {};
127
+ }
126
128
 
127
- var clientSideValidations = {
128
- validators: {
129
- all: function() { return jQuery.extend({}, clientSideValidations.validators.local, clientSideValidations.validators.remote) },
129
+ window.ClientSideValidations.reset = function(form) {
130
+ var $form, key;
131
+ $form = $(form);
132
+ ClientSideValidations.disable(form);
133
+ ClientSideValidations.disable($form.find(':input'));
134
+ for (key in form.ClientSideValidations.settings.validators) {
135
+ form.ClientSideValidations.removeError($form.find("[name='" + key + "']"));
136
+ }
137
+ return ClientSideValidations.enablers.form(form);
138
+ };
139
+
140
+ window.ClientSideValidations.disable = function(target) {
141
+ var $target;
142
+ $target = $(target);
143
+ $target.off('.ClientSideValidations');
144
+ $target.removeData('valid');
145
+ $target.removeData('changed');
146
+ return $target.filter(':input').each(function() {
147
+ return $(this).removeAttr('data-validate');
148
+ });
149
+ };
150
+
151
+ window.ClientSideValidations.enablers = {
152
+ form: function(form) {
153
+ var $form, binding, event, _ref;
154
+ $form = $(form);
155
+ form.ClientSideValidations = {
156
+ settings: window.ClientSideValidations.forms[$form.attr('id')],
157
+ addError: function(element, message) {
158
+ return ClientSideValidations.formBuilders[form.ClientSideValidations.settings.type].add(element, form.ClientSideValidations.settings, message);
159
+ },
160
+ removeError: function(element) {
161
+ return ClientSideValidations.formBuilders[form.ClientSideValidations.settings.type].remove(element, form.ClientSideValidations.settings);
162
+ }
163
+ };
164
+ _ref = {
165
+ 'submit.ClientSideValidations': function(eventData) {
166
+ if (!$form.isValid(form.ClientSideValidations.settings.validators)) {
167
+ eventData.preventDefault();
168
+ return eventData.stopImmediatePropagation();
169
+ }
170
+ },
171
+ 'ajax:beforeSend.ClientSideValidations': function(eventData) {
172
+ if (eventData.target === this) {
173
+ return $form.isValid(form.ClientSideValidations.settings.validators);
174
+ }
175
+ },
176
+ 'form:validate:after.ClientSideValidations': function(eventData) {
177
+ return ClientSideValidations.callbacks.form.after($form, eventData);
178
+ },
179
+ 'form:validate:before.ClientSideValidations': function(eventData) {
180
+ return ClientSideValidations.callbacks.form.before($form, eventData);
181
+ },
182
+ 'form:validate:fail.ClientSideValidations': function(eventData) {
183
+ return ClientSideValidations.callbacks.form.fail($form, eventData);
184
+ },
185
+ 'form:validate:pass.ClientSideValidations': function(eventData) {
186
+ return ClientSideValidations.callbacks.form.pass($form, eventData);
187
+ }
188
+ };
189
+ for (event in _ref) {
190
+ binding = _ref[event];
191
+ $form.on(event, binding);
192
+ }
193
+ return $form.find(':input').each(function() {
194
+ return ClientSideValidations.enablers.input(this);
195
+ });
196
+ },
197
+ input: function(input) {
198
+ var $form, $input, binding, event, form, _ref;
199
+ $input = $(input);
200
+ form = input.form;
201
+ $form = $(form);
202
+ _ref = {
203
+ 'focusout.ClientSideValidations': function() {
204
+ return $(this).isValid(form.ClientSideValidations.settings.validators);
205
+ },
206
+ 'change.ClientSideValidations': function() {
207
+ return $(this).data('changed', true);
208
+ },
209
+ 'element:validate:after.ClientSideValidations': function(eventData) {
210
+ return ClientSideValidations.callbacks.element.after($(this), eventData);
211
+ },
212
+ 'element:validate:before.ClientSideValidations': function(eventData) {
213
+ return ClientSideValidations.callbacks.element.before($(this), eventData);
214
+ },
215
+ 'element:validate:fail.ClientSideValidations': function(eventData, message) {
216
+ var element;
217
+ element = $(this);
218
+ return ClientSideValidations.callbacks.element.fail(element, message, function() {
219
+ return form.ClientSideValidations.addError(element, message);
220
+ }, eventData);
221
+ },
222
+ 'element:validate:pass.ClientSideValidations': function(eventData) {
223
+ var element;
224
+ element = $(this);
225
+ return ClientSideValidations.callbacks.element.pass(element, function() {
226
+ return form.ClientSideValidations.removeError(element);
227
+ }, eventData);
228
+ }
229
+ };
230
+ for (event in _ref) {
231
+ binding = _ref[event];
232
+ $input.filter(':enabled:not(:radio):not([id$=_confirmation]):visible:not(button)[name]').each(function() {
233
+ return $(this).attr('data-validate', true);
234
+ }).on(event, binding);
235
+ }
236
+ $input.filter(':checkbox:visible').on('click.ClientSideValidations', function() {
237
+ $(this).isValid(form.ClientSideValidations.settings.validators);
238
+ return true;
239
+ });
240
+ return $input.filter('[id$=_confirmation]:visible').each(function() {
241
+ var confirmationElement, element, _ref1, _results;
242
+ confirmationElement = $(this);
243
+ element = $form.find("#" + (this.id.match(/(.+)_confirmation/)[1]) + ":input");
244
+ if (element[0]) {
245
+ _ref1 = {
246
+ 'focusout.ClientSideValidations': function() {
247
+ return element.data('changed', true).isValid(form.ClientSideValidations.settings.validators);
248
+ },
249
+ 'keyup.ClientSideValidations': function() {
250
+ return element.data('changed', true).isValid(form.ClientSideValidations.settings.validators);
251
+ }
252
+ };
253
+ _results = [];
254
+ for (event in _ref1) {
255
+ binding = _ref1[event];
256
+ _results.push($("#" + (confirmationElement.attr('id'))).on(event, binding));
257
+ }
258
+ return _results;
259
+ }
260
+ });
261
+ }
262
+ };
263
+
264
+ window.ClientSideValidations.validators = {
265
+ all: function() {
266
+ return jQuery.extend({}, ClientSideValidations.validators.local, ClientSideValidations.validators.remote);
267
+ },
130
268
  local: {
131
269
  presence: function(element, options) {
132
- if (/^\s*$/.test(element.val() || "")) {
270
+ if (/^\s*$/.test(element.val() || '')) {
133
271
  return options.message;
134
272
  }
135
273
  },
136
274
  acceptance: function(element, options) {
275
+ var _ref;
137
276
  switch (element.attr('type')) {
138
277
  case 'checkbox':
139
278
  if (!element.attr('checked')) {
@@ -141,264 +280,317 @@ var clientSideValidations = {
141
280
  }
142
281
  break;
143
282
  case 'text':
144
- if (element.val() != (options.accept || '1')) {
283
+ if (element.val() !== (((_ref = options.accept) != null ? _ref.toString() : void 0) || '1')) {
145
284
  return options.message;
146
285
  }
147
- break;
148
286
  }
149
287
  },
150
288
  format: function(element, options) {
151
- if ((message = this.presence(element, options)) && options.allow_blank == true) {
152
- return;
153
- } else if (message) {
154
- return message;
155
- } else {
156
- if (options['with'] && !options['with'].test(element.val())) {
157
- return options.message;
158
- } else if (options['without'] && options['without'].test(element.val())) {
159
- return options.message;
289
+ var message;
290
+ message = this.presence(element, options);
291
+ if (message) {
292
+ if (options.allow_blank === true) {
293
+ return;
160
294
  }
295
+ return message;
296
+ }
297
+ if (options["with"] && !options["with"].test(element.val())) {
298
+ return options.message;
299
+ }
300
+ if (options.without && options.without.test(element.val())) {
301
+ return options.message;
161
302
  }
162
303
  },
163
304
  numericality: function(element, options) {
164
- if (!/^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d*)?$/.test(element.val())) {
305
+ var CHECKS, check, check_value, fn, form, operator, val;
306
+ val = jQuery.trim(element.val());
307
+ if (!ClientSideValidations.patterns.numericality.test(val)) {
308
+ if (options.allow_blank === true) {
309
+ return;
310
+ }
165
311
  return options.messages.numericality;
166
312
  }
167
-
168
- if (options.only_integer && !/^\d+$/.test(element.val())) {
313
+ if (options.only_integer && !/^[+-]?\d+$/.test(val)) {
169
314
  return options.messages.only_integer;
170
315
  }
171
-
172
- var CHECKS = { greater_than: '>', greater_than_or_equal_to: '>=',
173
- equal_to: '==', less_than: '<', less_than_or_equal_to: '<=' }
174
-
175
- for (var check in CHECKS) {
176
- if (options[check] != undefined && !(new Function("return " + element.val() + CHECKS[check] + options[check])())) {
316
+ CHECKS = {
317
+ greater_than: '>',
318
+ greater_than_or_equal_to: '>=',
319
+ equal_to: '==',
320
+ less_than: '<',
321
+ less_than_or_equal_to: '<='
322
+ };
323
+ form = $(element[0].form);
324
+ for (check in CHECKS) {
325
+ operator = CHECKS[check];
326
+ if (!(options[check] != null)) {
327
+ continue;
328
+ }
329
+ if (!isNaN(parseFloat(options[check])) && isFinite(options[check])) {
330
+ check_value = options[check];
331
+ } else if (form.find("[name*=" + options[check] + "]").size() === 1) {
332
+ check_value = form.find("[name*=" + options[check] + "]").val();
333
+ } else {
334
+ return;
335
+ }
336
+ fn = new Function("return " + val + " " + operator + " " + check_value);
337
+ if (!fn()) {
177
338
  return options.messages[check];
178
339
  }
179
340
  }
180
-
181
- if (options.odd && !(parseInt(element.val()) % 2)) {
341
+ if (options.odd && !(parseInt(val, 10) % 2)) {
182
342
  return options.messages.odd;
183
343
  }
184
-
185
- if (options.even && (parseInt(element.val()) % 2)) {
344
+ if (options.even && (parseInt(val, 10) % 2)) {
186
345
  return options.messages.even;
187
346
  }
188
347
  },
189
348
  length: function(element, options) {
190
- var blankOptions = {};
191
- if (options.is) {
192
- blankOptions.message = options.messages.is;
193
- } else if (options.minimum) {
194
- blankOptions.message = options.messages.minimum;
195
- }
196
- if ((message = this.presence(element, blankOptions)) && options.allow_blank == true) {
197
- return;
198
- } else if (message) {
349
+ var CHECKS, blankOptions, check, fn, message, operator, tokenized_length, tokenizer;
350
+ tokenizer = options.js_tokenizer || "split('')";
351
+ tokenized_length = new Function('element', "return (element.val()." + tokenizer + " || '').length")(element);
352
+ CHECKS = {
353
+ is: '==',
354
+ minimum: '>=',
355
+ maximum: '<='
356
+ };
357
+ blankOptions = {};
358
+ blankOptions.message = options.is ? options.messages.is : options.minimum ? options.messages.minimum : void 0;
359
+ message = this.presence(element, blankOptions);
360
+ if (message) {
361
+ if (options.allow_blank === true) {
362
+ return;
363
+ }
199
364
  return message;
200
- } else {
201
- var CHECKS = { is: '==', minimum: '>=', maximum: '<=' }
202
- var tokenizer = options.js_tokenizer || "split('')";
203
- var tokenized_length = new Function("element", "return (element.val()." + tokenizer + " || '').length;")(element);
204
-
205
- for (var check in CHECKS) {
206
- if (options[check] && !(new Function("return " + tokenized_length + CHECKS[check] + options[check])())) {
207
- return options.messages[check];
208
- }
365
+ }
366
+ for (check in CHECKS) {
367
+ operator = CHECKS[check];
368
+ if (!options[check]) {
369
+ continue;
370
+ }
371
+ fn = new Function("return " + tokenized_length + " " + operator + " " + options[check]);
372
+ if (!fn()) {
373
+ return options.messages[check];
209
374
  }
210
375
  }
211
376
  },
212
377
  exclusion: function(element, options) {
213
- if ((message = this.presence(element, options)) && options.allow_blank == true) {
214
- return;
215
- } else if (message) {
378
+ var lower, message, option, upper, _ref;
379
+ message = this.presence(element, options);
380
+ if (message) {
381
+ if (options.allow_blank === true) {
382
+ return;
383
+ }
216
384
  return message;
217
- } else {
218
- if (options['in']) {
219
- for (var i = 0; i < options['in'].length; i++) {
220
- if (options['in'][i] == element.val()) {
221
- return options.message;
222
- }
223
- }
224
- } else if (options['range']) {
225
- var lower = options['range'][0],
226
- upper = options['range'][1];
227
- if (element.val() >= lower && element.val() <= upper) {
228
- return options.message;
385
+ }
386
+ if (options["in"]) {
387
+ if (_ref = element.val(), __indexOf.call((function() {
388
+ var _i, _len, _ref1, _results;
389
+ _ref1 = options["in"];
390
+ _results = [];
391
+ for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
392
+ option = _ref1[_i];
393
+ _results.push(option.toString());
229
394
  }
395
+ return _results;
396
+ })(), _ref) >= 0) {
397
+ return options.message;
398
+ }
399
+ }
400
+ if (options.range) {
401
+ lower = options.range[0];
402
+ upper = options.range[1];
403
+ if (element.val() >= lower && element.val() <= upper) {
404
+ return options.message;
230
405
  }
231
406
  }
232
407
  },
233
408
  inclusion: function(element, options) {
234
- if ((message = this.presence(element, options)) && options.allow_blank == true) {
235
- return;
236
- } else if (message) {
409
+ var lower, message, option, upper, _ref;
410
+ message = this.presence(element, options);
411
+ if (message) {
412
+ if (options.allow_blank === true) {
413
+ return;
414
+ }
237
415
  return message;
238
- } else {
239
- if (options['in']) {
240
- for (var i = 0; i < options['in'].length; i++) {
241
- if (options['in'][i] == element.val()) {
242
- return;
243
- }
244
- }
245
- return options.message;
246
- } else if (options['range']) {
247
- var lower = options['range'][0],
248
- upper = options['range'][1];
249
-
250
- if (element.val() >= lower && element.val() <= upper) {
251
- return;
252
- } else {
253
- return options.message;
416
+ }
417
+ if (options["in"]) {
418
+ if (_ref = element.val(), __indexOf.call((function() {
419
+ var _i, _len, _ref1, _results;
420
+ _ref1 = options["in"];
421
+ _results = [];
422
+ for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
423
+ option = _ref1[_i];
424
+ _results.push(option.toString());
254
425
  }
426
+ return _results;
427
+ })(), _ref) >= 0) {
428
+ return;
255
429
  }
430
+ return options.message;
431
+ }
432
+ if (options.range) {
433
+ lower = options.range[0];
434
+ upper = options.range[1];
435
+ if (element.val() >= lower && element.val() <= upper) {
436
+ return;
437
+ }
438
+ return options.message;
256
439
  }
257
440
  },
258
441
  confirmation: function(element, options) {
259
- if (element.val() != jQuery('#' + element.attr('id') + '_confirmation').val()) {
442
+ if (element.val() !== jQuery("#" + (element.attr('id')) + "_confirmation").val()) {
260
443
  return options.message;
261
444
  }
445
+ },
446
+ uniqueness: function(element, options) {
447
+ var form, matches, name, name_prefix, name_suffix, valid, value;
448
+ name = element.attr('name');
449
+ if (/_attributes\]\[\d/.test(name)) {
450
+ matches = name.match(/^(.+_attributes\])\[\d+\](.+)$/);
451
+ name_prefix = matches[1];
452
+ name_suffix = matches[2];
453
+ value = element.val();
454
+ if (name_prefix && name_suffix) {
455
+ form = element.closest('form');
456
+ valid = true;
457
+ form.find(':input[name^="' + name_prefix + '"][name$="' + name_suffix + '"]').each(function() {
458
+ if ($(this).attr('name') !== name) {
459
+ if ($(this).val() === value) {
460
+ valid = false;
461
+ return $(this).data('notLocallyUnique', true);
462
+ } else {
463
+ if ($(this).data('notLocallyUnique')) {
464
+ return $(this).removeData('notLocallyUnique').data('changed', true);
465
+ }
466
+ }
467
+ }
468
+ });
469
+ if (!valid) {
470
+ return options.message;
471
+ }
472
+ }
473
+ }
262
474
  }
263
475
  },
264
476
  remote: {
265
477
  uniqueness: function(element, options) {
266
- var data = {};
267
- data['case_sensitive'] = !!options.case_sensitive;
478
+ var data, key, message, name, scope_value, scoped_element, scoped_name, _ref;
479
+ message = ClientSideValidations.validators.local.presence(element, options);
480
+ if (message) {
481
+ if (options.allow_blank === true) {
482
+ return;
483
+ }
484
+ return message;
485
+ }
486
+ data = {};
487
+ data.case_sensitive = !!options.case_sensitive;
268
488
  if (options.id) {
269
- data['id'] = options.id;
489
+ data.id = options.id;
270
490
  }
271
-
272
491
  if (options.scope) {
273
- data.scope = {}
274
- for (key in options.scope) {
275
- var scoped_element = jQuery('[name="' + element.attr('name').replace(/\[\w+]$/, '[' + key + ']' + '"]'));
276
- if (scoped_element[0] && scoped_element.val() != options.scope[key]) {
492
+ data.scope = {};
493
+ _ref = options.scope;
494
+ for (key in _ref) {
495
+ scope_value = _ref[key];
496
+ scoped_name = element.attr('name').replace(/\[\w+\]$/, "[" + key + "]");
497
+ scoped_element = jQuery("[name='" + scoped_name + "']");
498
+ jQuery("[name='" + scoped_name + "']:checkbox").each(function() {
499
+ if (this.checked) {
500
+ return scoped_element = this;
501
+ }
502
+ });
503
+ if (scoped_element[0] && scoped_element.val() !== scope_value) {
277
504
  data.scope[key] = scoped_element.val();
278
- scoped_element.unbind('change.' + element.id).bind('change.' + element.id, function() { element.trigger('change'); element.trigger('focusout'); });
505
+ scoped_element.unbind("change." + element.id).bind("change." + element.id, function() {
506
+ element.trigger('change.ClientSideValidations');
507
+ return element.trigger('focusout.ClientSideValidations');
508
+ });
279
509
  } else {
280
- data.scope[key] = options.scope[key];
510
+ data.scope[key] = scope_value;
281
511
  }
282
512
  }
283
513
  }
284
-
285
- // Kind of a hack but this will isolate the resource name and attribute.
286
- // e.g. user[records_attributes][0][title] => records[title]
287
- // e.g. user[record_attributes][title] => record[title]
288
- // Server side handles classifying the resource properly
289
- if (/_attributes]/.test(element.attr('name'))) {
290
- var name = element.attr('name').match(/\[\w+_attributes]/g).pop().match(/\[(\w+)_attributes]/).pop();
291
- name += /(\[\w+])$/.exec(element.attr('name'))[1];
514
+ if (/_attributes\]/.test(element.attr('name'))) {
515
+ name = element.attr('name').match(/\[\w+_attributes\]/g).pop().match(/\[(\w+)_attributes\]/).pop();
516
+ name += /(\[\w+\])$/.exec(element.attr('name'))[1];
292
517
  } else {
293
- var name = element.attr('name');
518
+ name = element.attr('name');
294
519
  }
295
-
296
- // Override the name if a nested module class is passed
297
520
  if (options['class']) {
298
- name = options['class'] + '[' + name.split('[')[1]
521
+ name = options['class'] + '[' + name.split('[')[1];
299
522
  }
300
523
  data[name] = element.val();
301
-
302
524
  if (jQuery.ajax({
303
525
  url: '/validators/uniqueness',
304
526
  data: data,
305
527
  async: false
306
- }).status == 200) {
528
+ }).status === 200) {
307
529
  return options.message;
308
530
  }
309
531
  }
310
532
  }
311
- },
312
- formBuilders: {
533
+ };
534
+
535
+ window.ClientSideValidations.formBuilders = {
313
536
  'ActionView::Helpers::FormBuilder': {
314
537
  add: function(element, settings, message) {
315
- if (element.data('valid') !== false && jQuery('label.message[for="' + element.attr('id') + '"]')[0] == undefined) {
316
- var inputErrorField = jQuery(settings.input_tag),
317
- labelErrorField = jQuery(settings.label_tag),
318
- label = jQuery('label[for="' + element.attr('id') + '"]:not(.message)');
319
-
320
- if (element.attr('autofocus')) { element.attr('autofocus', false) };
538
+ var form, inputErrorField, label, labelErrorField;
539
+ form = $(element[0].form);
540
+ if (element.data('valid') !== false && !(form.find("label.message[for='" + (element.attr('id')) + "']")[0] != null)) {
541
+ inputErrorField = jQuery(settings.input_tag);
542
+ labelErrorField = jQuery(settings.label_tag);
543
+ label = form.find("label[for='" + (element.attr('id')) + "']:not(.message)");
544
+ if (element.attr('autofocus')) {
545
+ element.attr('autofocus', false);
546
+ }
321
547
  element.before(inputErrorField);
322
548
  inputErrorField.find('span#input_tag').replaceWith(element);
323
549
  inputErrorField.find('label.message').attr('for', element.attr('id'));
324
550
  labelErrorField.find('label.message').attr('for', element.attr('id'));
325
- label.replaceWith(labelErrorField);
551
+ labelErrorField.insertAfter(label);
326
552
  labelErrorField.find('label#label_tag').replaceWith(label);
327
553
  }
328
- jQuery('label.message[for="' + element.attr('id') + '"]').text(message);
554
+ return form.find("label.message[for='" + (element.attr('id')) + "']").text(message);
329
555
  },
330
556
  remove: function(element, settings) {
331
- var errorFieldClass = jQuery(settings.input_tag).attr('class'),
332
- inputErrorField = element.closest('.' + errorFieldClass),
333
- label = jQuery('label[for="' + element.attr('id') + '"]:not(.message)'),
334
- labelErrorField = label.closest('.' + errorFieldClass);
335
-
557
+ var errorFieldClass, form, inputErrorField, label, labelErrorField;
558
+ form = $(element[0].form);
559
+ errorFieldClass = jQuery(settings.input_tag).attr('class');
560
+ inputErrorField = element.closest("." + (errorFieldClass.replace(" ", ".")));
561
+ label = form.find("label[for='" + (element.attr('id')) + "']:not(.message)");
562
+ labelErrorField = label.closest("." + errorFieldClass);
336
563
  if (inputErrorField[0]) {
337
- inputErrorField.find('#' + element.attr('id')).detach();
564
+ inputErrorField.find("#" + (element.attr('id'))).detach();
338
565
  inputErrorField.replaceWith(element);
339
566
  label.detach();
340
- labelErrorField.replaceWith(label);
341
- }
342
- }
343
- },
344
- 'SimpleForm::FormBuilder': {
345
- add: function(element, settings, message) {
346
- if (element.data('valid') !== false) {
347
- var wrapper = element.closest(settings.wrapper_tag);
348
- wrapper.addClass(settings.wrapper_error_class);
349
- var errorElement = $('<' + settings.error_tag + ' class="' + settings.error_class + '">' + message + '</' + settings.error_tag + '>');
350
- wrapper.append(errorElement);
351
- } else {
352
- element.parent().find(settings.error_tag + '.' + settings.error_class).text(message);
567
+ return labelErrorField.replaceWith(label);
353
568
  }
354
- },
355
- remove: function(element, settings) {
356
- var wrapper = element.closest(settings.wrapper_tag + '.' + settings.wrapper_error_class);
357
- wrapper.removeClass(settings.wrapper_error_class);
358
- var errorElement = wrapper.find(settings.error_tag + '.' + settings.error_class);
359
- errorElement.remove();
360
569
  }
570
+ }
571
+ };
361
572
 
362
- },
363
- 'Formtastic::FormBuilder': {
364
- add: function(element, settings, message) {
365
- if (element.data('valid') !== false) {
366
- var wrapper = element.closest('li');
367
- wrapper.addClass('error');
368
- var errorElement = $('<p class="' + settings.inline_error_class + '">' + message + '</p>');
369
- wrapper.append(errorElement);
370
- } else {
371
- element.parent().find('p.' + settings.inline_error_class).text(message);
372
- }
373
- },
374
- remove: function(element, settings) {
375
- var wrapper = element.closest('li.error');
376
- wrapper.removeClass('error');
377
- var errorElement = wrapper.find('p.' + settings.inline_error_class);
378
- errorElement.remove();
379
- }
380
- },
381
- 'NestedForm::Builder': {
382
- add: function(element, settings, message) {
383
- clientSideValidations.formBuilders['ActionView::Helpers::FormBuilder'].add(element, settings, message);
573
+ window.ClientSideValidations.patterns = {
574
+ numericality: /^(-|\+)?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d*)?$/
575
+ };
576
+
577
+ window.ClientSideValidations.callbacks = {
578
+ element: {
579
+ after: function(element, eventData) {},
580
+ before: function(element, eventData) {},
581
+ fail: function(element, message, addError, eventData) {
582
+ return addError();
384
583
  },
385
- remove: function(element, settings, message) {
386
- clientSideValidations.formBuilders['ActionView::Helpers::FormBuilder'].remove(element, settings, message);
584
+ pass: function(element, removeError, eventData) {
585
+ return removeError();
387
586
  }
388
- }
389
- },
390
- callbacks: {
391
- element: {
392
- after: function(element, eventData) { },
393
- before: function(element, eventData) { },
394
- fail: function(element, message, addError, eventData) { addError() },
395
- pass: function(element, removeError, eventData) { removeError() }
396
587
  },
397
588
  form: {
398
- after: function(form, eventData) { },
399
- before: function(form, eventData) { },
400
- fail: function(form, eventData) { },
401
- pass: function(form, eventData) { }
589
+ after: function(form, eventData) {},
590
+ before: function(form, eventData) {},
591
+ fail: function(form, eventData) {},
592
+ pass: function(form, eventData) {}
402
593
  }
403
- }
404
- };
594
+ };
595
+
596
+ }).call(this);