client_side_validations 3.1.5 → 3.2.0.beta.1

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 (105) hide show
  1. data/client_side_validations.gemspec +1 -9
  2. data/lib/client_side_validations.rb +6 -2
  3. data/lib/client_side_validations/action_view/form_builder.rb +4 -2
  4. data/lib/client_side_validations/action_view/form_helper.rb +15 -7
  5. data/lib/client_side_validations/active_model.rb +5 -2
  6. data/lib/client_side_validations/active_record.rb +2 -1
  7. data/lib/client_side_validations/active_record/middleware.rb +15 -2
  8. data/lib/client_side_validations/active_record/uniqueness.rb +0 -1
  9. data/lib/client_side_validations/core_ext/regexp.rb +1 -1
  10. data/lib/client_side_validations/middleware.rb +40 -16
  11. data/lib/client_side_validations/version.rb +1 -1
  12. data/lib/generators/templates/client_side_validations/initializer.rb +0 -3
  13. data/vendor/assets/javascripts/rails.validations.js +135 -170
  14. metadata +22 -329
  15. data/lib/client_side_validations/formtastic.rb +0 -21
  16. data/lib/client_side_validations/mongo_mapper.rb +0 -9
  17. data/lib/client_side_validations/mongo_mapper/middleware.rb +0 -20
  18. data/lib/client_side_validations/mongo_mapper/uniqueness.rb +0 -29
  19. data/lib/client_side_validations/mongoid.rb +0 -9
  20. data/lib/client_side_validations/mongoid/format.rb +0 -10
  21. data/lib/client_side_validations/mongoid/length.rb +0 -24
  22. data/lib/client_side_validations/mongoid/middleware.rb +0 -20
  23. data/lib/client_side_validations/mongoid/presence.rb +0 -10
  24. data/lib/client_side_validations/mongoid/uniqueness.rb +0 -29
  25. data/lib/client_side_validations/simple_form.rb +0 -24
  26. data/lib/generators/templates/client_side_validations/README.rails.3.0 +0 -6
  27. data/test/action_view/cases/helper.rb +0 -176
  28. data/test/action_view/cases/test_helpers.rb +0 -635
  29. data/test/action_view/cases/test_legacy_helpers.rb +0 -217
  30. data/test/action_view/models.rb +0 -3
  31. data/test/action_view/models/comment.rb +0 -35
  32. data/test/action_view/models/post.rb +0 -35
  33. data/test/active_model/cases/helper.rb +0 -4
  34. data/test/active_model/cases/test_acceptance_validator.rb +0 -16
  35. data/test/active_model/cases/test_base.rb +0 -11
  36. data/test/active_model/cases/test_confirmation_validator.rb +0 -16
  37. data/test/active_model/cases/test_exclusion_validator.rb +0 -20
  38. data/test/active_model/cases/test_format_validator.rb +0 -21
  39. data/test/active_model/cases/test_inclusion_validator.rb +0 -21
  40. data/test/active_model/cases/test_length_validator.rb +0 -61
  41. data/test/active_model/cases/test_numericality_validator.rb +0 -46
  42. data/test/active_model/cases/test_presence_validator.rb +0 -16
  43. data/test/active_model/cases/test_validations.rb +0 -175
  44. data/test/active_model/models/person.rb +0 -17
  45. data/test/active_record/cases/helper.rb +0 -12
  46. data/test/active_record/cases/test_base.rb +0 -11
  47. data/test/active_record/cases/test_middleware.rb +0 -175
  48. data/test/active_record/cases/test_uniqueness_validator.rb +0 -56
  49. data/test/active_record/models/guid.rb +0 -7
  50. data/test/active_record/models/user.rb +0 -14
  51. data/test/base_helper.rb +0 -21
  52. data/test/core_ext/cases/test_core_ext.rb +0 -46
  53. data/test/formtastic/cases/helper.rb +0 -7
  54. data/test/formtastic/cases/test_form_builder.rb +0 -11
  55. data/test/formtastic/cases/test_form_helper.rb +0 -21
  56. data/test/generators/cases/test_generators.rb +0 -70
  57. data/test/javascript/config.ru +0 -3
  58. data/test/javascript/public/test/callbacks/elementAfter.js +0 -54
  59. data/test/javascript/public/test/callbacks/elementBefore.js +0 -54
  60. data/test/javascript/public/test/callbacks/elementFail.js +0 -70
  61. data/test/javascript/public/test/callbacks/elementPass.js +0 -70
  62. data/test/javascript/public/test/callbacks/formAfter.js +0 -45
  63. data/test/javascript/public/test/callbacks/formBefore.js +0 -45
  64. data/test/javascript/public/test/callbacks/formFail.js +0 -51
  65. data/test/javascript/public/test/callbacks/formPass.js +0 -50
  66. data/test/javascript/public/test/form_builders/validateForm.js +0 -66
  67. data/test/javascript/public/test/form_builders/validateFormtastic.js +0 -54
  68. data/test/javascript/public/test/form_builders/validateNestedForm.js +0 -66
  69. data/test/javascript/public/test/form_builders/validateSimpleForm.js +0 -57
  70. data/test/javascript/public/test/settings.js +0 -15
  71. data/test/javascript/public/test/validateElement.js +0 -179
  72. data/test/javascript/public/test/validators/acceptance.js +0 -42
  73. data/test/javascript/public/test/validators/confirmation.js +0 -25
  74. data/test/javascript/public/test/validators/exclusion.js +0 -41
  75. data/test/javascript/public/test/validators/format.js +0 -27
  76. data/test/javascript/public/test/validators/inclusion.js +0 -42
  77. data/test/javascript/public/test/validators/length.js +0 -76
  78. data/test/javascript/public/test/validators/numericality.js +0 -158
  79. data/test/javascript/public/test/validators/presence.js +0 -21
  80. data/test/javascript/public/test/validators/uniqueness.js +0 -107
  81. data/test/javascript/public/vendor/jquery.metadata.js +0 -122
  82. data/test/javascript/public/vendor/qunit.css +0 -196
  83. data/test/javascript/public/vendor/qunit.js +0 -1374
  84. data/test/javascript/server.rb +0 -84
  85. data/test/javascript/views/index.erb +0 -20
  86. data/test/javascript/views/layout.erb +0 -21
  87. data/test/middleware/cases/helper.rb +0 -7
  88. data/test/middleware/cases/test_middleware.rb +0 -8
  89. data/test/mongo_mapper/cases/helper.rb +0 -9
  90. data/test/mongo_mapper/cases/test_base.rb +0 -15
  91. data/test/mongo_mapper/cases/test_middleware.rb +0 -77
  92. data/test/mongo_mapper/cases/test_uniqueness_validator.rb +0 -55
  93. data/test/mongo_mapper/models/magazine.rb +0 -11
  94. data/test/mongoid/cases/helper.rb +0 -12
  95. data/test/mongoid/cases/test_base.rb +0 -15
  96. data/test/mongoid/cases/test_format_validator.rb +0 -21
  97. data/test/mongoid/cases/test_length_validator.rb +0 -60
  98. data/test/mongoid/cases/test_middleware.rb +0 -77
  99. data/test/mongoid/cases/test_presence_validator.rb +0 -16
  100. data/test/mongoid/cases/test_uniqueness_validator.rb +0 -54
  101. data/test/mongoid/models/book.rb +0 -12
  102. data/test/simple_form/cases/helper.rb +0 -5
  103. data/test/simple_form/cases/test_form_builder.rb +0 -14
  104. data/test/simple_form/cases/test_form_helper.rb +0 -24
  105. data/test/test_loader.rb +0 -6
@@ -13,19 +13,11 @@ Gem::Specification.new do |s|
13
13
  s.description = %q{Client Side Validations}
14
14
 
15
15
  s.files = `git ls-files -- {lib/*,vendor/*,*.gemspec}`.split("\n")
16
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
16
  s.require_paths = ["lib"]
19
17
 
20
- s.add_development_dependency 'rails', '~> 3.1.0'
18
+ s.add_development_dependency 'rails', '~> 3.2.0'
21
19
  s.add_development_dependency 'sqlite3'
22
- s.add_development_dependency 'bson_ext'
23
- s.add_development_dependency 'mongoid', '~> 2.4.0'
24
- s.add_development_dependency 'mongo_mapper','~> 0.9.0'
25
20
  s.add_development_dependency 'mocha'
26
- s.add_development_dependency 'simple_form', '~> 1.5.0'
27
- s.add_development_dependency 'formtastic', '~> 2.0.0'
28
- s.add_development_dependency 'm'
29
21
 
30
22
  # For QUnit testing
31
23
  s.add_development_dependency 'sinatra', '~> 1.0'
@@ -1,10 +1,14 @@
1
1
  module ClientSideValidations
2
+ module Config
3
+ class << self
4
+ attr_accessor :uniqueness_validator_disabled
5
+ @uniqueness_validator_disabled = false
6
+ end
7
+ end
2
8
  end
3
9
 
4
10
  require 'client_side_validations/active_model' if defined?(::ActiveModel)
5
11
  require 'client_side_validations/active_record' if defined?(::ActiveRecord)
6
- require 'client_side_validations/mongoid' if defined?(::Mongoid)
7
- require 'client_side_validations/mongo_mapper' if defined?(::MongoMapper)
8
12
  require 'client_side_validations/action_view' if defined?(::ActionView)
9
13
  if defined?(::Rails)
10
14
  require 'client_side_validations/middleware'
@@ -72,7 +72,7 @@ module ClientSideValidations::ActionView::Helpers
72
72
 
73
73
  def time_zone_select_with_client_side_validations(method, priority_zones = nil, options = {}, html_options = {})
74
74
  apply_client_side_validators(method, html_options)
75
- time_zone_select_without_client_side_validations(method, priority_zones, options, html_options)
75
+ time_zone_select_without_client_side_validations(method, priority_zones = nil, options, html_options)
76
76
  end
77
77
 
78
78
  private
@@ -80,7 +80,9 @@ module ClientSideValidations::ActionView::Helpers
80
80
  def apply_client_side_validators(method, options = {})
81
81
  if @options[:validate] && options[:validate] != false && validators = filter_validators(method, options[:validate])
82
82
  options.merge!("data-validate" => true)
83
- @options[:validators].merge!("#{@object_name}[#{method}]#{options[:multiple] ? "[]" : nil}" => validators)
83
+ name = options[:name] || "#{@object_name}[#{method}]"
84
+
85
+ @options[:validators].merge!("#{name}#{options[:multiple] ? "[]" : nil}" => validators)
84
86
  end
85
87
  end
86
88
 
@@ -6,8 +6,6 @@ module ClientSideValidations::ActionView::Helpers
6
6
  options = args.extract_options!
7
7
  if options[:validate]
8
8
 
9
- content_for_name = options[:validate] unless options[:validate] == true
10
-
11
9
  # Always turn off HTML5 Validations
12
10
  options[:html] ||= {}
13
11
  options[:html][:novalidate] = 'novalidate'
@@ -23,17 +21,27 @@ module ClientSideValidations::ActionView::Helpers
23
21
  end
24
22
 
25
23
  @validators = {}
24
+
26
25
  # Order matters here. Rails mutates the options object
27
26
  script = client_side_form_settings(object, options)
28
27
  form = super(record_or_name_or_array, *(args << options), &proc)
28
+
29
29
  # Because of the load order requirement above this sub is necessary
30
30
  # Would be nice to not do this
31
31
  script = insert_validators_into_script(script)
32
- if content_for_name
33
- content_for(content_for_name) { script.html_safe }
34
- script = nil
32
+
33
+ if assign_script_to_content_for(options[:validate], script)
34
+ form.html_safe
35
+ else
36
+ "#{form}#{script}".html_safe
37
+ end
38
+ end
39
+
40
+ def assign_script_to_content_for(name, script)
41
+ if name && name != true
42
+ content_for(name) { script.html_safe }
43
+ true
35
44
  end
36
- "#{form}#{script}".html_safe
37
45
  end
38
46
 
39
47
  def apply_form_for_options!(object_or_array, options)
@@ -75,7 +83,7 @@ module ClientSideValidations::ActionView::Helpers
75
83
  end
76
84
 
77
85
  content_tag(:script) do
78
- "window['#{var_name}'] = #{builder.client_side_form_settings(options, self).merge(:validators => 'validator_hash').to_json};".html_safe
86
+ "window.ClientSideValidations.forms['#{var_name}'] = #{builder.client_side_form_settings(options, self).merge(:validators => 'validator_hash').to_json};".html_safe
79
87
  end
80
88
 
81
89
  end
@@ -5,7 +5,6 @@ module ClientSideValidations::ActiveModel
5
5
 
6
6
  def client_side_hash(model, attribute)
7
7
  options = self.options.dup
8
- # debugger
9
8
  { :message => model.errors.generate_message(attribute, message_type, options) }.merge(options.except(*::ActiveModel::Errors::CALLBACKS_OPTIONS - [:allow_blank, :if, :unless]))
10
9
  end
11
10
 
@@ -24,7 +23,7 @@ module ClientSideValidations::ActiveModel
24
23
  validator_hash = attr[1].inject({}) do |kind_hash, validator|
25
24
  client_side_hash = validator.client_side_hash(self, attr[0])
26
25
  # Yeah yeah, #new_record? is not part of ActiveModel :p
27
- if (can_use_for_client_side_validation?(client_side_hash, validator))
26
+ if can_use_for_client_side_validation?(client_side_hash, validator) && uniqueness_validations_allowed_or_not_applicable?(validator)
28
27
  kind_hash.merge!(validator.kind => client_side_hash.except(:on))
29
28
  else
30
29
  kind_hash.merge!({})
@@ -47,6 +46,10 @@ module ClientSideValidations::ActiveModel
47
46
  def can_use_for_client_side_validation?(client_side_hash, validator)
48
47
  ((self.respond_to?(:new_record?) && validator.options[:on] == (self.new_record? ? :create : :update)) || validator.options[:on].nil?) && validator.kind != :block
49
48
  end
49
+
50
+ def uniqueness_validations_allowed_or_not_applicable?(validator)
51
+ validator.kind != :uniqueness || !ClientSideValidations::Config.uniqueness_validator_disabled
52
+ end
50
53
  end
51
54
  end
52
55
 
@@ -1,4 +1,5 @@
1
1
  require 'client_side_validations/active_model'
2
+ require 'client_side_validations/middleware'
2
3
  require 'client_side_validations/active_record/middleware'
3
4
 
4
5
  %w{uniqueness}.each do |validator|
@@ -8,4 +9,4 @@ require 'client_side_validations/active_record/middleware'
8
9
  end
9
10
 
10
11
  ActiveRecord::Base.send(:include, ClientSideValidations::ActiveModel::Validations)
11
-
12
+ ClientSideValidations::Middleware::Uniqueness.register_orm(ClientSideValidations::ActiveRecord::Middleware)
@@ -1,7 +1,12 @@
1
1
  module ClientSideValidations::ActiveRecord
2
2
  class Middleware
3
3
 
4
+ def self.is_class?(klass)
5
+ klass < ::ActiveRecord::Base
6
+ end
7
+
4
8
  def self.is_unique?(klass, attribute, value, params)
9
+ value = type_cast_value(klass, attribute, value)
5
10
  column = klass.columns_hash[attribute.to_s]
6
11
  value = column.limit ? value.to_s.mb_chars[0, column.limit] : value.to_s if column.text?
7
12
 
@@ -23,11 +28,19 @@ module ClientSideValidations::ActiveRecord
23
28
  relation = relation.and(t.primary_key.not_eq(params[:id])) if params[:id]
24
29
  end
25
30
 
26
- (params[:scope] || {}).each do |key, value|
27
- relation = relation.and(t[key].eq(value))
31
+ (params[:scope] || {}).each do |attribute, value|
32
+ value = type_cast_value(klass, attribute, value)
33
+ relation = relation.and(t[attribute].eq(value))
28
34
  end
29
35
 
30
36
  !klass.where(relation).exists?
31
37
  end
38
+
39
+ private
40
+
41
+ def self.type_cast_value(klass, attribute, value)
42
+ klass.columns_hash[attribute].type_cast(value)
43
+ end
44
+
32
45
  end
33
46
  end
@@ -4,7 +4,6 @@ module ClientSideValidations::ActiveRecord
4
4
  hash = {}
5
5
  hash[:message] = model.errors.generate_message(attribute, message_type, options.except(:scope))
6
6
  hash[:case_sensitive] = options[:case_sensitive]
7
- hash[:allow_blank] = options[:allow_blank] if options.key?(:allow_blank)
8
7
  hash[:id] = model.id unless model.new_record?
9
8
  if options.key?(:scope) && options[:scope].present?
10
9
  hash[:scope] = Array.wrap(options[:scope]).inject({}) do |scope_hash, scope_item|
@@ -1,6 +1,6 @@
1
1
  class Regexp
2
2
  def as_json(options = nil)
3
- Regexp.new inspect.sub("\\A","^").sub("\\Z","$").sub("\\z","$").sub(/^\//,"").sub(/\/[a-z]*$/,""), self.options
3
+ Regexp.new inspect.sub('\\A','^').sub('\\Z','$').sub('\\z','$').sub(/^\//,'').sub(/\/[a-z]*$/,'').gsub(/\(\?#.+\)/, '').gsub(/\(\?-\w+:/,'('), self.options
4
4
  end
5
5
 
6
6
  def to_json(options = nil)
@@ -11,11 +11,11 @@ module ClientSideValidations
11
11
  end
12
12
 
13
13
  def call(env)
14
- case env['PATH_INFO']
15
- when %r{\/validators\/(\w+)}
16
- "::ClientSideValidations::Middleware::#{$1.camelize}".constantize.new(env).response
17
- else
14
+ matches = /^\/validators\/(\w+)$/.match(env['PATH_INFO'])
15
+ if !matches || (matches[1] == 'uniqueness' && Config.uniqueness_validator_disabled)
18
16
  @app.call(env)
17
+ else
18
+ "::ClientSideValidations::Middleware::#{matches[1].camelize}".constantize.new(env).response
19
19
  end
20
20
  end
21
21
  end
@@ -40,6 +40,7 @@ module ClientSideValidations
40
40
 
41
41
  class Uniqueness < Base
42
42
  IGNORE_PARAMS = %w{case_sensitive id scope}
43
+ REGISTERED_ORMS = []
43
44
 
44
45
  def response
45
46
  if is_unique?
@@ -52,23 +53,46 @@ module ClientSideValidations
52
53
  super
53
54
  end
54
55
 
56
+ def self.register_orm(orm)
57
+ registered_orms << orm
58
+ end
59
+
60
+ def self.registered_orms
61
+ REGISTERED_ORMS
62
+ end
63
+
64
+ def registered_orms
65
+ self.class.registered_orms
66
+ end
67
+
55
68
  private
56
69
 
57
70
  def is_unique?
58
- resource = extract_resource
59
- klass = resource.classify.constantize
60
- attribute = request.params[resource].keys.first
61
- value = request.params[resource][attribute]
62
-
63
- if (defined?(::ActiveRecord::Base) && klass < ::ActiveRecord::Base)
64
- middleware_klass = ClientSideValidations::ActiveRecord::Middleware
65
- elsif (defined?(::Mongoid::Document) && klass.included_modules.include?(::Mongoid::Document))
66
- middleware_klass = ClientSideValidations::Mongoid::Middleware
67
- elsif (defined?(::MongoMapper::Document) && klass.included_modules.include?(::MongoMapper::Document))
68
- middleware_klass = ClientSideValidations::MongoMapper::Middleware
71
+ convert_scope_value_from_null_to_nil
72
+ resource = extract_resource
73
+ klass = resource.classify.constantize
74
+ attribute = request.params[resource].keys.first
75
+ value = request.params[resource][attribute]
76
+ middleware_class = nil
77
+
78
+ registered_orms.each do |orm|
79
+ if orm.is_class?(klass)
80
+ middleware_class = orm
81
+ break
82
+ end
69
83
  end
70
84
 
71
- middleware_klass.is_unique?(klass, attribute, value, request.params)
85
+ middleware_class.is_unique?(klass, attribute, value, request.params)
86
+ end
87
+
88
+ def convert_scope_value_from_null_to_nil
89
+ if request.params['scope']
90
+ request.params['scope'].each do |key, value|
91
+ if value == 'null'
92
+ request.params['scope'][key] = nil
93
+ end
94
+ end
95
+ end
72
96
  end
73
97
 
74
98
  def extract_resource
@@ -1,3 +1,3 @@
1
1
  module ClientSideValidations
2
- VERSION = '3.1.5'
2
+ VERSION = '3.2.0.beta.1'
3
3
  end
@@ -1,8 +1,5 @@
1
1
  # ClientSideValidations Initializer
2
2
 
3
- require 'client_side_validations/simple_form' if defined?(::SimpleForm)
4
- require 'client_side_validations/formtastic' if defined?(::Formtastic)
5
-
6
3
  # Uncomment the following block if you want each input field to have the validation messages attached.
7
4
  # ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
8
5
  # unless html_tag =~ /^<label/
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Rails 3 Client Side Validations - v3.1.4
2
+ * Rails 3 Client Side Validations - v3.2.0.beta.1
3
3
  * https://github.com/bcardarella/client_side_validations
4
4
  *
5
5
  * Copyright (c) 2011 Brian Cardarella
@@ -7,80 +7,79 @@
7
7
  * http://www.opensource.org/licenses/mit-license.php
8
8
  */
9
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')];
10
+ (function ($) {
11
+ $.fn.validate = function () {
12
+ return this.filter('form[data-validate]').each(function () {
13
+ var form = $(this),
14
+ settings = window['ClientSideValidations']['forms'][form.attr('id')],
15
+ addError = function (element, message) {
16
+ ClientSideValidations.formBuilders[settings.type].add(element, settings, message);
17
+ },
18
+ removeError = function (element) {
19
+ ClientSideValidations.formBuilders[settings.type].remove(element, settings);
20
+ };
15
21
 
16
22
  // Set up the events for the form
17
23
  form
18
- .submit( function() { return form.isValid(settings.validators); })
19
- .bind('ajax:beforeSend', function(eventData) { if(eventData.target == this) return form.isValid(settings.validators); })
24
+ .submit( function () { return form.isValid(settings.validators); })
25
+ .bind('ajax:beforeSend', function (eventData) { if(eventData.target == this) return form.isValid(settings.validators); })
20
26
  // 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); })
27
+ .bind('form:validate:after', function (eventData) { ClientSideValidations.callbacks.form.after( form, eventData); } )
28
+ .bind('form:validate:before', function (eventData) { ClientSideValidations.callbacks.form.before(form, eventData); } )
29
+ .bind('form:validate:fail', function (eventData) { ClientSideValidations.callbacks.form.fail( form, eventData); } )
30
+ .bind('form:validate:pass', function (eventData) { ClientSideValidations.callbacks.form.pass( form, eventData); } )
25
31
 
26
32
  // 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); })
33
+ .find('[data-validate="true"]:input:enabled:not(:radio)')
34
+ .live('focusout', function () { $(this).isValid(settings.validators); })
35
+ .live('change', function () { $(this).data('changed', true); })
30
36
  // 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) {
37
+ .live('element:validate:after', function (eventData) { ClientSideValidations.callbacks.element.after( $(this), eventData); })
38
+ .live('element:validate:before', function (eventData) { ClientSideValidations.callbacks.element.before($(this), eventData); })
39
+ .live('element:validate:fail', function (eventData, message) {
34
40
  var element = $(this);
35
- clientSideValidations.callbacks.element.fail(element, message, function() {
41
+ ClientSideValidations.callbacks.element.fail(element, message, function () {
36
42
  addError(element, message);
37
- }, eventData) })
38
- .live('element:validate:pass', function(eventData) {
43
+ }, eventData); })
44
+ .live('element:validate:pass', function (eventData) {
39
45
  var element = $(this);
40
- clientSideValidations.callbacks.element.pass(element, function() {
46
+ ClientSideValidations.callbacks.element.pass(element, function () {
41
47
  removeError(element);
42
- }, eventData) })
48
+ }, eventData); })
43
49
  // Checkboxes - Live events don't support filter
44
- .end().find('[data-validate]:checkbox')
45
- .live('click', function() { $(this).isValid(settings.validators); })
50
+ .end().find('[data-validate="true"]:checkbox')
51
+ .live('click', function () { $(this).isValid(settings.validators); })
46
52
  // Inputs for confirmations
47
- .end().find('[id*=_confirmation]').each(function() {
53
+ .end().find('[id*=_confirmation]').each(function () {
48
54
  var confirmationElement = $(this),
49
- element = form.find('#' + this.id.match(/(.+)_confirmation/)[1] + '[data-validate]:input');
55
+ element = form.find('#' + this.id.match(/(.+)_confirmation/)[1] + '[data-validate="true"]:input');
50
56
 
51
57
  if (element[0]) {
52
58
  $('#' + confirmationElement.attr('id'))
53
- .live('focusout', function() {
59
+ .live('focusout', function () {
54
60
  element.data('changed', true).isValid(settings.validators);
55
61
  })
56
- .live('keyup', function() {
62
+ .live('keyup', function () {
57
63
  element.data('changed', true).isValid(settings.validators);
58
- })
64
+ });
59
65
  }
60
66
  });
61
67
 
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
68
  });
70
- }
69
+ };
71
70
 
72
- $.fn.isValid = function(validators) {
71
+ $.fn.isValid = function (validators) {
73
72
  if ($(this[0]).is('form')) {
74
73
  return validateForm($(this[0]), validators);
75
74
  } else {
76
75
  return validateElement($(this[0]), validators[this[0].name]);
77
76
  }
78
- }
77
+ };
79
78
 
80
- var validateForm = function(form, validators) {
79
+ var validateForm = function (form, validators) {
81
80
  var valid = true;
82
81
 
83
- form.trigger('form:validate:before').find('[data-validate]:input').each(function() {
82
+ form.trigger('form:validate:before').find('[data-validate="true"]:input:enabled').each(function() {
84
83
  if (!$(this).isValid(validators)) { valid = false; }
85
84
  });
86
85
 
@@ -92,48 +91,57 @@
92
91
 
93
92
  form.trigger('form:validate:after');
94
93
  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);
94
+ },
95
+ validateElement = function (element, validators) {
96
+ element.trigger('element:validate:before');
97
+
98
+ if (element.data('changed') !== false) {
99
+ var valid = true;
100
+ element.data('changed', false);
101
+
102
+ // Because 'length' is defined on the list of validators we cannot call jQuery.each on
103
+ for (kind in ClientSideValidations.validators.local) {
104
+ if (validators[kind] && (message = ClientSideValidations.validators.all()[kind](element, validators[kind]))) {
105
+ element.trigger('element:validate:fail', message).data('valid', false);
106
+ valid = false;
107
+ break;
108
+ }
109
+ }
103
110
 
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
+ if (valid) {
112
+ for (kind in ClientSideValidations.validators.remote) {
113
+ if (validators[kind] && (message = ClientSideValidations.validators.all()[kind](element, validators[kind]))) {
114
+ element.trigger('element:validate:fail', message).data('valid', false);
115
+ valid = false;
116
+ break;
117
+ }
118
+ }
111
119
  }
112
- }
113
120
 
114
- if (valid) { element.data('valid', null); element.trigger('element:validate:pass'); }
115
- }
121
+ if (valid) { element.data('valid', null); element.trigger('element:validate:pass'); }
122
+ }
116
123
 
117
- element.trigger('element:validate:after');
118
- return element.data('valid') === false ? false : true;
119
- }
124
+ element.trigger('element:validate:after');
125
+ return element.data('valid') === false ? false : true;
126
+ };
120
127
 
121
128
  // Main hook
122
129
  // If new forms are dynamically introduced into the DOM the .validate() method
123
130
  // must be invoked on that form
124
- $(function() { $('form[data-validate]').validate(); })
131
+ $(function () { $('form[data-validate]').validate(); });
125
132
  })(jQuery);
126
133
 
127
- var clientSideValidations = {
134
+ var ClientSideValidations = {
135
+ forms: {},
128
136
  validators: {
129
- all: function() { return jQuery.extend({}, clientSideValidations.validators.local, clientSideValidations.validators.remote) },
137
+ all: function() { return jQuery.extend({}, ClientSideValidations.validators.local, ClientSideValidations.validators.remote); },
130
138
  local: {
131
- presence: function(element, options) {
139
+ presence: function (element, options) {
132
140
  if (/^\s*$/.test(element.val() || "")) {
133
141
  return options.message;
134
142
  }
135
143
  },
136
- acceptance: function(element, options) {
144
+ acceptance: function (element, options) {
137
145
  switch (element.attr('type')) {
138
146
  case 'checkbox':
139
147
  if (!element.attr('checked')) {
@@ -147,8 +155,8 @@ var clientSideValidations = {
147
155
  break;
148
156
  }
149
157
  },
150
- format: function(element, options) {
151
- if ((message = this.presence(element, options)) && options.allow_blank == true) {
158
+ format: function (element, options) {
159
+ if ((message = this.presence(element, options)) && options.allow_blank === true) {
152
160
  return;
153
161
  } else if (message) {
154
162
  return message;
@@ -160,8 +168,8 @@ var clientSideValidations = {
160
168
  }
161
169
  }
162
170
  },
163
- numericality: function(element, options) {
164
- if (!/^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d*)?$/.test(element.val()) && element.val() != '') {
171
+ numericality: function (element, options) {
172
+ if (!/^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d*)?$/.test(element.val())) {
165
173
  return options.messages.numericality;
166
174
  }
167
175
 
@@ -170,82 +178,83 @@ var clientSideValidations = {
170
178
  }
171
179
 
172
180
  var CHECKS = { greater_than: '>', greater_than_or_equal_to: '>=',
173
- equal_to: '==', less_than: '<', less_than_or_equal_to: '<=' }
181
+ equal_to: '==', less_than: '<', less_than_or_equal_to: '<=' };
174
182
 
175
- for (var check in CHECKS) {
176
- if (options[check] != undefined && !(new Function("return " + element.val() + CHECKS[check] + options[check])())) {
183
+ for (check in CHECKS) {
184
+ if (options[check] !== undefined && !(new Function("return " + element.val() + CHECKS[check] + options[check])())) {
177
185
  return options.messages[check];
178
186
  }
179
187
  }
180
188
 
181
- if (options.odd && !(parseInt(element.val()) % 2)) {
189
+ if (options.odd && !(parseInt(element.val(), 10) % 2)) {
182
190
  return options.messages.odd;
183
191
  }
184
192
 
185
- if (options.even && (parseInt(element.val()) % 2)) {
193
+ if (options.even && (parseInt(element.val(), 10) % 2)) {
186
194
  return options.messages.even;
187
195
  }
188
196
  },
189
- length: function(element, options) {
190
- var blankOptions = {};
197
+ length: function (element, options) {
198
+ var blankOptions = {},
199
+ CHECKS = { is: '==', minimum: '>=', maximum: '<=' },
200
+ tokenizer = options.js_tokenizer || "split('')",
201
+ tokenized_length = new Function("element", "return (element.val()." + tokenizer + " || '').length;")(element);
191
202
  if (options.is) {
192
203
  blankOptions.message = options.messages.is;
193
204
  } else if (options.minimum) {
194
205
  blankOptions.message = options.messages.minimum;
195
206
  }
196
- if ((message = this.presence(element, blankOptions)) && options.allow_blank == true) {
207
+ if ((message = this.presence(element, blankOptions)) && options.allow_blank === true) {
197
208
  return;
198
209
  } else if (message) {
199
210
  return message;
200
211
  } 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) {
212
+ for (check in CHECKS) {
206
213
  if (options[check] && !(new Function("return " + tokenized_length + CHECKS[check] + options[check])())) {
207
214
  return options.messages[check];
208
215
  }
209
216
  }
210
217
  }
211
218
  },
212
- exclusion: function(element, options) {
213
- if ((message = this.presence(element, options)) && options.allow_blank == true) {
219
+ exclusion: function (element, options) {
220
+ var lower = null, upper = null;
221
+ if ((message = this.presence(element, options)) && options.allow_blank === true) {
214
222
  return;
215
223
  } else if (message) {
216
224
  return message;
217
225
  } else {
218
226
  if (options['in']) {
219
- for (var i = 0; i < options['in'].length; i++) {
227
+ for (i = 0; i < options['in'].length; i = i + 1) {
220
228
  if (options['in'][i] == element.val()) {
221
229
  return options.message;
222
230
  }
223
231
  }
224
- } else if (options['range']) {
225
- var lower = options['range'][0],
226
- upper = options['range'][1];
232
+ } else if (options.range) {
233
+ lower = options.range[0];
234
+ upper = options.range[1];
227
235
  if (element.val() >= lower && element.val() <= upper) {
228
236
  return options.message;
229
237
  }
230
238
  }
231
239
  }
232
240
  },
233
- inclusion: function(element, options) {
234
- if ((message = this.presence(element, options)) && options.allow_blank == true) {
241
+ inclusion: function (element, options) {
242
+ var lower = null, upper = null;
243
+ if ((message = this.presence(element, options)) && options.allow_blank === true) {
235
244
  return;
236
245
  } else if (message) {
237
246
  return message;
238
247
  } else {
239
248
  if (options['in']) {
240
- for (var i = 0; i < options['in'].length; i++) {
249
+ for (i = 0; i < options['in'].length; i = i + 1) {
241
250
  if (options['in'][i] == element.val()) {
242
251
  return;
243
252
  }
244
253
  }
245
254
  return options.message;
246
- } else if (options['range']) {
247
- var lower = options['range'][0],
248
- upper = options['range'][1];
255
+ } else if (options.range) {
256
+ lower = options.range[0];
257
+ upper = options.range[1];
249
258
 
250
259
  if (element.val() >= lower && element.val() <= upper) {
251
260
  return;
@@ -255,30 +264,31 @@ var clientSideValidations = {
255
264
  }
256
265
  }
257
266
  },
258
- confirmation: function(element, options) {
259
- if (element.val() != jQuery('#' + element.attr('id') + '_confirmation').val()) {
267
+ confirmation: function (element, options) {
268
+ if (element.val() !== jQuery('#' + element.attr('id') + '_confirmation').val()) {
260
269
  return options.message;
261
270
  }
262
271
  }
263
272
  },
264
273
  remote: {
265
- uniqueness: function(element, options) {
266
- if ((message = clientSideValidations.validators.local.presence(element, options)) && options.allow_blank === true) {
274
+ uniqueness: function (element, options) {
275
+ if ((message = ClientSideValidations.validators.local.presence(element, options)) && options.allow_blank === true) {
267
276
  return;
268
277
  } else if (message) {
269
278
  return message;
270
279
  } else {
271
- var data = {};
272
- data['case_sensitive'] = !!options.case_sensitive;
280
+ var data = {},
281
+ name = null;
282
+ data.case_sensitive = !!options.case_sensitive;
273
283
  if (options.id) {
274
- data['id'] = options.id;
284
+ data.id = options.id;
275
285
  }
276
286
 
277
287
  if (options.scope) {
278
- data.scope = {}
288
+ data.scope = {};
279
289
  for (key in options.scope) {
280
- var scoped_element = jQuery('[name="' + element.attr('name').replace(/\[\w+]$/, '[' + key + ']' + '"]'));
281
- if (scoped_element[0] && scoped_element.val() != options.scope[key]) {
290
+ var scoped_element = jQuery('[name="' + element.attr('name').replace(/\[\w+\]$/, '[' + key + ']' + '"]'));
291
+ if (scoped_element[0] && scoped_element.val() !== options.scope[key]) {
282
292
  data.scope[key] = scoped_element.val();
283
293
  scoped_element.unbind('change.' + element.id).bind('change.' + element.id, function() { element.trigger('change'); element.trigger('focusout'); });
284
294
  } else {
@@ -291,16 +301,16 @@ var clientSideValidations = {
291
301
  // e.g. user[records_attributes][0][title] => records[title]
292
302
  // e.g. user[record_attributes][title] => record[title]
293
303
  // Server side handles classifying the resource properly
294
- if (/_attributes]/.test(element.attr('name'))) {
295
- var name = element.attr('name').match(/\[\w+_attributes]/g).pop().match(/\[(\w+)_attributes]/).pop();
296
- name += /(\[\w+])$/.exec(element.attr('name'))[1];
304
+ if (/_attributes\]/.test(element.attr('name'))) {
305
+ name = element.attr('name').match(/\[\w+_attributes\]/g).pop().match(/\[(\w+)_attributes\]/).pop();
306
+ name += /(\[\w+\])$/.exec(element.attr('name'))[1];
297
307
  } else {
298
- var name = element.attr('name');
308
+ name = element.attr('name');
299
309
  }
300
310
 
301
311
  // Override the name if a nested module class is passed
302
312
  if (options['class']) {
303
- name = options['class'] + '[' + name.split('[')[1]
313
+ name = options['class'] + '[' + name.split('[')[1];
304
314
  }
305
315
  data[name] = element.val();
306
316
 
@@ -308,7 +318,7 @@ var clientSideValidations = {
308
318
  url: '/validators/uniqueness',
309
319
  data: data,
310
320
  async: false
311
- }).status == 200) {
321
+ }).status === 200) {
312
322
  return options.message;
313
323
  }
314
324
  }
@@ -317,13 +327,13 @@ var clientSideValidations = {
317
327
  },
318
328
  formBuilders: {
319
329
  'ActionView::Helpers::FormBuilder': {
320
- add: function(element, settings, message) {
321
- if (element.data('valid') !== false && jQuery('label.message[for="' + element.attr('id') + '"]')[0] == undefined) {
330
+ add: function (element, settings, message) {
331
+ if (element.data('valid') !== false && jQuery('label.message[for="' + element.attr('id') + '"]')[0] === undefined) {
322
332
  var inputErrorField = jQuery(settings.input_tag),
323
333
  labelErrorField = jQuery(settings.label_tag),
324
334
  label = jQuery('label[for="' + element.attr('id') + '"]:not(.message)');
325
335
 
326
- if (element.attr('autofocus')) { element.attr('autofocus', false) };
336
+ if (element.attr('autofocus')) { element.attr('autofocus', false); }
327
337
  element.before(inputErrorField);
328
338
  inputErrorField.find('span#input_tag').replaceWith(element);
329
339
  inputErrorField.find('label.message').attr('for', element.attr('id'));
@@ -333,7 +343,7 @@ var clientSideValidations = {
333
343
  }
334
344
  jQuery('label.message[for="' + element.attr('id') + '"]').text(message);
335
345
  },
336
- remove: function(element, settings) {
346
+ remove: function (element, settings) {
337
347
  var errorFieldClass = jQuery(settings.input_tag).attr('class'),
338
348
  inputErrorField = element.closest('.' + errorFieldClass),
339
349
  label = jQuery('label[for="' + element.attr('id') + '"]:not(.message)'),
@@ -347,64 +357,19 @@ var clientSideValidations = {
347
357
  }
348
358
  }
349
359
  },
350
- 'SimpleForm::FormBuilder': {
351
- add: function(element, settings, message) {
352
- if (element.data('valid') !== false) {
353
- var wrapper = element.closest(settings.wrapper_tag);
354
- wrapper.addClass(settings.wrapper_error_class);
355
- var errorElement = $('<' + settings.error_tag + ' class="' + settings.error_class + '">' + message + '</' + settings.error_tag + '>');
356
- wrapper.append(errorElement);
357
- } else {
358
- element.parent().find(settings.error_tag + '.' + settings.error_class).text(message);
359
- }
360
- },
361
- remove: function(element, settings) {
362
- var wrapper = element.closest(settings.wrapper_tag + '.' + settings.wrapper_error_class);
363
- wrapper.removeClass(settings.wrapper_error_class);
364
- var errorElement = wrapper.find(settings.error_tag + '.' + settings.error_class);
365
- errorElement.remove();
366
- }
367
-
368
- },
369
- 'Formtastic::FormBuilder': {
370
- add: function(element, settings, message) {
371
- if (element.data('valid') !== false) {
372
- var wrapper = element.closest('li');
373
- wrapper.addClass('error');
374
- var errorElement = $('<p class="' + settings.inline_error_class + '">' + message + '</p>');
375
- wrapper.append(errorElement);
376
- } else {
377
- element.parent().find('p.' + settings.inline_error_class).text(message);
378
- }
379
- },
380
- remove: function(element, settings) {
381
- var wrapper = element.closest('li.error');
382
- wrapper.removeClass('error');
383
- var errorElement = wrapper.find('p.' + settings.inline_error_class);
384
- errorElement.remove();
385
- }
386
- },
387
- 'NestedForm::Builder': {
388
- add: function(element, settings, message) {
389
- clientSideValidations.formBuilders['ActionView::Helpers::FormBuilder'].add(element, settings, message);
390
- },
391
- remove: function(element, settings, message) {
392
- clientSideValidations.formBuilders['ActionView::Helpers::FormBuilder'].remove(element, settings, message);
393
- }
394
- }
395
360
  },
396
361
  callbacks: {
397
362
  element: {
398
- after: function(element, eventData) { },
399
- before: function(element, eventData) { },
400
- fail: function(element, message, addError, eventData) { addError() },
401
- pass: function(element, removeError, eventData) { removeError() }
363
+ after: function (element, eventData) { },
364
+ before: function (element, eventData) { },
365
+ fail: function (element, message, addError, eventData) { addError(); },
366
+ pass: function (element, removeError, eventData) { removeError(); }
402
367
  },
403
368
  form: {
404
- after: function(form, eventData) { },
405
- before: function(form, eventData) { },
406
- fail: function(form, eventData) { },
407
- pass: function(form, eventData) { }
369
+ after: function (form, eventData) { },
370
+ before: function (form, eventData) { },
371
+ fail: function (form, eventData) { },
372
+ pass: function (form, eventData) { }
408
373
  }
409
374
  }
410
375
  };