af-client_side_validations 3.1.4.af1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. data/client_side_validations.gemspec +34 -0
  2. data/lib/client_side_validations/action_view/form_builder.rb +160 -0
  3. data/lib/client_side_validations/action_view/form_helper.rb +92 -0
  4. data/lib/client_side_validations/action_view/form_tag_helper.rb +12 -0
  5. data/lib/client_side_validations/action_view.rb +14 -0
  6. data/lib/client_side_validations/active_model/acceptance.rb +10 -0
  7. data/lib/client_side_validations/active_model/exclusion.rb +15 -0
  8. data/lib/client_side_validations/active_model/format.rb +10 -0
  9. data/lib/client_side_validations/active_model/inclusion.rb +15 -0
  10. data/lib/client_side_validations/active_model/length.rb +24 -0
  11. data/lib/client_side_validations/active_model/numericality.rb +31 -0
  12. data/lib/client_side_validations/active_model/presence.rb +10 -0
  13. data/lib/client_side_validations/active_model.rb +60 -0
  14. data/lib/client_side_validations/active_record/middleware.rb +33 -0
  15. data/lib/client_side_validations/active_record/uniqueness.rb +28 -0
  16. data/lib/client_side_validations/active_record.rb +11 -0
  17. data/lib/client_side_validations/core_ext/range.rb +10 -0
  18. data/lib/client_side_validations/core_ext/regexp.rb +14 -0
  19. data/lib/client_side_validations/core_ext.rb +3 -0
  20. data/lib/client_side_validations/engine.rb +6 -0
  21. data/lib/client_side_validations/files.rb +8 -0
  22. data/lib/client_side_validations/formtastic.rb +21 -0
  23. data/lib/client_side_validations/middleware.rb +81 -0
  24. data/lib/client_side_validations/mongo_mapper/middleware.rb +20 -0
  25. data/lib/client_side_validations/mongo_mapper/uniqueness.rb +28 -0
  26. data/lib/client_side_validations/mongo_mapper.rb +9 -0
  27. data/lib/client_side_validations/mongoid/middleware.rb +20 -0
  28. data/lib/client_side_validations/mongoid/uniqueness.rb +28 -0
  29. data/lib/client_side_validations/mongoid.rb +9 -0
  30. data/lib/client_side_validations/simple_form.rb +24 -0
  31. data/lib/client_side_validations/version.rb +3 -0
  32. data/lib/client_side_validations.rb +13 -0
  33. data/lib/generators/client_side_validations/copy_asset_generator.rb +36 -0
  34. data/lib/generators/client_side_validations/install_generator.rb +45 -0
  35. data/lib/generators/templates/client_side_validations/README.rails.3.0 +6 -0
  36. data/lib/generators/templates/client_side_validations/README.rails.3.1 +7 -0
  37. data/lib/generators/templates/client_side_validations/initializer.rb +14 -0
  38. data/test/action_view/cases/helper.rb +176 -0
  39. data/test/action_view/cases/test_helpers.rb +666 -0
  40. data/test/action_view/cases/test_legacy_helpers.rb +217 -0
  41. data/test/action_view/models/comment.rb +35 -0
  42. data/test/action_view/models/post.rb +35 -0
  43. data/test/action_view/models.rb +3 -0
  44. data/test/active_model/cases/helper.rb +4 -0
  45. data/test/active_model/cases/test_acceptance_validator.rb +16 -0
  46. data/test/active_model/cases/test_base.rb +11 -0
  47. data/test/active_model/cases/test_confirmation_validator.rb +16 -0
  48. data/test/active_model/cases/test_exclusion_validator.rb +20 -0
  49. data/test/active_model/cases/test_format_validator.rb +21 -0
  50. data/test/active_model/cases/test_inclusion_validator.rb +21 -0
  51. data/test/active_model/cases/test_length_validator.rb +61 -0
  52. data/test/active_model/cases/test_numericality_validator.rb +46 -0
  53. data/test/active_model/cases/test_presence_validator.rb +16 -0
  54. data/test/active_model/cases/test_validations.rb +175 -0
  55. data/test/active_model/models/person.rb +17 -0
  56. data/test/active_record/cases/helper.rb +12 -0
  57. data/test/active_record/cases/test_base.rb +11 -0
  58. data/test/active_record/cases/test_middleware.rb +175 -0
  59. data/test/active_record/cases/test_uniqueness_validator.rb +50 -0
  60. data/test/active_record/models/guid.rb +7 -0
  61. data/test/active_record/models/user.rb +14 -0
  62. data/test/base_helper.rb +7 -0
  63. data/test/core_ext/cases/test_core_ext.rb +46 -0
  64. data/test/formtastic/cases/helper.rb +7 -0
  65. data/test/formtastic/cases/test_form_builder.rb +11 -0
  66. data/test/formtastic/cases/test_form_helper.rb +21 -0
  67. data/test/generators/cases/test_generators.rb +70 -0
  68. data/test/javascript/config.ru +3 -0
  69. data/test/javascript/public/test/callbacks/elementAfter.js +54 -0
  70. data/test/javascript/public/test/callbacks/elementBefore.js +54 -0
  71. data/test/javascript/public/test/callbacks/elementFail.js +70 -0
  72. data/test/javascript/public/test/callbacks/elementPass.js +70 -0
  73. data/test/javascript/public/test/callbacks/formAfter.js +45 -0
  74. data/test/javascript/public/test/callbacks/formBefore.js +45 -0
  75. data/test/javascript/public/test/callbacks/formFail.js +51 -0
  76. data/test/javascript/public/test/callbacks/formPass.js +50 -0
  77. data/test/javascript/public/test/form_builders/validateForm.js +66 -0
  78. data/test/javascript/public/test/form_builders/validateFormtastic.js +54 -0
  79. data/test/javascript/public/test/form_builders/validateNestedForm.js +66 -0
  80. data/test/javascript/public/test/form_builders/validateSimpleForm.js +57 -0
  81. data/test/javascript/public/test/settings.js +15 -0
  82. data/test/javascript/public/test/validateElement.js +209 -0
  83. data/test/javascript/public/test/validators/acceptance.js +42 -0
  84. data/test/javascript/public/test/validators/confirmation.js +25 -0
  85. data/test/javascript/public/test/validators/exclusion.js +41 -0
  86. data/test/javascript/public/test/validators/format.js +27 -0
  87. data/test/javascript/public/test/validators/inclusion.js +42 -0
  88. data/test/javascript/public/test/validators/length.js +76 -0
  89. data/test/javascript/public/test/validators/numericality.js +158 -0
  90. data/test/javascript/public/test/validators/presence.js +21 -0
  91. data/test/javascript/public/test/validators/uniqueness.js +107 -0
  92. data/test/javascript/public/vendor/jquery.metadata.js +122 -0
  93. data/test/javascript/public/vendor/qunit.css +196 -0
  94. data/test/javascript/public/vendor/qunit.js +1374 -0
  95. data/test/javascript/server.rb +84 -0
  96. data/test/javascript/views/index.erb +20 -0
  97. data/test/javascript/views/layout.erb +21 -0
  98. data/test/middleware/cases/helper.rb +18 -0
  99. data/test/middleware/cases/test_middleware.rb +8 -0
  100. data/test/mongo_mapper/cases/helper.rb +9 -0
  101. data/test/mongo_mapper/cases/test_base.rb +15 -0
  102. data/test/mongo_mapper/cases/test_middleware.rb +77 -0
  103. data/test/mongo_mapper/cases/test_uniqueness_validator.rb +50 -0
  104. data/test/mongo_mapper/models/magazine.rb +11 -0
  105. data/test/mongoid/cases/helper.rb +16 -0
  106. data/test/mongoid/cases/test_base.rb +15 -0
  107. data/test/mongoid/cases/test_middleware.rb +77 -0
  108. data/test/mongoid/cases/test_uniqueness_validator.rb +49 -0
  109. data/test/mongoid/models/book.rb +12 -0
  110. data/test/simple_form/cases/helper.rb +5 -0
  111. data/test/simple_form/cases/test_form_builder.rb +14 -0
  112. data/test/simple_form/cases/test_form_helper.rb +24 -0
  113. data/test/test_loader.rb +6 -0
  114. data/vendor/assets/javascripts/rails.validations.js +418 -0
  115. metadata +436 -0
@@ -0,0 +1,418 @@
1
+ /*!
2
+ * Rails 3 Client Side Validations - v3.1.4
3
+ * https://github.com/bcardarella/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
+ */
9
+
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(eventData) { if(eventData.target == this) 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); })
25
+
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');
50
+
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
+ });
61
+
62
+ var addError = function(element, message) {
63
+ clientSideValidations.formBuilders[settings.type].add(element, settings, message);
64
+ }
65
+
66
+ var removeError = function(element) {
67
+ clientSideValidations.formBuilders[settings.type].remove(element, settings);
68
+ }
69
+ });
70
+ }
71
+
72
+ $.fn.isValid = function(validators) {
73
+ if ($(this[0]).is('form')) {
74
+ return validateForm($(this[0]), validators);
75
+ } else {
76
+ return validateElement($(this[0]), validatorsFor(this[0].name, validators));
77
+ }
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; }
85
+ });
86
+
87
+ if (valid) {
88
+ form.trigger('form:validate:pass');
89
+ } else {
90
+ form.trigger('form:validate:fail');
91
+ }
92
+
93
+ form.trigger('form:validate:after');
94
+ 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;
111
+ }
112
+ }
113
+
114
+ if (valid) { element.data('valid', null); element.trigger('element:validate:pass'); }
115
+ }
116
+
117
+ element.trigger('element:validate:after');
118
+ return element.data('valid') === false ? false : true;
119
+ };
120
+
121
+ var validatorsFor = function(name, validators) {
122
+ name = name.replace(/_attributes\]\[\d+\]/g,"_attributes][]")
123
+ return validators[name]
124
+ };
125
+
126
+ // Main hook
127
+ // If new forms are dynamically introduced into the DOM the .validate() method
128
+ // must be invoked on that form
129
+ $(function() { $('form[data-validate]').validate(); })
130
+ })(jQuery);
131
+
132
+ var clientSideValidations = {
133
+ validators: {
134
+ all: function() { return jQuery.extend({}, clientSideValidations.validators.local, clientSideValidations.validators.remote) },
135
+ local: {
136
+ presence: function(element, options) {
137
+ if (/^\s*$/.test(element.val() || "")) {
138
+ return options.message;
139
+ }
140
+ },
141
+ acceptance: function(element, options) {
142
+ switch (element.attr('type')) {
143
+ case 'checkbox':
144
+ if (!element.attr('checked')) {
145
+ return options.message;
146
+ }
147
+ break;
148
+ case 'text':
149
+ if (element.val() != (options.accept || '1')) {
150
+ return options.message;
151
+ }
152
+ break;
153
+ }
154
+ },
155
+ format: function(element, options) {
156
+ if ((message = this.presence(element, options)) && options.allow_blank == true) {
157
+ return;
158
+ } else if (message) {
159
+ return message;
160
+ } else {
161
+ if (options['with'] && !options['with'].test(element.val())) {
162
+ return options.message;
163
+ } else if (options['without'] && options['without'].test(element.val())) {
164
+ return options.message;
165
+ }
166
+ }
167
+ },
168
+ numericality: function(element, options) {
169
+ if (!/^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d*)?$/.test(element.val()) && element.val() != '') {
170
+ return options.messages.numericality;
171
+ }
172
+
173
+ if (options.only_integer && !/^[+-]?\d+$/.test(element.val())) {
174
+ return options.messages.only_integer;
175
+ }
176
+
177
+ var CHECKS = { greater_than: '>', greater_than_or_equal_to: '>=',
178
+ equal_to: '==', less_than: '<', less_than_or_equal_to: '<=' }
179
+
180
+ for (var check in CHECKS) {
181
+ if (options[check] != undefined && !(new Function("return " + element.val() + CHECKS[check] + options[check])())) {
182
+ return options.messages[check];
183
+ }
184
+ }
185
+
186
+ if (options.odd && !(parseInt(element.val()) % 2)) {
187
+ return options.messages.odd;
188
+ }
189
+
190
+ if (options.even && (parseInt(element.val()) % 2)) {
191
+ return options.messages.even;
192
+ }
193
+ },
194
+ length: function(element, options) {
195
+ var blankOptions = {};
196
+ if (options.is) {
197
+ blankOptions.message = options.messages.is;
198
+ } else if (options.minimum) {
199
+ blankOptions.message = options.messages.minimum;
200
+ }
201
+ if ((message = this.presence(element, blankOptions)) && options.allow_blank == true) {
202
+ return;
203
+ } else if (message) {
204
+ return message;
205
+ } else {
206
+ var CHECKS = { is: '==', minimum: '>=', maximum: '<=' }
207
+ var tokenizer = options.js_tokenizer || "split('')";
208
+ var tokenized_length = new Function("element", "return (element.val()." + tokenizer + " || '').length;")(element);
209
+
210
+ for (var check in CHECKS) {
211
+ if (options[check] && !(new Function("return " + tokenized_length + CHECKS[check] + options[check])())) {
212
+ return options.messages[check];
213
+ }
214
+ }
215
+ }
216
+ },
217
+ exclusion: function(element, options) {
218
+ if ((message = this.presence(element, options)) && options.allow_blank == true) {
219
+ return;
220
+ } else if (message) {
221
+ return message;
222
+ } else {
223
+ if (options['in']) {
224
+ for (var i = 0; i < options['in'].length; i++) {
225
+ if (options['in'][i] == element.val()) {
226
+ return options.message;
227
+ }
228
+ }
229
+ } else if (options['range']) {
230
+ var lower = options['range'][0],
231
+ upper = options['range'][1];
232
+ if (element.val() >= lower && element.val() <= upper) {
233
+ return options.message;
234
+ }
235
+ }
236
+ }
237
+ },
238
+ inclusion: function(element, options) {
239
+ if ((message = this.presence(element, options)) && options.allow_blank == true) {
240
+ return;
241
+ } else if (message) {
242
+ return message;
243
+ } else {
244
+ if (options['in']) {
245
+ for (i = 0; i < options['in'].length; i++) {
246
+ if (options['in'][i] == element.val()
247
+ || (options['in'][i] === true && ('true' === element.val()))
248
+ || (options['in'][i] === false && ('false' === element.val()))
249
+ ) {
250
+ return;
251
+ }
252
+ }
253
+ return options.message;
254
+ } else if (options['range']) {
255
+ var lower = options['range'][0],
256
+ upper = options['range'][1];
257
+
258
+ if (element.val() >= lower && element.val() <= upper) {
259
+ return;
260
+ } else {
261
+ return options.message;
262
+ }
263
+ }
264
+ }
265
+ },
266
+ confirmation: function(element, options) {
267
+ if (element.val() != jQuery('#' + element.attr('id') + '_confirmation').val()) {
268
+ return options.message;
269
+ }
270
+ }
271
+ },
272
+ remote: {
273
+ uniqueness: function(element, options) {
274
+ if ((message = clientSideValidations.validators.local.presence(element, options)) && options.allow_blank === true) {
275
+ return;
276
+ } else if (message) {
277
+ return message;
278
+ } else {
279
+ var data = {};
280
+ data['case_sensitive'] = !!options.case_sensitive;
281
+ if (options.id) {
282
+ data['id'] = options.id;
283
+ }
284
+
285
+ if (options.scope) {
286
+ data.scope = {}
287
+ for (key in options.scope) {
288
+ var scoped_element = jQuery('[name="' + element.attr('name').replace(/\[\w+]$/, '[' + key + ']' + '"]'));
289
+ if (scoped_element[0] && scoped_element.val() != options.scope[key]) {
290
+ data.scope[key] = scoped_element.val();
291
+ scoped_element.unbind('change.' + element.id).bind('change.' + element.id, function() { element.trigger('change'); element.trigger('focusout'); });
292
+ } else {
293
+ data.scope[key] = options.scope[key];
294
+ }
295
+ }
296
+ }
297
+
298
+ // Kind of a hack but this will isolate the resource name and attribute.
299
+ // e.g. user[records_attributes][0][title] => records[title]
300
+ // e.g. user[record_attributes][title] => record[title]
301
+ // Server side handles classifying the resource properly
302
+ if (/_attributes]/.test(element.attr('name'))) {
303
+ var name = element.attr('name').match(/\[\w+_attributes]/g).pop().match(/\[(\w+)_attributes]/).pop();
304
+ name += /(\[\w+])$/.exec(element.attr('name'))[1];
305
+ } else {
306
+ var name = element.attr('name');
307
+ }
308
+
309
+ // Override the name if a nested module class is passed
310
+ if (options['class']) {
311
+ name = options['class'] + '[' + name.split('[')[1]
312
+ }
313
+ data[name] = element.val();
314
+
315
+ if (jQuery.ajax({
316
+ url: '/validators/uniqueness',
317
+ data: data,
318
+ async: false
319
+ }).status == 200) {
320
+ return options.message;
321
+ }
322
+ }
323
+ }
324
+ }
325
+ },
326
+ formBuilders: {
327
+ 'ActionView::Helpers::FormBuilder': {
328
+ add: function(element, settings, message) {
329
+ if (element.data('valid') !== false && jQuery('label.message[for="' + element.attr('id') + '"]')[0] == undefined) {
330
+ var inputErrorField = jQuery(settings.input_tag),
331
+ labelErrorField = jQuery(settings.label_tag),
332
+ label = jQuery('label[for="' + element.attr('id') + '"]:not(.message)');
333
+
334
+ if (element.attr('autofocus')) { element.attr('autofocus', false) };
335
+ element.before(inputErrorField);
336
+ inputErrorField.find('span#input_tag').replaceWith(element);
337
+ inputErrorField.find('label.message').attr('for', element.attr('id'));
338
+ labelErrorField.find('label.message').attr('for', element.attr('id'));
339
+ label.replaceWith(labelErrorField);
340
+ labelErrorField.find('label#label_tag').replaceWith(label);
341
+ }
342
+ jQuery('label.message[for="' + element.attr('id') + '"]').text(message);
343
+ },
344
+ remove: function(element, settings) {
345
+ var errorFieldClass = jQuery(settings.input_tag).attr('class'),
346
+ inputErrorField = element.closest('.' + errorFieldClass),
347
+ label = jQuery('label[for="' + element.attr('id') + '"]:not(.message)'),
348
+ labelErrorField = label.closest('.' + errorFieldClass);
349
+
350
+ if (inputErrorField[0]) {
351
+ inputErrorField.find('#' + element.attr('id')).detach();
352
+ inputErrorField.replaceWith(element);
353
+ label.detach();
354
+ labelErrorField.replaceWith(label);
355
+ }
356
+ }
357
+ },
358
+ 'SimpleForm::FormBuilder': {
359
+ add: function(element, settings, message) {
360
+ if (element.data('valid') !== false) {
361
+ var wrapper = element.closest(settings.wrapper_tag);
362
+ wrapper.addClass(settings.wrapper_error_class);
363
+ var errorElement = $('<' + settings.error_tag + ' class="' + settings.error_class + '">' + message + '</' + settings.error_tag + '>');
364
+ wrapper.append(errorElement);
365
+ } else {
366
+ element.parent().find(settings.error_tag + '.' + settings.error_class).text(message);
367
+ }
368
+ },
369
+ remove: function(element, settings) {
370
+ var wrapper = element.closest(settings.wrapper_tag + '.' + settings.wrapper_error_class);
371
+ wrapper.removeClass(settings.wrapper_error_class);
372
+ var errorElement = wrapper.find(settings.error_tag + '.' + settings.error_class);
373
+ errorElement.remove();
374
+ }
375
+
376
+ },
377
+ 'Formtastic::FormBuilder': {
378
+ add: function(element, settings, message) {
379
+ if (element.data('valid') !== false) {
380
+ var wrapper = element.closest('li');
381
+ wrapper.addClass('error');
382
+ var errorElement = $('<p class="' + settings.inline_error_class + '">' + message + '</p>');
383
+ wrapper.append(errorElement);
384
+ } else {
385
+ element.parent().find('p.' + settings.inline_error_class).text(message);
386
+ }
387
+ },
388
+ remove: function(element, settings) {
389
+ var wrapper = element.closest('li.error');
390
+ wrapper.removeClass('error');
391
+ var errorElement = wrapper.find('p.' + settings.inline_error_class);
392
+ errorElement.remove();
393
+ }
394
+ },
395
+ 'NestedForm::Builder': {
396
+ add: function(element, settings, message) {
397
+ clientSideValidations.formBuilders['ActionView::Helpers::FormBuilder'].add(element, settings, message);
398
+ },
399
+ remove: function(element, settings, message) {
400
+ clientSideValidations.formBuilders['ActionView::Helpers::FormBuilder'].remove(element, settings, message);
401
+ }
402
+ }
403
+ },
404
+ callbacks: {
405
+ element: {
406
+ after: function(element, eventData) { },
407
+ before: function(element, eventData) { },
408
+ fail: function(element, message, addError, eventData) { addError() },
409
+ pass: function(element, removeError, eventData) { removeError() }
410
+ },
411
+ form: {
412
+ after: function(form, eventData) { },
413
+ before: function(form, eventData) { },
414
+ fail: function(form, eventData) { },
415
+ pass: function(form, eventData) { }
416
+ }
417
+ }
418
+ };