client_side_validations 8.0.2 → 9.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fed0feff4a513664392a746868722e04d5812444
4
- data.tar.gz: b0c93e7e5a06f28666f63241b9a446378ce39376
3
+ metadata.gz: 9517f4c6685202d257a67fed4948d6ddc6663104
4
+ data.tar.gz: 2a185c425e483fc2b2776da0715c9312bb6abf0c
5
5
  SHA512:
6
- metadata.gz: d92f7babb26e804be9a632593c70319dff02af7a9332f185eb6fbc9a894dc16195729eb7df31d50fea765d8c16a1b89f93f9337879347ace22654334e09b87d1
7
- data.tar.gz: 6aef78c42b2ab083780fdcdc4379597b6a370e026b5ad7b3b96e84bf85fdc1591457a77c4b5b2bb15c5a0f4c5b3fafef76df31eeaf6d59f846fd80bd9aea51fb
6
+ metadata.gz: 746dabde16b38f4b5941016d38ada3108ea3884b1c20ae655bf24aa3dddcc4cfd752403c77dfd09beba577180df71ff696f1809b1ac8d12c71d2c24364453f3d
7
+ data.tar.gz: 7a6527a1c051063414572982dac3c3372e301a04f94b766d56f26b396214110deeda09e7947ea1bbdc1c0a5df81f2277642abe193b17c3ef897b0203c44e4214
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 9.0.0 (2017-01-31)
4
+
5
+ * Unobtrusive JavaScript
6
+
3
7
  ## 8.0.2 (2017-01-31)
4
8
 
5
9
  * Under the hood improvements
data/README.md CHANGED
@@ -72,9 +72,7 @@ Rails `FormBuilders`. Please see the [Plugin wiki page](https://github.com/DavyJ
72
72
  (feel free to add your own)
73
73
 
74
74
  * [SimpleForm](https://github.com/DavyJonesLocker/client_side_validations-simple_form)
75
- * [Formtastic](https://github.com/DavyJonesLocker/client_side_validations-formtastic)
76
75
  * [Mongoid](https://github.com/DavyJonesLocker/client_side_validations-mongoid)
77
- * [MongoMapper](https://github.com/DavyJonesLocker/client_side_validations-mongo_mapper)
78
76
 
79
77
  ## Usage ##
80
78
 
@@ -98,21 +96,6 @@ In your `FormBuilder` you only need to enable validations:
98
96
 
99
97
  That should be enough to get you going.
100
98
 
101
- By default the validators will be serialized and embedded in a
102
- `<script>` tag following the `<form>` tag. If you would like to render
103
- that `<script>` tag elsewhere you can do this by passing a name to
104
- `:validate`
105
-
106
- ```erb
107
- <%= form_for @user, validate: :user_validators do |f| %>
108
- ```
109
-
110
- The `<script`> tag is added to `content_for()` with the name you passed,
111
- so you can simply render that anywhere you like:
112
-
113
- ```erb
114
- <%= yield(:user_validators) %>
115
- ```
116
99
 
117
100
  ## Conditional Validators ##
118
101
 
@@ -187,20 +170,15 @@ You can even turn them off per fieldset:
187
170
  ...
188
171
  ```
189
172
 
190
- ## Understanding the embedded `<script>` tag ##
173
+ ## Understanding the client side validations data attribute ##
191
174
 
192
- A rendered form with validations will always have a `<script>` appended
193
- directly after:
194
-
195
- ```html
196
- <script>//<![CDATA[if(window.ClientSideValidations==undefined)window.ClientSideValidations={};if(window.ClientSideValidations.forms==undefined)window.ClientSideValidations.forms={};window.ClientSideValidations.forms['new_person'] = {"type":"ActionView::Helpers::FormBuilder","input_tag":"<div class=\"field_with_errors\"><span id=\"input_tag\" /><label for=\"\" class=\"message\"></label></div>","label_tag":"<div class=\"field_with_errors\"><label id=\"label_tag\" /></div>","validators":{"person[name]":{"inclusion":[{"message":"is not included in the list","in":["Happy"]}]}}};//]]></script>
197
- ```
175
+ A rendered form with validations will always have a `data-client-side-validations` attribute.
198
176
 
199
- This script registers a new form object on `ClientSideValidations.form`. The key is equal to the ID of the form that is rendered. The objects it contains will have different keys depending upon the `FormBuilder` being used. However, `type` and `validators` will always be present.
177
+ The objects it contains will have different keys depending upon the `FormBuilder` being used. However, `html_settings` and `validators` will always be present.
200
178
 
201
- ### `type` ###
179
+ ### `html_settings` ###
202
180
 
203
- This will always be equal to the class of the `FormBuilder` that did the rendering. The type will be used by the JavaScript to determine how to `add` and `remove` the error messages. If you create a new `FormBuilder` you will need to write your own handlers for adding and removing.
181
+ This will always contain the type to the class of the `FormBuilder` that did the rendering. The type will be used by the JavaScript to determine how to `add` and `remove` the error messages. If you create a new `FormBuilder`, you will need to write your own handlers for adding and removing.
204
182
 
205
183
  ### `validators` ###
206
184
 
@@ -7,9 +7,7 @@ end
7
7
 
8
8
  require 'client_side_validations/core_ext'
9
9
  require 'client_side_validations/action_view/form_helper'
10
- require 'client_side_validations/action_view/form_tag_helper'
11
10
  require 'client_side_validations/action_view/form_builder'
12
11
 
13
12
  ActionView::Base.send(:include, ClientSideValidations::ActionView::Helpers::FormHelper)
14
- ActionView::Base.send(:include, ClientSideValidations::ActionView::Helpers::FormTagHelper)
15
13
  ActionView::Helpers::FormBuilder.send(:prepend, ClientSideValidations::ActionView::Helpers::FormBuilder)
@@ -9,40 +9,29 @@ module ClientSideValidations
9
9
  def form_for(record, options = {}, &block)
10
10
  return super unless options[:validate]
11
11
 
12
- turn_off_html5_validations! options
12
+ # We are not going to use super here, because we need
13
+ # to inject the csv options in a data attribute in a clean way.
14
+ # So we basically reimplement the whole form_for method
15
+ raise ArgumentError, 'Missing block' unless block_given?
16
+ html_options = options[:html] ||= {}
13
17
 
14
- case record
15
- when String, Symbol
16
- raise ClientSideValidations::ActionView::Helpers::FormHelper::Error, 'Using form_for(:name, @resource) is not supported with ClientSideValidations. Please use form_for(@resource, as: :name) instead.'
17
- else
18
- object = record.is_a?(Array) ? record.last : record
19
- object_name = options[:as] || model_name_from_record_or_class(object).param_key
20
- end
18
+ # Moving the switch statement to another method to
19
+ # lower complexity
20
+ object, object_name = check_record(record, options)
21
21
 
22
22
  @validators = {}
23
23
 
24
- # Order matters here. Rails mutates the `options` object
25
- form = super
24
+ apply_html_options! options, html_options
26
25
 
27
- build_bound_validators! options
28
26
  builder = instantiate_builder(object_name, object, options)
29
- script = client_side_form_settings(options, builder)
27
+ output = capture(builder, &block)
28
+ html_options[:multipart] ||= builder.multipart?
30
29
 
31
- if assign_script_to_content_for(options[:validate], script)
32
- form
33
- else
34
- form << script
35
- end
36
- end
37
-
38
- def assign_script_to_content_for(name, script)
39
- return false if name == true
40
-
41
- # rubocop:disable OutputSafety
42
- content_for name, script.html_safe
43
- # rubocop:enable OutputSafety
30
+ build_bound_validators! options
44
31
 
45
- true
32
+ apply_csv_html_options! html_options, options, builder
33
+ html_options = html_options_for_form(options[:url] || {}, html_options)
34
+ form_tag_with_body(html_options, output)
46
35
  end
47
36
 
48
37
  def apply_form_for_options!(record, object, options)
@@ -61,6 +50,20 @@ module ClientSideValidations
61
50
 
62
51
  private
63
52
 
53
+ def check_record(record, options)
54
+ case record
55
+ when String, Symbol
56
+ raise ClientSideValidations::ActionView::Helpers::FormHelper::Error, 'Using form_for(:name, @resource) is not supported with ClientSideValidations. Please use form_for(@resource, as: :name) instead.'
57
+ else
58
+ object = record.is_a?(Array) ? record.last : record
59
+ raise ArgumentError, 'First argument in form cannot contain nil or be empty' unless object
60
+ object_name = options[:as] || model_name_from_record_or_class(object).param_key
61
+ apply_form_for_options!(record, object, options)
62
+ end
63
+
64
+ [object, object_name]
65
+ end
66
+
64
67
  def build_bound_validators!(options)
65
68
  return unless @validators
66
69
 
@@ -90,26 +93,35 @@ module ClientSideValidations
90
93
  end
91
94
  end
92
95
 
93
- def client_side_form_settings(options, builder)
94
- javascript_tag "if(window.ClientSideValidations===undefined)window.ClientSideValidations={};window.ClientSideValidations.disabled_validators=#{ClientSideValidations::Config.disabled_validators.to_json};window.ClientSideValidations.number_format=#{number_format.to_json};if(window.ClientSideValidations.patterns===undefined)window.ClientSideValidations.patterns = {};window.ClientSideValidations.patterns.numericality=#{numericality_patterns};#{"if(window.ClientSideValidations.remote_validators_prefix===undefined)window.ClientSideValidations.remote_validators_prefix='#{ClientSideValidations::Config.root_path.sub(%r{/+\Z}, '')}';" if ClientSideValidations::Config.root_path.present?}if(window.ClientSideValidations.forms===undefined)window.ClientSideValidations.forms={};window.ClientSideValidations.forms['#{options[:html]['id']}'] = #{builder.client_side_form_settings(options, self).merge(validators: construct_validators).to_json};"
95
- end
96
-
97
96
  def number_format
98
- @number_format ||=
99
- if ClientSideValidations::Config.number_format_with_locale && defined?(I18n)
100
- I18n.t('number.format').slice :separator, :delimiter
101
- else
102
- { separator: '.', delimiter: ',' }
103
- end
97
+ if ClientSideValidations::Config.number_format_with_locale && defined?(I18n)
98
+ I18n.t('number.format').slice :separator, :delimiter
99
+ else
100
+ { separator: '.', delimiter: ',' }
101
+ end
104
102
  end
105
103
 
106
- def numericality_patterns
107
- "/^(-|\\+)?(?:\\d+|\\d{1,3}(?:\\#{number_format[:delimiter]}\\d{3})+)(?:\\#{number_format[:separator]}\\d*)?$/"
104
+ def apply_html_options!(options, html_options)
105
+ # Turn off HTML5 validations
106
+ html_options[:novalidate] = 'novalidate'
107
+
108
+ html_options[:data] = options.delete(:data) if options.key?(:data)
109
+ html_options[:remote] = options.delete(:remote) if options.key?(:remote)
110
+ html_options[:method] = options.delete(:method) if options.key?(:method)
111
+ html_options[:enforce_utf8] = options.delete(:enforce_utf8) if options.key?(:enforce_utf8)
112
+ html_options[:authenticity_token] = options.delete(:authenticity_token)
108
113
  end
109
114
 
110
- def turn_off_html5_validations!(options)
111
- options[:html] ||= {}
112
- options[:html][:novalidate] = 'novalidate'
115
+ def apply_csv_html_options!(html_options, options, builder)
116
+ html_options.delete :validate
117
+
118
+ csv_options = {
119
+ html_settings: builder.client_side_form_settings(options, self),
120
+ number_format: number_format,
121
+ validators: construct_validators
122
+ }
123
+
124
+ html_options['data-client-side-validations'] = csv_options.to_json
113
125
  end
114
126
  end
115
127
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module ClientSideValidations
3
- VERSION = '8.0.2'.freeze
3
+ VERSION = '9.0.0'.freeze
4
4
  end
@@ -1,12 +1,12 @@
1
1
 
2
2
  /*!
3
- * Client Side Validations - v8.0.2 (https://github.com/DavyJonesLocker/client_side_validations)
3
+ * Client Side Validations - v9.0.0 (https://github.com/DavyJonesLocker/client_side_validations)
4
4
  * Copyright (c) 2017 Geremia Taglialatela, Brian Cardarella
5
5
  * Licensed under MIT (http://opensource.org/licenses/mit-license.php)
6
6
  */
7
7
 
8
8
  (function() {
9
- var $, validateElement, validateForm, validatorsFor,
9
+ var $, ClientSideValidations, initializeOnEvent, validateElement, validateForm, validatorsFor,
10
10
  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; };
11
11
 
12
12
  $ = jQuery;
@@ -50,6 +50,8 @@
50
50
  }
51
51
  };
52
52
 
53
+ initializeOnEvent = (window.Turbolinks != null) && window.Turbolinks.supported ? window.Turbolinks.EVENTS != null ? 'page:change' : 'turbolinks:load' : 'ready';
54
+
53
55
  validatorsFor = function(name, validators) {
54
56
  var captures, validator, validator_name;
55
57
  if (captures = name.match(/\[(\w+_attributes)\].*\[(\w+)\]$/)) {
@@ -135,471 +137,434 @@
135
137
  return afterValidate();
136
138
  };
137
139
 
138
- if (window.ClientSideValidations === void 0) {
139
- window.ClientSideValidations = {};
140
- }
141
-
142
- if (window.ClientSideValidations.forms === void 0) {
143
- window.ClientSideValidations.forms = {};
144
- }
145
-
146
- window.ClientSideValidations.selectors = {
147
- inputs: ':input:not(button):not([type="submit"])[name]:visible:enabled',
148
- validate_inputs: ':input:enabled:visible[data-validate]',
149
- forms: 'form[data-validate]'
150
- };
151
-
152
- window.ClientSideValidations.reset = function(form) {
153
- var $form, key;
154
- $form = $(form);
155
- ClientSideValidations.disable(form);
156
- for (key in form.ClientSideValidations.settings.validators) {
157
- form.ClientSideValidations.removeError($form.find("[name='" + key + "']"));
158
- }
159
- return ClientSideValidations.enablers.form(form);
160
- };
161
-
162
- window.ClientSideValidations.disable = function(target) {
163
- var $target;
164
- $target = $(target);
165
- $target.off('.ClientSideValidations');
166
- if ($target.is('form')) {
167
- return ClientSideValidations.disable($target.find(':input'));
168
- } else {
169
- $target.removeData('valid');
170
- $target.removeData('changed');
171
- return $target.filter(':input').each(function() {
172
- return $(this).removeAttr('data-validate');
173
- });
174
- }
175
- };
176
-
177
- window.ClientSideValidations.enablers = {
178
- form: function(form) {
179
- var $form, binding, event, ref;
180
- $form = $(form);
181
- form.ClientSideValidations = {
182
- settings: window.ClientSideValidations.forms[$form.attr('id')],
183
- addError: function(element, message) {
184
- return ClientSideValidations.formBuilders[form.ClientSideValidations.settings.type].add(element, form.ClientSideValidations.settings, message);
140
+ ClientSideValidations = {
141
+ callbacks: {
142
+ element: {
143
+ after: function(element, eventData) {},
144
+ before: function(element, eventData) {},
145
+ fail: function(element, message, addError, eventData) {
146
+ return addError();
185
147
  },
186
- removeError: function(element) {
187
- return ClientSideValidations.formBuilders[form.ClientSideValidations.settings.type].remove(element, form.ClientSideValidations.settings);
148
+ pass: function(element, removeError, eventData) {
149
+ return removeError();
188
150
  }
189
- };
190
- ref = {
191
- 'submit.ClientSideValidations': function(eventData) {
192
- if (!$form.isValid(form.ClientSideValidations.settings.validators)) {
193
- eventData.preventDefault();
194
- eventData.stopImmediatePropagation();
151
+ },
152
+ form: {
153
+ after: function(form, eventData) {},
154
+ before: function(form, eventData) {},
155
+ fail: function(form, eventData) {},
156
+ pass: function(form, eventData) {}
157
+ }
158
+ },
159
+ enablers: {
160
+ form: function(form) {
161
+ var $form, binding, event, ref;
162
+ $form = $(form);
163
+ form.ClientSideValidations = {
164
+ settings: $form.data('clientSideValidations'),
165
+ addError: function(element, message) {
166
+ return ClientSideValidations.formBuilders[form.ClientSideValidations.settings.html_settings.type].add(element, form.ClientSideValidations.settings.html_settings, message);
167
+ },
168
+ removeError: function(element) {
169
+ return ClientSideValidations.formBuilders[form.ClientSideValidations.settings.html_settings.type].remove(element, form.ClientSideValidations.settings.html_settings);
195
170
  }
196
- },
197
- 'ajax:beforeSend.ClientSideValidations': function(eventData) {
198
- if (eventData.target === this) {
199
- $form.isValid(form.ClientSideValidations.settings.validators);
171
+ };
172
+ ref = {
173
+ 'submit.ClientSideValidations': function(eventData) {
174
+ if (!$form.isValid(form.ClientSideValidations.settings.validators)) {
175
+ eventData.preventDefault();
176
+ eventData.stopImmediatePropagation();
177
+ }
178
+ },
179
+ 'ajax:beforeSend.ClientSideValidations': function(eventData) {
180
+ if (eventData.target === this) {
181
+ $form.isValid(form.ClientSideValidations.settings.validators);
182
+ }
183
+ },
184
+ 'form:validate:after.ClientSideValidations': function(eventData) {
185
+ ClientSideValidations.callbacks.form.after($form, eventData);
186
+ },
187
+ 'form:validate:before.ClientSideValidations': function(eventData) {
188
+ ClientSideValidations.callbacks.form.before($form, eventData);
189
+ },
190
+ 'form:validate:fail.ClientSideValidations': function(eventData) {
191
+ ClientSideValidations.callbacks.form.fail($form, eventData);
192
+ },
193
+ 'form:validate:pass.ClientSideValidations': function(eventData) {
194
+ ClientSideValidations.callbacks.form.pass($form, eventData);
200
195
  }
201
- },
202
- 'form:validate:after.ClientSideValidations': function(eventData) {
203
- ClientSideValidations.callbacks.form.after($form, eventData);
204
- },
205
- 'form:validate:before.ClientSideValidations': function(eventData) {
206
- ClientSideValidations.callbacks.form.before($form, eventData);
207
- },
208
- 'form:validate:fail.ClientSideValidations': function(eventData) {
209
- ClientSideValidations.callbacks.form.fail($form, eventData);
210
- },
211
- 'form:validate:pass.ClientSideValidations': function(eventData) {
212
- ClientSideValidations.callbacks.form.pass($form, eventData);
196
+ };
197
+ for (event in ref) {
198
+ binding = ref[event];
199
+ $form.on(event, binding);
200
+ }
201
+ return $form.find(ClientSideValidations.selectors.inputs).each(function() {
202
+ return ClientSideValidations.enablers.input(this);
203
+ });
204
+ },
205
+ input: function(input) {
206
+ var $form, $input, binding, event, form, ref;
207
+ $input = $(input);
208
+ form = input.form;
209
+ $form = $(form);
210
+ ref = {
211
+ 'focusout.ClientSideValidations': function() {
212
+ $(this).isValid(form.ClientSideValidations.settings.validators);
213
+ },
214
+ 'change.ClientSideValidations': function() {
215
+ $(this).data('changed', true);
216
+ },
217
+ 'element:validate:after.ClientSideValidations': function(eventData) {
218
+ ClientSideValidations.callbacks.element.after($(this), eventData);
219
+ },
220
+ 'element:validate:before.ClientSideValidations': function(eventData) {
221
+ ClientSideValidations.callbacks.element.before($(this), eventData);
222
+ },
223
+ 'element:validate:fail.ClientSideValidations': function(eventData, message) {
224
+ var element;
225
+ element = $(this);
226
+ ClientSideValidations.callbacks.element.fail(element, message, function() {
227
+ return form.ClientSideValidations.addError(element, message);
228
+ }, eventData);
229
+ },
230
+ 'element:validate:pass.ClientSideValidations': function(eventData) {
231
+ var element;
232
+ element = $(this);
233
+ ClientSideValidations.callbacks.element.pass(element, function() {
234
+ return form.ClientSideValidations.removeError(element);
235
+ }, eventData);
236
+ }
237
+ };
238
+ for (event in ref) {
239
+ binding = ref[event];
240
+ $input.filter(':not(:radio):not([id$=_confirmation])').each(function() {
241
+ return $(this).attr('data-validate', true);
242
+ }).on(event, binding);
213
243
  }
214
- };
215
- for (event in ref) {
216
- binding = ref[event];
217
- $form.on(event, binding);
244
+ $input.filter(':checkbox').on('change.ClientSideValidations', function() {
245
+ $(this).isValid(form.ClientSideValidations.settings.validators);
246
+ });
247
+ return $input.filter('[id$=_confirmation]').each(function() {
248
+ var confirmationElement, element, ref1, results;
249
+ confirmationElement = $(this);
250
+ element = $form.find("#" + (this.id.match(/(.+)_confirmation/)[1]) + ":input");
251
+ if (element[0]) {
252
+ ref1 = {
253
+ 'focusout.ClientSideValidations': function() {
254
+ element.data('changed', true).isValid(form.ClientSideValidations.settings.validators);
255
+ },
256
+ 'keyup.ClientSideValidations': function() {
257
+ element.data('changed', true).isValid(form.ClientSideValidations.settings.validators);
258
+ }
259
+ };
260
+ results = [];
261
+ for (event in ref1) {
262
+ binding = ref1[event];
263
+ results.push($("#" + (confirmationElement.attr('id'))).on(event, binding));
264
+ }
265
+ return results;
266
+ }
267
+ });
218
268
  }
219
- return $form.find(ClientSideValidations.selectors.inputs).each(function() {
220
- return ClientSideValidations.enablers.input(this);
221
- });
222
269
  },
223
- input: function(input) {
224
- var $form, $input, binding, event, form, ref;
225
- $input = $(input);
226
- form = input.form;
227
- $form = $(form);
228
- ref = {
229
- 'focusout.ClientSideValidations': function() {
230
- $(this).isValid(form.ClientSideValidations.settings.validators);
270
+ formBuilders: {
271
+ 'ActionView::Helpers::FormBuilder': {
272
+ add: function(element, settings, message) {
273
+ var form, inputErrorField, label, labelErrorField;
274
+ form = $(element[0].form);
275
+ if (element.data('valid') !== false && (form.find("label.message[for='" + (element.attr('id')) + "']")[0] == null)) {
276
+ inputErrorField = $(settings.input_tag);
277
+ labelErrorField = $(settings.label_tag);
278
+ label = form.find("label[for='" + (element.attr('id')) + "']:not(.message)");
279
+ if (element.attr('autofocus')) {
280
+ element.attr('autofocus', false);
281
+ }
282
+ element.before(inputErrorField);
283
+ inputErrorField.find('span#input_tag').replaceWith(element);
284
+ inputErrorField.find('label.message').attr('for', element.attr('id'));
285
+ labelErrorField.find('label.message').attr('for', element.attr('id'));
286
+ labelErrorField.insertAfter(label);
287
+ labelErrorField.find('label#label_tag').replaceWith(label);
288
+ }
289
+ return form.find("label.message[for='" + (element.attr('id')) + "']").text(message);
231
290
  },
232
- 'change.ClientSideValidations': function() {
233
- $(this).data('changed', true);
291
+ remove: function(element, settings) {
292
+ var errorFieldClass, form, inputErrorField, label, labelErrorField;
293
+ form = $(element[0].form);
294
+ errorFieldClass = $(settings.input_tag).attr('class');
295
+ inputErrorField = element.closest("." + (errorFieldClass.replace(/\ /g, ".")));
296
+ label = form.find("label[for='" + (element.attr('id')) + "']:not(.message)");
297
+ labelErrorField = label.closest("." + errorFieldClass);
298
+ if (inputErrorField[0]) {
299
+ inputErrorField.find("#" + (element.attr('id'))).detach();
300
+ inputErrorField.replaceWith(element);
301
+ label.detach();
302
+ return labelErrorField.replaceWith(label);
303
+ }
304
+ }
305
+ }
306
+ },
307
+ patterns: {
308
+ numericality: function(number_format) {
309
+ return new RegExp("^(-|\\+)?(?:\\d+|\\d{1,3}(?:\\" + number_format.delimiter + "\\d{3})+)(?:\\" + number_format.separator + "\\d*)?$");
310
+ }
311
+ },
312
+ selectors: {
313
+ inputs: ':input:not(button):not([type="submit"])[name]:visible:enabled',
314
+ validate_inputs: ':input:enabled:visible[data-validate]',
315
+ forms: 'form[data-client-side-validations]'
316
+ },
317
+ validators: {
318
+ all: function() {
319
+ return $.extend({}, local, remote);
320
+ },
321
+ local: {
322
+ absence: function(element, options) {
323
+ if (!/^\s*$/.test(element.val() || '')) {
324
+ return options.message;
325
+ }
234
326
  },
235
- 'element:validate:after.ClientSideValidations': function(eventData) {
236
- ClientSideValidations.callbacks.element.after($(this), eventData);
327
+ presence: function(element, options) {
328
+ if (/^\s*$/.test(element.val() || '')) {
329
+ return options.message;
330
+ }
237
331
  },
238
- 'element:validate:before.ClientSideValidations': function(eventData) {
239
- ClientSideValidations.callbacks.element.before($(this), eventData);
332
+ acceptance: function(element, options) {
333
+ var ref;
334
+ switch (element.attr('type')) {
335
+ case 'checkbox':
336
+ if (!element.prop('checked')) {
337
+ return options.message;
338
+ }
339
+ break;
340
+ case 'text':
341
+ if (element.val() !== (((ref = options.accept) != null ? ref.toString() : void 0) || '1')) {
342
+ return options.message;
343
+ }
344
+ }
240
345
  },
241
- 'element:validate:fail.ClientSideValidations': function(eventData, message) {
242
- var element;
243
- element = $(this);
244
- ClientSideValidations.callbacks.element.fail(element, message, function() {
245
- return form.ClientSideValidations.addError(element, message);
246
- }, eventData);
346
+ format: function(element, options) {
347
+ var message;
348
+ message = this.presence(element, options);
349
+ if (message) {
350
+ if (options.allow_blank === true) {
351
+ return;
352
+ }
353
+ return message;
354
+ }
355
+ if (options["with"] && !new RegExp(options["with"].source, options["with"].options).test(element.val())) {
356
+ return options.message;
357
+ }
358
+ if (options.without && new RegExp(options.without.source, options.without.options).test(element.val())) {
359
+ return options.message;
360
+ }
247
361
  },
248
- 'element:validate:pass.ClientSideValidations': function(eventData) {
249
- var element;
250
- element = $(this);
251
- ClientSideValidations.callbacks.element.pass(element, function() {
252
- return form.ClientSideValidations.removeError(element);
253
- }, eventData);
254
- }
255
- };
256
- for (event in ref) {
257
- binding = ref[event];
258
- $input.filter(':not(:radio):not([id$=_confirmation])').each(function() {
259
- return $(this).attr('data-validate', true);
260
- }).on(event, binding);
261
- }
262
- $input.filter(':checkbox').on('change.ClientSideValidations', function() {
263
- $(this).isValid(form.ClientSideValidations.settings.validators);
264
- });
265
- return $input.filter('[id$=_confirmation]').each(function() {
266
- var confirmationElement, element, ref1, results;
267
- confirmationElement = $(this);
268
- element = $form.find("#" + (this.id.match(/(.+)_confirmation/)[1]) + ":input");
269
- if (element[0]) {
270
- ref1 = {
271
- 'focusout.ClientSideValidations': function() {
272
- element.data('changed', true).isValid(form.ClientSideValidations.settings.validators);
273
- },
274
- 'keyup.ClientSideValidations': function() {
275
- element.data('changed', true).isValid(form.ClientSideValidations.settings.validators);
362
+ numericality: function(element, options) {
363
+ var $form, CHECKS, check, checkValue, fn, number_format, operator, val;
364
+ $form = $(element[0].form);
365
+ val = $.trim(element.val());
366
+ number_format = $form[0].ClientSideValidations.settings.number_format;
367
+ if (!ClientSideValidations.patterns.numericality(number_format).test(val)) {
368
+ if (options.allow_blank === true && this.presence(element, {
369
+ message: options.messages.numericality
370
+ })) {
371
+ return;
276
372
  }
277
- };
278
- results = [];
279
- for (event in ref1) {
280
- binding = ref1[event];
281
- results.push($("#" + (confirmationElement.attr('id'))).on(event, binding));
373
+ return options.messages.numericality;
282
374
  }
283
- return results;
284
- }
285
- });
286
- }
287
- };
288
-
289
- window.ClientSideValidations.validators = {
290
- all: function() {
291
- return $.extend({}, ClientSideValidations.validators.local, ClientSideValidations.validators.remote);
292
- },
293
- local: {
294
- absence: function(element, options) {
295
- if (!/^\s*$/.test(element.val() || '')) {
296
- return options.message;
297
- }
298
- },
299
- presence: function(element, options) {
300
- if (/^\s*$/.test(element.val() || '')) {
301
- return options.message;
302
- }
303
- },
304
- acceptance: function(element, options) {
305
- var ref;
306
- switch (element.attr('type')) {
307
- case 'checkbox':
308
- if (!element.prop('checked')) {
309
- return options.message;
375
+ val = val.replace(new RegExp("\\" + number_format.delimiter, 'g'), '').replace(new RegExp("\\" + number_format.separator, 'g'), '.');
376
+ if (options.only_integer && !/^[+-]?\d+$/.test(val)) {
377
+ return options.messages.only_integer;
378
+ }
379
+ CHECKS = {
380
+ greater_than: '>',
381
+ greater_than_or_equal_to: '>=',
382
+ equal_to: '==',
383
+ less_than: '<',
384
+ less_than_or_equal_to: '<='
385
+ };
386
+ for (check in CHECKS) {
387
+ operator = CHECKS[check];
388
+ if (!(options[check] != null)) {
389
+ continue;
310
390
  }
311
- break;
312
- case 'text':
313
- if (element.val() !== (((ref = options.accept) != null ? ref.toString() : void 0) || '1')) {
314
- return options.message;
391
+ checkValue = !isNaN(parseFloat(options[check])) && isFinite(options[check]) ? options[check] : $form.find("[name*=" + options[check] + "]").length === 1 ? $form.find("[name*=" + options[check] + "]").val() : void 0;
392
+ if ((checkValue == null) || checkValue === '') {
393
+ return;
394
+ }
395
+ fn = new Function("return " + val + " " + operator + " " + checkValue);
396
+ if (!fn()) {
397
+ return options.messages[check];
315
398
  }
316
- }
317
- },
318
- format: function(element, options) {
319
- var message;
320
- message = this.presence(element, options);
321
- if (message) {
322
- if (options.allow_blank === true) {
323
- return;
324
- }
325
- return message;
326
- }
327
- if (options["with"] && !new RegExp(options["with"].source, options["with"].options).test(element.val())) {
328
- return options.message;
329
- }
330
- if (options.without && new RegExp(options.without.source, options.without.options).test(element.val())) {
331
- return options.message;
332
- }
333
- },
334
- numericality: function(element, options) {
335
- var CHECKS, check, checkValue, fn, form, operator, val;
336
- val = $.trim(element.val());
337
- if (!ClientSideValidations.patterns.numericality.test(val)) {
338
- if (options.allow_blank === true && this.presence(element, {
339
- message: options.messages.numericality
340
- })) {
341
- return;
342
399
  }
343
- return options.messages.numericality;
344
- }
345
- val = val.replace(new RegExp("\\" + ClientSideValidations.number_format.delimiter, 'g'), '').replace(new RegExp("\\" + ClientSideValidations.number_format.separator, 'g'), '.');
346
- if (options.only_integer && !/^[+-]?\d+$/.test(val)) {
347
- return options.messages.only_integer;
348
- }
349
- CHECKS = {
350
- greater_than: '>',
351
- greater_than_or_equal_to: '>=',
352
- equal_to: '==',
353
- less_than: '<',
354
- less_than_or_equal_to: '<='
355
- };
356
- form = $(element[0].form);
357
- for (check in CHECKS) {
358
- operator = CHECKS[check];
359
- if (!(options[check] != null)) {
360
- continue;
400
+ if (options.odd && !(parseInt(val, 10) % 2)) {
401
+ return options.messages.odd;
361
402
  }
362
- checkValue = !isNaN(parseFloat(options[check])) && isFinite(options[check]) ? options[check] : form.find("[name*=" + options[check] + "]").length === 1 ? form.find("[name*=" + options[check] + "]").val() : void 0;
363
- if ((checkValue == null) || checkValue === '') {
364
- return;
403
+ if (options.even && (parseInt(val, 10) % 2)) {
404
+ return options.messages.even;
365
405
  }
366
- fn = new Function("return " + val + " " + operator + " " + checkValue);
367
- if (!fn()) {
368
- return options.messages[check];
406
+ },
407
+ length: function(element, options) {
408
+ var CHECKS, blankOptions, check, fn, message, operator, tokenized_length, tokenizer;
409
+ tokenizer = options.js_tokenizer || "split('')";
410
+ tokenized_length = new Function('element', "return (element.val()." + tokenizer + " || '').length")(element);
411
+ CHECKS = {
412
+ is: '==',
413
+ minimum: '>=',
414
+ maximum: '<='
415
+ };
416
+ blankOptions = {};
417
+ blankOptions.message = options.is ? options.messages.is : options.minimum ? options.messages.minimum : void 0;
418
+ message = this.presence(element, blankOptions);
419
+ if (message) {
420
+ if (options.allow_blank === true) {
421
+ return;
422
+ }
423
+ return message;
369
424
  }
370
- }
371
- if (options.odd && !(parseInt(val, 10) % 2)) {
372
- return options.messages.odd;
373
- }
374
- if (options.even && (parseInt(val, 10) % 2)) {
375
- return options.messages.even;
376
- }
377
- },
378
- length: function(element, options) {
379
- var CHECKS, blankOptions, check, fn, message, operator, tokenized_length, tokenizer;
380
- tokenizer = options.js_tokenizer || "split('')";
381
- tokenized_length = new Function('element', "return (element.val()." + tokenizer + " || '').length")(element);
382
- CHECKS = {
383
- is: '==',
384
- minimum: '>=',
385
- maximum: '<='
386
- };
387
- blankOptions = {};
388
- blankOptions.message = options.is ? options.messages.is : options.minimum ? options.messages.minimum : void 0;
389
- message = this.presence(element, blankOptions);
390
- if (message) {
391
- if (options.allow_blank === true) {
392
- return;
425
+ for (check in CHECKS) {
426
+ operator = CHECKS[check];
427
+ if (!options[check]) {
428
+ continue;
429
+ }
430
+ fn = new Function("return " + tokenized_length + " " + operator + " " + options[check]);
431
+ if (!fn()) {
432
+ return options.messages[check];
433
+ }
393
434
  }
394
- return message;
395
- }
396
- for (check in CHECKS) {
397
- operator = CHECKS[check];
398
- if (!options[check]) {
399
- continue;
435
+ },
436
+ exclusion: function(element, options) {
437
+ var lower, message, option, ref, upper;
438
+ message = this.presence(element, options);
439
+ if (message) {
440
+ if (options.allow_blank === true) {
441
+ return;
442
+ }
443
+ return message;
400
444
  }
401
- fn = new Function("return " + tokenized_length + " " + operator + " " + options[check]);
402
- if (!fn()) {
403
- return options.messages[check];
445
+ if (options["in"]) {
446
+ if (ref = element.val(), indexOf.call((function() {
447
+ var i, len, ref1, results;
448
+ ref1 = options["in"];
449
+ results = [];
450
+ for (i = 0, len = ref1.length; i < len; i++) {
451
+ option = ref1[i];
452
+ results.push(option.toString());
453
+ }
454
+ return results;
455
+ })(), ref) >= 0) {
456
+ return options.message;
457
+ }
404
458
  }
405
- }
406
- },
407
- exclusion: function(element, options) {
408
- var lower, message, option, ref, upper;
409
- message = this.presence(element, options);
410
- if (message) {
411
- if (options.allow_blank === true) {
412
- return;
459
+ if (options.range) {
460
+ lower = options.range[0];
461
+ upper = options.range[1];
462
+ if (element.val() >= lower && element.val() <= upper) {
463
+ return options.message;
464
+ }
413
465
  }
414
- return message;
415
- }
416
- if (options["in"]) {
417
- if (ref = element.val(), indexOf.call((function() {
418
- var i, len, ref1, results;
419
- ref1 = options["in"];
420
- results = [];
421
- for (i = 0, len = ref1.length; i < len; i++) {
422
- option = ref1[i];
423
- results.push(option.toString());
466
+ },
467
+ inclusion: function(element, options) {
468
+ var lower, message, option, ref, upper;
469
+ message = this.presence(element, options);
470
+ if (message) {
471
+ if (options.allow_blank === true) {
472
+ return;
424
473
  }
425
- return results;
426
- })(), ref) >= 0) {
427
- return options.message;
474
+ return message;
428
475
  }
429
- }
430
- if (options.range) {
431
- lower = options.range[0];
432
- upper = options.range[1];
433
- if (element.val() >= lower && element.val() <= upper) {
476
+ if (options["in"]) {
477
+ if (ref = element.val(), indexOf.call((function() {
478
+ var i, len, ref1, results;
479
+ ref1 = options["in"];
480
+ results = [];
481
+ for (i = 0, len = ref1.length; i < len; i++) {
482
+ option = ref1[i];
483
+ results.push(option.toString());
484
+ }
485
+ return results;
486
+ })(), ref) >= 0) {
487
+ return;
488
+ }
434
489
  return options.message;
435
490
  }
436
- }
437
- },
438
- inclusion: function(element, options) {
439
- var lower, message, option, ref, upper;
440
- message = this.presence(element, options);
441
- if (message) {
442
- if (options.allow_blank === true) {
443
- return;
444
- }
445
- return message;
446
- }
447
- if (options["in"]) {
448
- if (ref = element.val(), indexOf.call((function() {
449
- var i, len, ref1, results;
450
- ref1 = options["in"];
451
- results = [];
452
- for (i = 0, len = ref1.length; i < len; i++) {
453
- option = ref1[i];
454
- results.push(option.toString());
491
+ if (options.range) {
492
+ lower = options.range[0];
493
+ upper = options.range[1];
494
+ if (element.val() >= lower && element.val() <= upper) {
495
+ return;
455
496
  }
456
- return results;
457
- })(), ref) >= 0) {
458
- return;
497
+ return options.message;
459
498
  }
460
- return options.message;
461
- }
462
- if (options.range) {
463
- lower = options.range[0];
464
- upper = options.range[1];
465
- if (element.val() >= lower && element.val() <= upper) {
466
- return;
499
+ },
500
+ confirmation: function(element, options) {
501
+ var regex;
502
+ regex = new RegExp("^" + (element.val()) + "$", options.case_sensitive ? '' : 'i');
503
+ if (!regex.test($("#" + (element.attr('id')) + "_confirmation").val())) {
504
+ return options.message;
467
505
  }
468
- return options.message;
469
- }
470
- },
471
- confirmation: function(element, options) {
472
- var regex;
473
- regex = new RegExp("^" + (element.val()) + "$", options.case_sensitive ? '' : 'i');
474
- if (!regex.test($("#" + (element.attr('id')) + "_confirmation").val())) {
475
- return options.message;
476
- }
477
- },
478
- uniqueness: function(element, options) {
479
- var form, matches, name, name_prefix, name_suffix, valid, value;
480
- name = element.attr('name');
481
- if (/_attributes\]\[\d/.test(name)) {
482
- matches = name.match(/^(.+_attributes\])\[\d+\](.+)$/);
483
- name_prefix = matches[1];
484
- name_suffix = matches[2];
485
- value = element.val();
486
- if (name_prefix && name_suffix) {
487
- form = element.closest('form');
488
- valid = true;
489
- form.find(":input[name^=\"" + name_prefix + "\"][name$=\"" + name_suffix + "\"]").each(function() {
490
- if ($(this).attr('name') !== name) {
491
- if ($(this).val() === value) {
492
- valid = false;
493
- return $(this).data('notLocallyUnique', true);
494
- } else {
495
- if ($(this).data('notLocallyUnique')) {
496
- return $(this).removeData('notLocallyUnique').data('changed', true);
506
+ },
507
+ uniqueness: function(element, options) {
508
+ var form, matches, name, name_prefix, name_suffix, valid, value;
509
+ name = element.attr('name');
510
+ if (/_attributes\]\[\d/.test(name)) {
511
+ matches = name.match(/^(.+_attributes\])\[\d+\](.+)$/);
512
+ name_prefix = matches[1];
513
+ name_suffix = matches[2];
514
+ value = element.val();
515
+ if (name_prefix && name_suffix) {
516
+ form = element.closest('form');
517
+ valid = true;
518
+ form.find(":input[name^=\"" + name_prefix + "\"][name$=\"" + name_suffix + "\"]").each(function() {
519
+ if ($(this).attr('name') !== name) {
520
+ if ($(this).val() === value) {
521
+ valid = false;
522
+ return $(this).data('notLocallyUnique', true);
523
+ } else {
524
+ if ($(this).data('notLocallyUnique')) {
525
+ return $(this).removeData('notLocallyUnique').data('changed', true);
526
+ }
497
527
  }
498
528
  }
529
+ });
530
+ if (!valid) {
531
+ return options.message;
499
532
  }
500
- });
501
- if (!valid) {
502
- return options.message;
503
533
  }
504
534
  }
505
535
  }
506
- }
536
+ },
537
+ remote: {}
507
538
  },
508
- remote: {}
509
- };
510
-
511
- window.ClientSideValidations.remote_validators_url_for = function(validator) {
512
- if (ClientSideValidations.remote_validators_prefix != null) {
513
- return "//" + window.location.host + "/" + ClientSideValidations.remote_validators_prefix + "/validators/" + validator;
514
- } else {
515
- return "//" + window.location.host + "/validators/" + validator;
516
- }
517
- };
518
-
519
- window.ClientSideValidations.disableValidators = function() {
520
- var func, ref, results, validator;
521
- if (window.ClientSideValidations.disabled_validators === void 0) {
522
- return;
523
- }
524
- ref = window.ClientSideValidations.validators.remote;
525
- results = [];
526
- for (validator in ref) {
527
- func = ref[validator];
528
- if (indexOf.call(window.ClientSideValidations.disabled_validators, validator) >= 0) {
529
- results.push(delete window.ClientSideValidations.validators.remote[validator]);
539
+ disable: function(target) {
540
+ var $target;
541
+ $target = $(target);
542
+ $target.off('.ClientSideValidations');
543
+ if ($target.is('form')) {
544
+ return ClientSideValidations.disable($target.find(':input'));
530
545
  } else {
531
- results.push(void 0);
532
- }
533
- }
534
- return results;
535
- };
536
-
537
- window.ClientSideValidations.formBuilders = {
538
- 'ActionView::Helpers::FormBuilder': {
539
- add: function(element, settings, message) {
540
- var form, inputErrorField, label, labelErrorField;
541
- form = $(element[0].form);
542
- if (element.data('valid') !== false && (form.find("label.message[for='" + (element.attr('id')) + "']")[0] == null)) {
543
- inputErrorField = $(settings.input_tag);
544
- labelErrorField = $(settings.label_tag);
545
- label = form.find("label[for='" + (element.attr('id')) + "']:not(.message)");
546
- if (element.attr('autofocus')) {
547
- element.attr('autofocus', false);
548
- }
549
- element.before(inputErrorField);
550
- inputErrorField.find('span#input_tag').replaceWith(element);
551
- inputErrorField.find('label.message').attr('for', element.attr('id'));
552
- labelErrorField.find('label.message').attr('for', element.attr('id'));
553
- labelErrorField.insertAfter(label);
554
- labelErrorField.find('label#label_tag').replaceWith(label);
555
- }
556
- return form.find("label.message[for='" + (element.attr('id')) + "']").text(message);
557
- },
558
- remove: function(element, settings) {
559
- var errorFieldClass, form, inputErrorField, label, labelErrorField;
560
- form = $(element[0].form);
561
- errorFieldClass = $(settings.input_tag).attr('class');
562
- inputErrorField = element.closest("." + (errorFieldClass.replace(/\ /g, ".")));
563
- label = form.find("label[for='" + (element.attr('id')) + "']:not(.message)");
564
- labelErrorField = label.closest("." + errorFieldClass);
565
- if (inputErrorField[0]) {
566
- inputErrorField.find("#" + (element.attr('id'))).detach();
567
- inputErrorField.replaceWith(element);
568
- label.detach();
569
- return labelErrorField.replaceWith(label);
570
- }
571
- }
572
- }
573
- };
574
-
575
- window.ClientSideValidations.patterns = {
576
- numericality: /^(-|\+)?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d*)?$/
577
- };
578
-
579
- window.ClientSideValidations.callbacks = {
580
- element: {
581
- after: function(element, eventData) {},
582
- before: function(element, eventData) {},
583
- fail: function(element, message, addError, eventData) {
584
- return addError();
585
- },
586
- pass: function(element, removeError, eventData) {
587
- return removeError();
546
+ $target.removeData('valid');
547
+ $target.removeData('changed');
548
+ return $target.filter(':input').each(function() {
549
+ return $(this).removeAttr('data-validate');
550
+ });
588
551
  }
589
552
  },
590
- form: {
591
- after: function(form, eventData) {},
592
- before: function(form, eventData) {},
593
- fail: function(form, eventData) {},
594
- pass: function(form, eventData) {}
553
+ reset: function(form) {
554
+ var $form, key;
555
+ $form = $(form);
556
+ ClientSideValidations.disable(form);
557
+ for (key in form.ClientSideValidations.settings.validators) {
558
+ form.ClientSideValidations.removeError($form.find("[name='" + key + "']"));
559
+ }
560
+ return ClientSideValidations.enablers.form(form);
595
561
  }
596
562
  };
597
563
 
598
- window.ClientSideValidations.event = (window.Turbolinks != null) && window.Turbolinks.supported ? window.Turbolinks.EVENTS != null ? 'page:change' : 'turbolinks:load' : 'ready';
599
-
600
- $(document).on(window.ClientSideValidations.event, function() {
601
- ClientSideValidations.disableValidators();
564
+ $(document).on(initializeOnEvent, function() {
602
565
  return $(ClientSideValidations.selectors.forms).validate();
603
566
  });
604
567
 
568
+ window.ClientSideValidations = ClientSideValidations;
569
+
605
570
  }).call(this);
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: client_side_validations
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.0.2
4
+ version: 9.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Geremia Taglialatela
@@ -270,7 +270,6 @@ files:
270
270
  - lib/client_side_validations/action_view.rb
271
271
  - lib/client_side_validations/action_view/form_builder.rb
272
272
  - lib/client_side_validations/action_view/form_helper.rb
273
- - lib/client_side_validations/action_view/form_tag_helper.rb
274
273
  - lib/client_side_validations/active_model.rb
275
274
  - lib/client_side_validations/active_model/absence.rb
276
275
  - lib/client_side_validations/active_model/acceptance.rb
@@ -1,16 +0,0 @@
1
- module ClientSideValidations
2
- module ActionView
3
- module Helpers
4
- module FormTagHelper
5
- private
6
-
7
- def html_options_for_form(url_for_options, options)
8
- options.stringify_keys!
9
- html_options = {}
10
- html_options['data-validate'] = options.delete('validate') if options['validate']
11
- html_options.merge!(super(url_for_options, options))
12
- end
13
- end
14
- end
15
- end
16
- end