adamhunter-client_side_validations 2.9.10
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.
- data/LICENSE +24 -0
- data/README.markdown +132 -0
- data/generators/client_side_validations/client_side_validations_generator.rb +17 -0
- data/javascript/lib/client_side_validations.js +93 -0
- data/javascript/lib/jquery-validation.js +1146 -0
- data/lib/client_side_validations.rb +60 -0
- data/lib/client_side_validations/adapters/action_view.rb +153 -0
- data/lib/client_side_validations/adapters/active_model.rb +137 -0
- data/lib/client_side_validations/adapters/orm_base.rb +89 -0
- data/lib/client_side_validations/orm.rb +226 -0
- data/lib/client_side_validations/rails.rb +11 -0
- data/lib/client_side_validations/template.rb +3 -0
- data/lib/generators/client_side_validations_generator.rb +15 -0
- metadata +533 -0
data/LICENSE
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
Copyright (c) 2010, Democratic National Committee
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
6
|
+
* Redistributions of source code must retain the above copyright
|
7
|
+
notice, this list of conditions and the following disclaimer.
|
8
|
+
* Redistributions in binary form must reproduce the above copyright
|
9
|
+
notice, this list of conditions and the following disclaimer in the
|
10
|
+
documentation and/or other materials provided with the distribution.
|
11
|
+
* Neither the name of the Democratic National Committee nor the
|
12
|
+
names of its contributors may be used to endorse or promote products
|
13
|
+
derived from this software without specific prior written permission.
|
14
|
+
|
15
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
16
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
17
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
18
|
+
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
19
|
+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
20
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
21
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
22
|
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
23
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
24
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.markdown
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
# Client Side Validations
|
2
|
+
Now you can easily drop in client side validations in any Rails app. It will use validations defined in a given ActiveRecord (or ActiveModel) class for use with [jquery-validation](http://bassistance.de/jquery-plugins/jquery-plugin-validation/)
|
3
|
+
|
4
|
+
For Rails 2 and Rails 3 example apps please see [client_side_validations_example](http://github.com/dnclabs/client_side_validations_examples)
|
5
|
+
|
6
|
+
The concept is simple:
|
7
|
+
|
8
|
+
1. Include the middleware
|
9
|
+
2. Define validations in the model as you normally would
|
10
|
+
3. The validations are sent to the client in JSON
|
11
|
+
4. client_side_validations.js converts the JSON for a given validation plugin and binds the validator to the form
|
12
|
+
|
13
|
+
Currently the following validations are supported:
|
14
|
+
|
15
|
+
* validates_presence_of
|
16
|
+
* validates_format_of
|
17
|
+
* validates_numericality_of
|
18
|
+
* validates_length_of
|
19
|
+
* validates_size_of
|
20
|
+
* validates_uniqueness_of
|
21
|
+
* validates_confirmation_of
|
22
|
+
* validates_acceptance_of
|
23
|
+
* validates_inclusion_of
|
24
|
+
* validates_exclusion_of
|
25
|
+
|
26
|
+
The uniqueness validation works for both ActiveRecord and Mongoid.
|
27
|
+
|
28
|
+
## Installation
|
29
|
+
> gem install client_side_validations
|
30
|
+
|
31
|
+
### Rails 2
|
32
|
+
Add "config.gem :client_side_validations" to the "config/environment.rb" file
|
33
|
+
|
34
|
+
Then run the generator:
|
35
|
+
> script/generate client_side_validations
|
36
|
+
|
37
|
+
This will copy client_side_validations.js to "public/javascripts"
|
38
|
+
|
39
|
+
**This version of ClientSideValidations will also copy a patched version of jquery-validation.js to "public/javascript"**
|
40
|
+
|
41
|
+
### Rails 3
|
42
|
+
Add "gem 'client_side_validations'" to the Gemfile
|
43
|
+
|
44
|
+
Then run the generator:
|
45
|
+
> rails g client_side_validations
|
46
|
+
|
47
|
+
This will copy client_side_validations.js to "public/javascripts"
|
48
|
+
|
49
|
+
**This version of ClientSideValidations will also copy a patched version of jquery-validation.js to "public/javascript"**
|
50
|
+
|
51
|
+
## Configuration
|
52
|
+
#### *NOTE* This version of ClientSideValidations has a patched version of jquery-validation that will install automatically with the generator. *Do not* download the version listed below.
|
53
|
+
Download [jQuery](http://docs.jquery.com/Downloading_jQuery) and [jQuery Validation](http://bassistance.de/jquery-plugins/jquery-plugin-validation/) plugin to "public/javascripts"
|
54
|
+
|
55
|
+
### Rack
|
56
|
+
As of version 2.9.0 the ClientSideValidations::Uniqueness middleware is automatically included as a Rails Engine. (both Rails 2 and Rails 3)
|
57
|
+
|
58
|
+
### Model
|
59
|
+
Validate your models as you normally would
|
60
|
+
|
61
|
+
class Book < ActiveRecord::Base
|
62
|
+
validates_presence_of :author
|
63
|
+
end
|
64
|
+
|
65
|
+
### Layout
|
66
|
+
You currently need both jQuery and the jQuery Validate plugin loaded before you load Client Side Validations
|
67
|
+
|
68
|
+
...
|
69
|
+
<%= javascript_include_tag 'jquery', 'jquery-validation', 'client_side_validations' %>
|
70
|
+
...
|
71
|
+
|
72
|
+
### View
|
73
|
+
Have a form ask for client side validations by passing :validate => true
|
74
|
+
|
75
|
+
...
|
76
|
+
|
77
|
+
<% form_for @book, :validations => true do |b| %>
|
78
|
+
<%= b.label :author %></br>
|
79
|
+
<%= b.text_field :author %></br>
|
80
|
+
<%= submit_tag 'Create' %>
|
81
|
+
<% end %>
|
82
|
+
|
83
|
+
...
|
84
|
+
|
85
|
+
That should be it!
|
86
|
+
|
87
|
+
## Advanced Options
|
88
|
+
|
89
|
+
### Initialization
|
90
|
+
[jquery-validation can be customized by setting various options](http://docs.jquery.com/Plugins/Validation/validate#toptions)
|
91
|
+
|
92
|
+
Create config/initializers/client_side_validations.rb
|
93
|
+
|
94
|
+
An example set of default options can look like:
|
95
|
+
|
96
|
+
ClientSideValidations.default_options = {
|
97
|
+
:onkeyup => false,
|
98
|
+
:errorClass => "validation_errors"
|
99
|
+
}
|
100
|
+
|
101
|
+
### Model
|
102
|
+
If you want to define only specific fields for client side validations just override the validation_fields method on each model
|
103
|
+
|
104
|
+
class Book < ActiveRecord::Base
|
105
|
+
validatese_presence_of :author
|
106
|
+
validates_presence_of :body
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
def validation_fields
|
111
|
+
[:author]
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
### View
|
117
|
+
You can override the default options set in the initializer for each form:
|
118
|
+
|
119
|
+
<% form_for @book, :validations => { :options => { :errorClass => "bad-field" } } do |b| %>
|
120
|
+
...
|
121
|
+
|
122
|
+
If you are not using an instance variable for form_for or for some reason want to use the validations from another class that can be done in two ways:
|
123
|
+
|
124
|
+
<% form_for :book, :validations => Book %>
|
125
|
+
...
|
126
|
+
|
127
|
+
<% form_for :book, :validations => { :class => Book } %>
|
128
|
+
...
|
129
|
+
|
130
|
+
Written by Brian Cardarella
|
131
|
+
|
132
|
+
Copyright (c) 2010 Democratic National Committee. See LICENSE for details.
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class ClientSideValidationsGenerator < Rails::Generator::Base
|
2
|
+
|
3
|
+
def initialize(runtime_args, runtime_options = {})
|
4
|
+
runtime_options[:source] = File.join(spec.path, '../../javascript/lib')
|
5
|
+
super
|
6
|
+
end
|
7
|
+
|
8
|
+
|
9
|
+
def manifest
|
10
|
+
record do |c|
|
11
|
+
c.directory('public/javascripts')
|
12
|
+
c.file('client_side_validations.js', 'public/javascripts/client_side_validations.js')
|
13
|
+
c.file('jquery-validation.js', 'public/javascripts/jquery-validation.js')
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
jQuery.validator.addMethod("confirmation", function(value, element) {
|
2
|
+
name = element.name.match(/\w+\[(\w+)\]/)[1];
|
3
|
+
confirmation_name = element.name.replace(name, name + '_confirmation')
|
4
|
+
confirmation_element = $('[name="'+ confirmation_name +'"]');
|
5
|
+
confirmation_element.rules('add', {
|
6
|
+
confirmer: $(element)
|
7
|
+
})
|
8
|
+
return this.optional(element) || value == confirmation_element.attr('value');
|
9
|
+
}, jQuery.validator.format("Must match confirmation."));
|
10
|
+
|
11
|
+
jQuery.validator.addMethod("confirmer", function(value, element, original_element) {
|
12
|
+
original_element.valid();
|
13
|
+
return true;
|
14
|
+
}, jQuery.validator.format(""));
|
15
|
+
|
16
|
+
jQuery.validator.addMethod("numericality", function(value, element) {
|
17
|
+
return this.optional(element) || /^(\d+(\.|,)\d+|\d+)$/.test(value);
|
18
|
+
}, jQuery.validator.format("Is not a number."));
|
19
|
+
|
20
|
+
jQuery.validator.addMethod("greater_than", function(value, element, params) {
|
21
|
+
return this.optional(element) || parseFloat(value) > params;
|
22
|
+
}, jQuery.validator.format("Wrong number."));
|
23
|
+
|
24
|
+
jQuery.validator.addMethod("less_than", function(value, element, params) {
|
25
|
+
return this.optional(element) || parseFloat(value) < params;
|
26
|
+
}, jQuery.validator.format("Wrong number."));
|
27
|
+
|
28
|
+
jQuery.validator.addMethod("odd", function(value, element) {
|
29
|
+
return this.optional(element) || parseInt(value) % 2 == 1;
|
30
|
+
}, jQuery.validator.format("Must be odd."));
|
31
|
+
|
32
|
+
jQuery.validator.addMethod("even", function(value, element) {
|
33
|
+
return this.optional(element) || parseInt(value) % 2 == 0;
|
34
|
+
}, jQuery.validator.format("Must be even."));
|
35
|
+
|
36
|
+
jQuery.validator.addMethod("format", function(value, element, params) {
|
37
|
+
var pattern = new RegExp(params, "i");
|
38
|
+
return this.optional(element) || pattern.test(value);
|
39
|
+
}, jQuery.validator.format("Invalid format."));
|
40
|
+
|
41
|
+
jQuery.validator.addMethod("acceptance", function(value, element, params) {
|
42
|
+
return element.checked;
|
43
|
+
}, jQuery.validator.format("Was not accepted."));
|
44
|
+
|
45
|
+
jQuery.validator.addMethod("inclusion", function(value, element, params) {
|
46
|
+
if (this.optional(element)) {
|
47
|
+
return true;
|
48
|
+
} else {
|
49
|
+
|
50
|
+
for (var i=0, len=params.length; i<len; ++i ) {
|
51
|
+
if (value == String(params[i])) {
|
52
|
+
return true;
|
53
|
+
}
|
54
|
+
}
|
55
|
+
}
|
56
|
+
return false;
|
57
|
+
}, jQuery.validator.format("Not included in list."));
|
58
|
+
|
59
|
+
jQuery.validator.addMethod("exclusion", function(value, element, params) {
|
60
|
+
if (this.optional(element)) {
|
61
|
+
return true;
|
62
|
+
} else {
|
63
|
+
for (var i=0, len=params.length; i<len; ++i ) {
|
64
|
+
if (value == String(params[i])) {
|
65
|
+
return false;
|
66
|
+
}
|
67
|
+
}
|
68
|
+
}
|
69
|
+
return true;
|
70
|
+
}, jQuery.validator.format("Is reserved."));
|
71
|
+
|
72
|
+
jQuery.validator.addMethod("islength", function(value, element, length) {
|
73
|
+
return this.optional(element) || value.length == length;
|
74
|
+
}, jQuery.validator.format("Is the wrong length."));
|
75
|
+
|
76
|
+
$.extend($.fn, {
|
77
|
+
clientSideValidations: function() {
|
78
|
+
for (var i = 0; i < this.size(); i++) {
|
79
|
+
var form = $(this[i]);
|
80
|
+
var object = form.attr('data-csv');
|
81
|
+
var validate_options = eval(object + "_validate_options");
|
82
|
+
if (typeof(validate_options['options']) == 'undefined') {
|
83
|
+
validate_options['options'] = { };
|
84
|
+
}
|
85
|
+
validate_options.options.ignore = ':hidden';
|
86
|
+
form.validate(validate_options);
|
87
|
+
}
|
88
|
+
}
|
89
|
+
});
|
90
|
+
|
91
|
+
$(function() {
|
92
|
+
$('form[data-csv]').clientSideValidations();
|
93
|
+
});
|
@@ -0,0 +1,1146 @@
|
|
1
|
+
/*
|
2
|
+
* jQuery validation plug-in 1.7
|
3
|
+
*
|
4
|
+
* http://bassistance.de/jquery-plugins/jquery-plugin-validation/
|
5
|
+
* http://docs.jquery.com/Plugins/Validation
|
6
|
+
*
|
7
|
+
* Copyright (c) 2006 - 2008 Jörn Zaefferer
|
8
|
+
*
|
9
|
+
* $Id: jquery-validation.js 6403 2009-06-17 14:27:16Z joern.zaefferer $
|
10
|
+
*
|
11
|
+
* Dual licensed under the MIT and GPL licenses:
|
12
|
+
* http://www.opensource.org/licenses/mit-license.php
|
13
|
+
* http://www.gnu.org/licenses/gpl.html
|
14
|
+
*/
|
15
|
+
|
16
|
+
(function($) {
|
17
|
+
|
18
|
+
$.extend($.fn, {
|
19
|
+
// http://docs.jquery.com/Plugins/Validation/validate
|
20
|
+
validate: function( options ) {
|
21
|
+
|
22
|
+
// if nothing is selected, return nothing; can't chain anyway
|
23
|
+
if (!this.length) {
|
24
|
+
options && options.debug && window.console && console.warn( "nothing selected, can't validate, returning nothing" );
|
25
|
+
return;
|
26
|
+
}
|
27
|
+
|
28
|
+
// check if a validator for this form was already created
|
29
|
+
var validator = $.data(this[0], 'validator');
|
30
|
+
if ( validator ) {
|
31
|
+
return validator;
|
32
|
+
}
|
33
|
+
|
34
|
+
validator = new $.validator( options, this[0] );
|
35
|
+
$.data(this[0], 'validator', validator);
|
36
|
+
|
37
|
+
if ( validator.settings.onsubmit ) {
|
38
|
+
|
39
|
+
// allow suppresing validation by adding a cancel class to the submit button
|
40
|
+
this.find("input, button").filter(".cancel").click(function() {
|
41
|
+
validator.cancelSubmit = true;
|
42
|
+
});
|
43
|
+
|
44
|
+
// when a submitHandler is used, capture the submitting button
|
45
|
+
if (validator.settings.submitHandler) {
|
46
|
+
this.find("input, button").filter(":submit").click(function() {
|
47
|
+
validator.submitButton = this;
|
48
|
+
});
|
49
|
+
}
|
50
|
+
|
51
|
+
// validate the form on submit
|
52
|
+
this.submit( function( event ) {
|
53
|
+
if ( validator.settings.debug )
|
54
|
+
// prevent form submit to be able to see console output
|
55
|
+
event.preventDefault();
|
56
|
+
|
57
|
+
function handle() {
|
58
|
+
if ( validator.settings.submitHandler ) {
|
59
|
+
if (validator.submitButton) {
|
60
|
+
// insert a hidden input as a replacement for the missing submit button
|
61
|
+
var hidden = $("<input type='hidden'/>").attr("name", validator.submitButton.name).val(validator.submitButton.value).appendTo(validator.currentForm);
|
62
|
+
}
|
63
|
+
validator.settings.submitHandler.call( validator, validator.currentForm );
|
64
|
+
if (validator.submitButton) {
|
65
|
+
// and clean up afterwards; thanks to no-block-scope, hidden can be referenced
|
66
|
+
hidden.remove();
|
67
|
+
}
|
68
|
+
return false;
|
69
|
+
}
|
70
|
+
return true;
|
71
|
+
}
|
72
|
+
|
73
|
+
// prevent submit for invalid forms or custom submit handlers
|
74
|
+
if ( validator.cancelSubmit ) {
|
75
|
+
validator.cancelSubmit = false;
|
76
|
+
return handle();
|
77
|
+
}
|
78
|
+
if ( validator.form() ) {
|
79
|
+
if ( validator.pendingRequest ) {
|
80
|
+
validator.formSubmitted = true;
|
81
|
+
return false;
|
82
|
+
}
|
83
|
+
return handle();
|
84
|
+
} else {
|
85
|
+
validator.focusInvalid();
|
86
|
+
return false;
|
87
|
+
}
|
88
|
+
});
|
89
|
+
}
|
90
|
+
|
91
|
+
return validator;
|
92
|
+
},
|
93
|
+
// http://docs.jquery.com/Plugins/Validation/valid
|
94
|
+
valid: function() {
|
95
|
+
if ( $(this[0]).is('form')) {
|
96
|
+
return this.validate().form();
|
97
|
+
} else {
|
98
|
+
var valid = true;
|
99
|
+
var validator = $(this[0].form).validate();
|
100
|
+
this.each(function() {
|
101
|
+
valid &= validator.element(this);
|
102
|
+
});
|
103
|
+
return valid;
|
104
|
+
}
|
105
|
+
},
|
106
|
+
// attributes: space seperated list of attributes to retrieve and remove
|
107
|
+
removeAttrs: function(attributes) {
|
108
|
+
var result = {},
|
109
|
+
$element = this;
|
110
|
+
$.each(attributes.split(/\s/), function(index, value) {
|
111
|
+
result[value] = $element.attr(value);
|
112
|
+
$element.removeAttr(value);
|
113
|
+
});
|
114
|
+
return result;
|
115
|
+
},
|
116
|
+
// http://docs.jquery.com/Plugins/Validation/rules
|
117
|
+
rules: function(command, argument) {
|
118
|
+
var element = this[0];
|
119
|
+
|
120
|
+
if (command) {
|
121
|
+
var settings = $.data(element.form, 'validator').settings;
|
122
|
+
var staticRules = settings.rules;
|
123
|
+
var existingRules = $.validator.staticRules(element);
|
124
|
+
switch(command) {
|
125
|
+
case "add":
|
126
|
+
$.extend(existingRules, $.validator.normalizeRule(argument));
|
127
|
+
staticRules[element.name] = existingRules;
|
128
|
+
if (argument.messages)
|
129
|
+
settings.messages[element.name] = $.extend( settings.messages[element.name], argument.messages );
|
130
|
+
break;
|
131
|
+
case "remove":
|
132
|
+
if (!argument) {
|
133
|
+
delete staticRules[element.name];
|
134
|
+
return existingRules;
|
135
|
+
}
|
136
|
+
var filtered = {};
|
137
|
+
$.each(argument.split(/\s/), function(index, method) {
|
138
|
+
filtered[method] = existingRules[method];
|
139
|
+
delete existingRules[method];
|
140
|
+
});
|
141
|
+
return filtered;
|
142
|
+
}
|
143
|
+
}
|
144
|
+
|
145
|
+
var data = $.validator.normalizeRules(
|
146
|
+
$.extend(
|
147
|
+
{},
|
148
|
+
$.validator.metadataRules(element),
|
149
|
+
$.validator.classRules(element),
|
150
|
+
$.validator.attributeRules(element),
|
151
|
+
$.validator.staticRules(element)
|
152
|
+
), element);
|
153
|
+
|
154
|
+
// make sure required is at front
|
155
|
+
if (data.required) {
|
156
|
+
var param = data.required;
|
157
|
+
delete data.required;
|
158
|
+
data = $.extend({required: param}, data);
|
159
|
+
}
|
160
|
+
|
161
|
+
return data;
|
162
|
+
}
|
163
|
+
});
|
164
|
+
|
165
|
+
// Custom selectors
|
166
|
+
$.extend($.expr[":"], {
|
167
|
+
// http://docs.jquery.com/Plugins/Validation/blank
|
168
|
+
blank: function(a) {return !$.trim("" + a.value);},
|
169
|
+
// http://docs.jquery.com/Plugins/Validation/filled
|
170
|
+
filled: function(a) {return !!$.trim("" + a.value);},
|
171
|
+
// http://docs.jquery.com/Plugins/Validation/unchecked
|
172
|
+
unchecked: function(a) {return !a.checked;}
|
173
|
+
});
|
174
|
+
|
175
|
+
// constructor for validator
|
176
|
+
$.validator = function( options, form ) {
|
177
|
+
this.settings = $.extend( true, {}, $.validator.defaults, options );
|
178
|
+
this.currentForm = form;
|
179
|
+
this.init();
|
180
|
+
};
|
181
|
+
|
182
|
+
$.validator.format = function(source, params) {
|
183
|
+
if ( arguments.length == 1 )
|
184
|
+
return function() {
|
185
|
+
var args = $.makeArray(arguments);
|
186
|
+
args.unshift(source);
|
187
|
+
return $.validator.format.apply( this, args );
|
188
|
+
};
|
189
|
+
if ( arguments.length > 2 && params.constructor != Array ) {
|
190
|
+
params = $.makeArray(arguments).slice(1);
|
191
|
+
}
|
192
|
+
if ( params.constructor != Array ) {
|
193
|
+
params = [ params ];
|
194
|
+
}
|
195
|
+
$.each(params, function(i, n) {
|
196
|
+
source = source.replace(new RegExp("\\{" + i + "\\}", "g"), n);
|
197
|
+
});
|
198
|
+
return source;
|
199
|
+
};
|
200
|
+
|
201
|
+
$.extend($.validator, {
|
202
|
+
|
203
|
+
defaults: {
|
204
|
+
messages: {},
|
205
|
+
groups: {},
|
206
|
+
rules: {},
|
207
|
+
errorClass: "error",
|
208
|
+
validClass: "valid",
|
209
|
+
errorElement: "label",
|
210
|
+
focusInvalid: true,
|
211
|
+
errorContainer: $( [] ),
|
212
|
+
errorLabelContainer: $( [] ),
|
213
|
+
onsubmit: true,
|
214
|
+
ignore: [],
|
215
|
+
ignoreTitle: false,
|
216
|
+
onfocusin: function(element) {
|
217
|
+
this.lastActive = element;
|
218
|
+
|
219
|
+
// hide error label and remove error class on focus if enabled
|
220
|
+
if ( this.settings.focusCleanup && !this.blockFocusCleanup ) {
|
221
|
+
this.settings.unhighlight && this.settings.unhighlight.call( this, element, this.settings.errorClass, this.settings.validClass );
|
222
|
+
this.errorsFor(element).hide();
|
223
|
+
}
|
224
|
+
},
|
225
|
+
onfocusout: function(element) {
|
226
|
+
if ( !this.checkable(element) && (element.name in this.submitted || !this.optional(element)) ) {
|
227
|
+
this.element(element);
|
228
|
+
}
|
229
|
+
},
|
230
|
+
onkeyup: function(element) {
|
231
|
+
if ( element.name in this.submitted || element == this.lastElement ) {
|
232
|
+
this.element(element);
|
233
|
+
}
|
234
|
+
},
|
235
|
+
onclick: function(element) {
|
236
|
+
// click on selects, radiobuttons and checkboxes
|
237
|
+
if ( element.name in this.submitted )
|
238
|
+
this.element(element);
|
239
|
+
// or option elements, check parent select in that case
|
240
|
+
else if (element.parentNode.name in this.submitted)
|
241
|
+
this.element(element.parentNode);
|
242
|
+
},
|
243
|
+
highlight: function( element, errorClass, validClass ) {
|
244
|
+
$(element).addClass(errorClass).removeClass(validClass);
|
245
|
+
},
|
246
|
+
unhighlight: function( element, errorClass, validClass ) {
|
247
|
+
$(element).removeClass(errorClass).addClass(validClass);
|
248
|
+
}
|
249
|
+
},
|
250
|
+
|
251
|
+
// http://docs.jquery.com/Plugins/Validation/Validator/setDefaults
|
252
|
+
setDefaults: function(settings) {
|
253
|
+
$.extend( $.validator.defaults, settings );
|
254
|
+
},
|
255
|
+
|
256
|
+
messages: {
|
257
|
+
required: "This field is required.",
|
258
|
+
remote: "Please fix this field.",
|
259
|
+
email: "Please enter a valid email address.",
|
260
|
+
url: "Please enter a valid URL.",
|
261
|
+
date: "Please enter a valid date.",
|
262
|
+
dateISO: "Please enter a valid date (ISO).",
|
263
|
+
number: "Please enter a valid number.",
|
264
|
+
digits: "Please enter only digits.",
|
265
|
+
creditcard: "Please enter a valid credit card number.",
|
266
|
+
equalTo: "Please enter the same value again.",
|
267
|
+
accept: "Please enter a value with a valid extension.",
|
268
|
+
maxlength: $.validator.format("Please enter no more than {0} characters."),
|
269
|
+
minlength: $.validator.format("Please enter at least {0} characters."),
|
270
|
+
rangelength: $.validator.format("Please enter a value between {0} and {1} characters long."),
|
271
|
+
range: $.validator.format("Please enter a value between {0} and {1}."),
|
272
|
+
max: $.validator.format("Please enter a value less than or equal to {0}."),
|
273
|
+
min: $.validator.format("Please enter a value greater than or equal to {0}.")
|
274
|
+
},
|
275
|
+
|
276
|
+
autoCreateRanges: false,
|
277
|
+
|
278
|
+
prototype: {
|
279
|
+
|
280
|
+
init: function() {
|
281
|
+
this.labelContainer = $(this.settings.errorLabelContainer);
|
282
|
+
this.errorContext = this.labelContainer.length && this.labelContainer || $(this.currentForm);
|
283
|
+
this.containers = $(this.settings.errorContainer).add( this.settings.errorLabelContainer );
|
284
|
+
this.submitted = {};
|
285
|
+
this.valueCache = {};
|
286
|
+
this.pendingRequest = 0;
|
287
|
+
this.pending = {};
|
288
|
+
this.invalid = {};
|
289
|
+
this.reset();
|
290
|
+
|
291
|
+
var groups = (this.groups = {});
|
292
|
+
$.each(this.settings.groups, function(key, value) {
|
293
|
+
$.each(value.split(/\s/), function(index, name) {
|
294
|
+
groups[name] = key;
|
295
|
+
});
|
296
|
+
});
|
297
|
+
var rules = this.settings.rules;
|
298
|
+
$.each(rules, function(key, value) {
|
299
|
+
rules[key] = $.validator.normalizeRule(value);
|
300
|
+
});
|
301
|
+
|
302
|
+
function delegate(event) {
|
303
|
+
var validator = $.data(this[0].form, "validator"),
|
304
|
+
eventType = "on" + event.type.replace(/^validate/, "");
|
305
|
+
validator.settings[eventType] && validator.settings[eventType].call(validator, this[0] );
|
306
|
+
}
|
307
|
+
$(this.currentForm)
|
308
|
+
.validateDelegate(":text, :password, :file, select, textarea", "focusin focusout keyup", delegate)
|
309
|
+
.validateDelegate(":radio, :checkbox, select, option", "click", delegate);
|
310
|
+
|
311
|
+
if (this.settings.invalidHandler)
|
312
|
+
$(this.currentForm).bind("invalid-form.validate", this.settings.invalidHandler);
|
313
|
+
},
|
314
|
+
|
315
|
+
// http://docs.jquery.com/Plugins/Validation/Validator/form
|
316
|
+
form: function() {
|
317
|
+
this.checkForm();
|
318
|
+
$.extend(this.submitted, this.errorMap);
|
319
|
+
this.invalid = $.extend({}, this.errorMap);
|
320
|
+
if (!this.valid())
|
321
|
+
$(this.currentForm).triggerHandler("invalid-form", [this]);
|
322
|
+
this.showErrors();
|
323
|
+
return this.valid();
|
324
|
+
},
|
325
|
+
|
326
|
+
checkForm: function() {
|
327
|
+
this.prepareForm();
|
328
|
+
for ( var i = 0, elements = (this.currentElements = this.elements()); elements[i]; i++ ) {
|
329
|
+
this.check( elements[i] );
|
330
|
+
}
|
331
|
+
return this.valid();
|
332
|
+
},
|
333
|
+
|
334
|
+
// http://docs.jquery.com/Plugins/Validation/Validator/element
|
335
|
+
element: function( element ) {
|
336
|
+
element = this.clean( element );
|
337
|
+
this.lastElement = element;
|
338
|
+
this.prepareElement( element );
|
339
|
+
this.currentElements = $(element);
|
340
|
+
var result = this.check( element );
|
341
|
+
if ( result ) {
|
342
|
+
delete this.invalid[element.name];
|
343
|
+
} else {
|
344
|
+
this.invalid[element.name] = true;
|
345
|
+
}
|
346
|
+
if ( !this.numberOfInvalids() ) {
|
347
|
+
// Hide error containers on last error
|
348
|
+
this.toHide = this.toHide.add( this.containers );
|
349
|
+
}
|
350
|
+
this.showErrors();
|
351
|
+
return result;
|
352
|
+
},
|
353
|
+
|
354
|
+
// http://docs.jquery.com/Plugins/Validation/Validator/showErrors
|
355
|
+
showErrors: function(errors) {
|
356
|
+
if(errors) {
|
357
|
+
// add items to error list and map
|
358
|
+
$.extend( this.errorMap, errors );
|
359
|
+
this.errorList = [];
|
360
|
+
for ( var name in errors ) {
|
361
|
+
this.errorList.push({
|
362
|
+
message: errors[name],
|
363
|
+
element: this.findByName(name)[0]
|
364
|
+
});
|
365
|
+
}
|
366
|
+
// remove items from success list
|
367
|
+
this.successList = $.grep( this.successList, function(element) {
|
368
|
+
return !(element.name in errors);
|
369
|
+
});
|
370
|
+
}
|
371
|
+
this.settings.showErrors
|
372
|
+
? this.settings.showErrors.call( this, this.errorMap, this.errorList )
|
373
|
+
: this.defaultShowErrors();
|
374
|
+
},
|
375
|
+
|
376
|
+
// http://docs.jquery.com/Plugins/Validation/Validator/resetForm
|
377
|
+
resetForm: function() {
|
378
|
+
if ( $.fn.resetForm )
|
379
|
+
$( this.currentForm ).resetForm();
|
380
|
+
this.submitted = {};
|
381
|
+
this.prepareForm();
|
382
|
+
this.hideErrors();
|
383
|
+
this.elements().removeClass( this.settings.errorClass );
|
384
|
+
},
|
385
|
+
|
386
|
+
numberOfInvalids: function() {
|
387
|
+
return this.objectLength(this.invalid);
|
388
|
+
},
|
389
|
+
|
390
|
+
objectLength: function( obj ) {
|
391
|
+
var count = 0;
|
392
|
+
for ( var i in obj )
|
393
|
+
count++;
|
394
|
+
return count;
|
395
|
+
},
|
396
|
+
|
397
|
+
hideErrors: function() {
|
398
|
+
this.addWrapper( this.toHide ).hide();
|
399
|
+
},
|
400
|
+
|
401
|
+
valid: function() {
|
402
|
+
return this.size() == 0;
|
403
|
+
},
|
404
|
+
|
405
|
+
size: function() {
|
406
|
+
return this.errorList.length;
|
407
|
+
},
|
408
|
+
|
409
|
+
focusInvalid: function() {
|
410
|
+
if( this.settings.focusInvalid ) {
|
411
|
+
try {
|
412
|
+
$(this.findLastActive() || this.errorList.length && this.errorList[0].element || [])
|
413
|
+
.filter(":visible")
|
414
|
+
.focus()
|
415
|
+
// manually trigger focusin event; without it, focusin handler isn't called, findLastActive won't have anything to find
|
416
|
+
.trigger("focusin");
|
417
|
+
} catch(e) {
|
418
|
+
// ignore IE throwing errors when focusing hidden elements
|
419
|
+
}
|
420
|
+
}
|
421
|
+
},
|
422
|
+
|
423
|
+
findLastActive: function() {
|
424
|
+
var lastActive = this.lastActive;
|
425
|
+
return lastActive && $.grep(this.errorList, function(n) {
|
426
|
+
return n.element.name == lastActive.name;
|
427
|
+
}).length == 1 && lastActive;
|
428
|
+
},
|
429
|
+
|
430
|
+
elements: function() {
|
431
|
+
var validator = this,
|
432
|
+
rulesCache = {};
|
433
|
+
|
434
|
+
// select all valid inputs inside the form (no submit or reset buttons)
|
435
|
+
// workaround $Query([]).add until http://dev.jquery.com/ticket/2114 is solved
|
436
|
+
return $([]).add(this.currentForm.elements)
|
437
|
+
.filter(":input")
|
438
|
+
.not(":submit, :reset, :image, [disabled]")
|
439
|
+
.not( this.settings.ignore )
|
440
|
+
.filter(function() {
|
441
|
+
!this.name && validator.settings.debug && window.console && console.error( "%o has no name assigned", this);
|
442
|
+
|
443
|
+
// select only the first element for each name, and only those with rules specified
|
444
|
+
if ( this.name in rulesCache || !validator.objectLength($(this).rules()) )
|
445
|
+
return false;
|
446
|
+
|
447
|
+
rulesCache[this.name] = true;
|
448
|
+
return true;
|
449
|
+
});
|
450
|
+
},
|
451
|
+
|
452
|
+
clean: function( selector ) {
|
453
|
+
return $( selector )[0];
|
454
|
+
},
|
455
|
+
|
456
|
+
errors: function() {
|
457
|
+
return $( this.settings.errorElement + "." + this.settings.errorClass, this.errorContext );
|
458
|
+
},
|
459
|
+
|
460
|
+
reset: function() {
|
461
|
+
this.successList = [];
|
462
|
+
this.errorList = [];
|
463
|
+
this.errorMap = {};
|
464
|
+
this.toShow = $([]);
|
465
|
+
this.toHide = $([]);
|
466
|
+
this.currentElements = $([]);
|
467
|
+
},
|
468
|
+
|
469
|
+
prepareForm: function() {
|
470
|
+
this.reset();
|
471
|
+
this.toHide = this.errors().add( this.containers );
|
472
|
+
},
|
473
|
+
|
474
|
+
prepareElement: function( element ) {
|
475
|
+
this.reset();
|
476
|
+
this.toHide = this.errorsFor(element);
|
477
|
+
},
|
478
|
+
|
479
|
+
check: function( element ) {
|
480
|
+
element = this.clean( element );
|
481
|
+
|
482
|
+
// if radio/checkbox, validate first element in group instead
|
483
|
+
if (this.checkable(element)) {
|
484
|
+
element = this.findByName( element.name ).not(this.settings.ignore)[0];
|
485
|
+
}
|
486
|
+
|
487
|
+
var rules = $(element).rules();
|
488
|
+
var dependencyMismatch = false;
|
489
|
+
for( method in rules ) {
|
490
|
+
var rule = { method: method, parameters: rules[method] };
|
491
|
+
try {
|
492
|
+
var result = $.validator.methods[method].call( this, element.value.replace(/\r/g, ""), element, rule.parameters );
|
493
|
+
|
494
|
+
// if a method indicates that the field is optional and therefore valid,
|
495
|
+
// don't mark it as valid when there are no other rules
|
496
|
+
if ( result == "dependency-mismatch" ) {
|
497
|
+
dependencyMismatch = true;
|
498
|
+
continue;
|
499
|
+
}
|
500
|
+
dependencyMismatch = false;
|
501
|
+
|
502
|
+
if ( result == "pending" ) {
|
503
|
+
this.toHide = this.toHide.not( this.errorsFor(element) );
|
504
|
+
return;
|
505
|
+
}
|
506
|
+
|
507
|
+
if( !result ) {
|
508
|
+
this.formatAndAdd( element, rule );
|
509
|
+
return false;
|
510
|
+
}
|
511
|
+
} catch(e) {
|
512
|
+
this.settings.debug && window.console && console.log("exception occured when checking element " + element.id
|
513
|
+
+ ", check the '" + rule.method + "' method", e);
|
514
|
+
throw e;
|
515
|
+
}
|
516
|
+
}
|
517
|
+
if (dependencyMismatch)
|
518
|
+
return;
|
519
|
+
if ( this.objectLength(rules) )
|
520
|
+
this.successList.push(element);
|
521
|
+
return true;
|
522
|
+
},
|
523
|
+
|
524
|
+
// return the custom message for the given element and validation method
|
525
|
+
// specified in the element's "messages" metadata
|
526
|
+
customMetaMessage: function(element, method) {
|
527
|
+
if (!$.metadata)
|
528
|
+
return;
|
529
|
+
|
530
|
+
var meta = this.settings.meta
|
531
|
+
? $(element).metadata()[this.settings.meta]
|
532
|
+
: $(element).metadata();
|
533
|
+
|
534
|
+
return meta && meta.messages && meta.messages[method];
|
535
|
+
},
|
536
|
+
|
537
|
+
// return the custom message for the given element name and validation method
|
538
|
+
customMessage: function( name, method ) {
|
539
|
+
var m = this.settings.messages[name];
|
540
|
+
return m && (m.constructor == String
|
541
|
+
? m
|
542
|
+
: m[method]);
|
543
|
+
},
|
544
|
+
|
545
|
+
// return the first defined argument, allowing empty strings
|
546
|
+
findDefined: function() {
|
547
|
+
for(var i = 0; i < arguments.length; i++) {
|
548
|
+
if (arguments[i] !== undefined)
|
549
|
+
return arguments[i];
|
550
|
+
}
|
551
|
+
return undefined;
|
552
|
+
},
|
553
|
+
|
554
|
+
defaultMessage: function( element, method) {
|
555
|
+
return this.findDefined(
|
556
|
+
this.customMessage( element.name, method ),
|
557
|
+
this.customMetaMessage( element, method ),
|
558
|
+
// title is never undefined, so handle empty string as undefined
|
559
|
+
!this.settings.ignoreTitle && element.title || undefined,
|
560
|
+
$.validator.messages[method],
|
561
|
+
"<strong>Warning: No message defined for " + element.name + "</strong>"
|
562
|
+
);
|
563
|
+
},
|
564
|
+
|
565
|
+
formatAndAdd: function( element, rule ) {
|
566
|
+
var message = this.defaultMessage( element, rule.method ),
|
567
|
+
theregex = /\$?\{(\d+)\}/g;
|
568
|
+
if ( typeof message == "function" ) {
|
569
|
+
message = message.call(this, rule.parameters, element);
|
570
|
+
} else if (theregex.test(message)) {
|
571
|
+
message = jQuery.format(message.replace(theregex, '{$1}'), rule.parameters);
|
572
|
+
}
|
573
|
+
this.errorList.push({
|
574
|
+
message: message,
|
575
|
+
element: element
|
576
|
+
});
|
577
|
+
|
578
|
+
this.errorMap[element.name] = message;
|
579
|
+
this.submitted[element.name] = message;
|
580
|
+
},
|
581
|
+
|
582
|
+
addWrapper: function(toToggle) {
|
583
|
+
if ( this.settings.wrapper )
|
584
|
+
toToggle = toToggle.add( toToggle.parent( this.settings.wrapper ) );
|
585
|
+
return toToggle;
|
586
|
+
},
|
587
|
+
|
588
|
+
defaultShowErrors: function() {
|
589
|
+
for ( var i = 0; this.errorList[i]; i++ ) {
|
590
|
+
var error = this.errorList[i];
|
591
|
+
this.settings.highlight && this.settings.highlight.call( this, error.element, this.settings.errorClass, this.settings.validClass );
|
592
|
+
this.showLabel( error.element, error.message );
|
593
|
+
}
|
594
|
+
if( this.errorList.length ) {
|
595
|
+
this.toShow = this.toShow.add( this.containers );
|
596
|
+
}
|
597
|
+
if (this.settings.success) {
|
598
|
+
for ( var i = 0; this.successList[i]; i++ ) {
|
599
|
+
this.showLabel( this.successList[i] );
|
600
|
+
}
|
601
|
+
}
|
602
|
+
if (this.settings.unhighlight) {
|
603
|
+
for ( var i = 0, elements = this.validElements(); elements[i]; i++ ) {
|
604
|
+
this.settings.unhighlight.call( this, elements[i], this.settings.errorClass, this.settings.validClass );
|
605
|
+
}
|
606
|
+
}
|
607
|
+
this.toHide = this.toHide.not( this.toShow );
|
608
|
+
this.hideErrors();
|
609
|
+
this.addWrapper( this.toShow ).show();
|
610
|
+
},
|
611
|
+
|
612
|
+
validElements: function() {
|
613
|
+
return this.currentElements.not(this.invalidElements());
|
614
|
+
},
|
615
|
+
|
616
|
+
invalidElements: function() {
|
617
|
+
return $(this.errorList).map(function() {
|
618
|
+
return this.element;
|
619
|
+
});
|
620
|
+
},
|
621
|
+
|
622
|
+
showLabel: function(element, message) {
|
623
|
+
var label = this.errorsFor( element );
|
624
|
+
if ( label.length ) {
|
625
|
+
// refresh error/success class
|
626
|
+
label.removeClass().addClass( this.settings.errorClass );
|
627
|
+
|
628
|
+
// check if we have a generated label, replace the message then
|
629
|
+
label.attr("generated") && label.html(message);
|
630
|
+
} else {
|
631
|
+
// create label
|
632
|
+
label = $("<" + this.settings.errorElement + "/>")
|
633
|
+
.attr({"for": this.idOrName(element), generated: true})
|
634
|
+
.addClass(this.settings.errorClass)
|
635
|
+
.html(message || "");
|
636
|
+
if ( this.settings.wrapper ) {
|
637
|
+
// make sure the element is visible, even in IE
|
638
|
+
// actually showing the wrapped element is handled elsewhere
|
639
|
+
label = label.hide().show().wrap("<" + this.settings.wrapper + "/>").parent();
|
640
|
+
}
|
641
|
+
if ( !this.labelContainer.append(label).length )
|
642
|
+
this.settings.errorPlacement
|
643
|
+
? this.settings.errorPlacement(label, $(element) )
|
644
|
+
: label.insertAfter(element);
|
645
|
+
}
|
646
|
+
if ( !message && this.settings.success ) {
|
647
|
+
label.text("");
|
648
|
+
typeof this.settings.success == "string"
|
649
|
+
? label.addClass( this.settings.success )
|
650
|
+
: this.settings.success( label );
|
651
|
+
}
|
652
|
+
this.toShow = this.toShow.add(label);
|
653
|
+
},
|
654
|
+
|
655
|
+
errorsFor: function(element) {
|
656
|
+
var name = this.idOrName(element);
|
657
|
+
return this.errors().filter(function() {
|
658
|
+
return $(this).attr('for') == name;
|
659
|
+
});
|
660
|
+
},
|
661
|
+
|
662
|
+
idOrName: function(element) {
|
663
|
+
return this.groups[element.name] || (this.checkable(element) ? element.name : element.id || element.name);
|
664
|
+
},
|
665
|
+
|
666
|
+
checkable: function( element ) {
|
667
|
+
return /radio|checkbox/i.test(element.type);
|
668
|
+
},
|
669
|
+
|
670
|
+
findByName: function( name ) {
|
671
|
+
// select by name and filter by form for performance over form.find("[name=...]")
|
672
|
+
var form = this.currentForm;
|
673
|
+
return $(document.getElementsByName(name)).map(function(index, element) {
|
674
|
+
return element.form == form && element.name == name && element || null;
|
675
|
+
});
|
676
|
+
},
|
677
|
+
|
678
|
+
getLength: function(value, element) {
|
679
|
+
switch( element.nodeName.toLowerCase() ) {
|
680
|
+
case 'select':
|
681
|
+
return $("option:selected", element).length;
|
682
|
+
case 'input':
|
683
|
+
if( this.checkable( element) )
|
684
|
+
return this.findByName(element.name).filter(':checked').length;
|
685
|
+
}
|
686
|
+
return value.length;
|
687
|
+
},
|
688
|
+
|
689
|
+
depend: function(param, element) {
|
690
|
+
return this.dependTypes[typeof param]
|
691
|
+
? this.dependTypes[typeof param](param, element)
|
692
|
+
: true;
|
693
|
+
},
|
694
|
+
|
695
|
+
dependTypes: {
|
696
|
+
"boolean": function(param, element) {
|
697
|
+
return param;
|
698
|
+
},
|
699
|
+
"string": function(param, element) {
|
700
|
+
return !!$(param, element.form).length;
|
701
|
+
},
|
702
|
+
"function": function(param, element) {
|
703
|
+
return param(element);
|
704
|
+
}
|
705
|
+
},
|
706
|
+
|
707
|
+
optional: function(element) {
|
708
|
+
return !$.validator.methods.required.call(this, $.trim(element.value), element) && "dependency-mismatch";
|
709
|
+
},
|
710
|
+
|
711
|
+
startRequest: function(element) {
|
712
|
+
if (!this.pending[element.name]) {
|
713
|
+
this.pendingRequest++;
|
714
|
+
this.pending[element.name] = true;
|
715
|
+
}
|
716
|
+
},
|
717
|
+
|
718
|
+
stopRequest: function(element, valid) {
|
719
|
+
this.pendingRequest--;
|
720
|
+
// sometimes synchronization fails, make sure pendingRequest is never < 0
|
721
|
+
if (this.pendingRequest < 0)
|
722
|
+
this.pendingRequest = 0;
|
723
|
+
delete this.pending[element.name];
|
724
|
+
if ( valid && this.pendingRequest == 0 && this.formSubmitted && this.form() ) {
|
725
|
+
$(this.currentForm).submit();
|
726
|
+
this.formSubmitted = false;
|
727
|
+
} else if (!valid && this.pendingRequest == 0 && this.formSubmitted) {
|
728
|
+
$(this.currentForm).triggerHandler("invalid-form", [this]);
|
729
|
+
this.formSubmitted = false;
|
730
|
+
}
|
731
|
+
},
|
732
|
+
|
733
|
+
previousValue: function(element) {
|
734
|
+
return $.data(element, "previousValue") || $.data(element, "previousValue", {
|
735
|
+
old: null,
|
736
|
+
valid: true,
|
737
|
+
message: this.defaultMessage( element, "remote" )
|
738
|
+
});
|
739
|
+
}
|
740
|
+
|
741
|
+
},
|
742
|
+
|
743
|
+
classRuleSettings: {
|
744
|
+
required: {required: true},
|
745
|
+
email: {email: true},
|
746
|
+
url: {url: true},
|
747
|
+
date: {date: true},
|
748
|
+
dateISO: {dateISO: true},
|
749
|
+
dateDE: {dateDE: true},
|
750
|
+
number: {number: true},
|
751
|
+
numberDE: {numberDE: true},
|
752
|
+
digits: {digits: true},
|
753
|
+
creditcard: {creditcard: true}
|
754
|
+
},
|
755
|
+
|
756
|
+
addClassRules: function(className, rules) {
|
757
|
+
className.constructor == String ?
|
758
|
+
this.classRuleSettings[className] = rules :
|
759
|
+
$.extend(this.classRuleSettings, className);
|
760
|
+
},
|
761
|
+
|
762
|
+
classRules: function(element) {
|
763
|
+
var rules = {};
|
764
|
+
var classes = $(element).attr('class');
|
765
|
+
classes && $.each(classes.split(' '), function() {
|
766
|
+
if (this in $.validator.classRuleSettings) {
|
767
|
+
$.extend(rules, $.validator.classRuleSettings[this]);
|
768
|
+
}
|
769
|
+
});
|
770
|
+
return rules;
|
771
|
+
},
|
772
|
+
|
773
|
+
attributeRules: function(element) {
|
774
|
+
var rules = {};
|
775
|
+
var $element = $(element);
|
776
|
+
|
777
|
+
for (method in $.validator.methods) {
|
778
|
+
var value = $element.attr(method);
|
779
|
+
if (value) {
|
780
|
+
rules[method] = value;
|
781
|
+
}
|
782
|
+
}
|
783
|
+
|
784
|
+
// maxlength may be returned as -1, 2147483647 (IE) and 524288 (safari) for text inputs
|
785
|
+
if (rules.maxlength && /-1|2147483647|524288/.test(rules.maxlength)) {
|
786
|
+
delete rules.maxlength;
|
787
|
+
}
|
788
|
+
|
789
|
+
return rules;
|
790
|
+
},
|
791
|
+
|
792
|
+
metadataRules: function(element) {
|
793
|
+
if (!$.metadata) return {};
|
794
|
+
|
795
|
+
var meta = $.data(element.form, 'validator').settings.meta;
|
796
|
+
return meta ?
|
797
|
+
$(element).metadata()[meta] :
|
798
|
+
$(element).metadata();
|
799
|
+
},
|
800
|
+
|
801
|
+
staticRules: function(element) {
|
802
|
+
var rules = {};
|
803
|
+
var validator = $.data(element.form, 'validator');
|
804
|
+
if (validator.settings.rules) {
|
805
|
+
rules = $.validator.normalizeRule(validator.settings.rules[element.name]) || {};
|
806
|
+
}
|
807
|
+
return rules;
|
808
|
+
},
|
809
|
+
|
810
|
+
normalizeRules: function(rules, element) {
|
811
|
+
// handle dependency check
|
812
|
+
$.each(rules, function(prop, val) {
|
813
|
+
// ignore rule when param is explicitly false, eg. required:false
|
814
|
+
if (val === false) {
|
815
|
+
delete rules[prop];
|
816
|
+
return;
|
817
|
+
}
|
818
|
+
if (val.param || val.depends) {
|
819
|
+
var keepRule = true;
|
820
|
+
switch (typeof val.depends) {
|
821
|
+
case "string":
|
822
|
+
keepRule = !!$(val.depends, element.form).length;
|
823
|
+
break;
|
824
|
+
case "function":
|
825
|
+
keepRule = val.depends.call(element, element);
|
826
|
+
break;
|
827
|
+
}
|
828
|
+
if (keepRule) {
|
829
|
+
rules[prop] = val.param !== undefined ? val.param : true;
|
830
|
+
} else {
|
831
|
+
delete rules[prop];
|
832
|
+
}
|
833
|
+
}
|
834
|
+
});
|
835
|
+
|
836
|
+
// evaluate parameters
|
837
|
+
$.each(rules, function(rule, parameter) {
|
838
|
+
rules[rule] = $.isFunction(parameter) ? parameter(element) : parameter;
|
839
|
+
});
|
840
|
+
|
841
|
+
// clean number parameters
|
842
|
+
$.each(['minlength', 'maxlength', 'min', 'max'], function() {
|
843
|
+
if (rules[this]) {
|
844
|
+
rules[this] = Number(rules[this]);
|
845
|
+
}
|
846
|
+
});
|
847
|
+
$.each(['rangelength', 'range'], function() {
|
848
|
+
if (rules[this]) {
|
849
|
+
rules[this] = [Number(rules[this][0]), Number(rules[this][1])];
|
850
|
+
}
|
851
|
+
});
|
852
|
+
|
853
|
+
if ($.validator.autoCreateRanges) {
|
854
|
+
// auto-create ranges
|
855
|
+
if (rules.min && rules.max) {
|
856
|
+
rules.range = [rules.min, rules.max];
|
857
|
+
delete rules.min;
|
858
|
+
delete rules.max;
|
859
|
+
}
|
860
|
+
if (rules.minlength && rules.maxlength) {
|
861
|
+
rules.rangelength = [rules.minlength, rules.maxlength];
|
862
|
+
delete rules.minlength;
|
863
|
+
delete rules.maxlength;
|
864
|
+
}
|
865
|
+
}
|
866
|
+
|
867
|
+
// To support custom messages in metadata ignore rule methods titled "messages"
|
868
|
+
if (rules.messages) {
|
869
|
+
delete rules.messages;
|
870
|
+
}
|
871
|
+
|
872
|
+
return rules;
|
873
|
+
},
|
874
|
+
|
875
|
+
// Converts a simple string to a {string: true} rule, e.g., "required" to {required:true}
|
876
|
+
normalizeRule: function(data) {
|
877
|
+
if( typeof data == "string" ) {
|
878
|
+
var transformed = {};
|
879
|
+
$.each(data.split(/\s/), function() {
|
880
|
+
transformed[this] = true;
|
881
|
+
});
|
882
|
+
data = transformed;
|
883
|
+
}
|
884
|
+
return data;
|
885
|
+
},
|
886
|
+
|
887
|
+
// http://docs.jquery.com/Plugins/Validation/Validator/addMethod
|
888
|
+
addMethod: function(name, method, message) {
|
889
|
+
$.validator.methods[name] = method;
|
890
|
+
$.validator.messages[name] = message != undefined ? message : $.validator.messages[name];
|
891
|
+
if (method.length < 3) {
|
892
|
+
$.validator.addClassRules(name, $.validator.normalizeRule(name));
|
893
|
+
}
|
894
|
+
},
|
895
|
+
|
896
|
+
methods: {
|
897
|
+
|
898
|
+
// http://docs.jquery.com/Plugins/Validation/Methods/required
|
899
|
+
required: function(value, element, param) {
|
900
|
+
// check if dependency is met
|
901
|
+
if ( !this.depend(param, element) )
|
902
|
+
return "dependency-mismatch";
|
903
|
+
switch( element.nodeName.toLowerCase() ) {
|
904
|
+
case 'select':
|
905
|
+
// could be an array for select-multiple or a string, both are fine this way
|
906
|
+
var val = $(element).val();
|
907
|
+
return val && val.length > 0;
|
908
|
+
case 'input':
|
909
|
+
if ( this.checkable(element) )
|
910
|
+
return this.getLength(value, element) > 0;
|
911
|
+
default:
|
912
|
+
return $.trim(value).length > 0;
|
913
|
+
}
|
914
|
+
},
|
915
|
+
|
916
|
+
// http://docs.jquery.com/Plugins/Validation/Methods/remote
|
917
|
+
remote: function(value, element, param) {
|
918
|
+
if ( this.optional(element) )
|
919
|
+
return "dependency-mismatch";
|
920
|
+
|
921
|
+
var previous = this.previousValue(element);
|
922
|
+
if (!this.settings.messages[element.name] )
|
923
|
+
this.settings.messages[element.name] = {};
|
924
|
+
previous.originalMessage = this.settings.messages[element.name].remote;
|
925
|
+
this.settings.messages[element.name].remote = previous.message;
|
926
|
+
|
927
|
+
param = typeof param == "string" && {url:param} || param;
|
928
|
+
|
929
|
+
if ( previous.old !== value ) {
|
930
|
+
previous.old = value;
|
931
|
+
var validator = this;
|
932
|
+
this.startRequest(element);
|
933
|
+
var data = {};
|
934
|
+
data[element.name] = value;
|
935
|
+
$.ajax($.extend(true, {
|
936
|
+
url: param,
|
937
|
+
mode: "abort",
|
938
|
+
port: "validate" + element.name,
|
939
|
+
dataType: "json",
|
940
|
+
data: data,
|
941
|
+
success: function(response) {
|
942
|
+
validator.settings.messages[element.name].remote = previous.originalMessage;
|
943
|
+
var valid = response === true;
|
944
|
+
if ( valid ) {
|
945
|
+
var submitted = validator.formSubmitted;
|
946
|
+
validator.prepareElement(element);
|
947
|
+
validator.formSubmitted = submitted;
|
948
|
+
validator.successList.push(element);
|
949
|
+
validator.showErrors();
|
950
|
+
} else {
|
951
|
+
var errors = {};
|
952
|
+
var message = (previous.message = response || validator.defaultMessage( element, "remote" ));
|
953
|
+
errors[element.name] = $.isFunction(message) ? message(value) : message;
|
954
|
+
validator.showErrors(errors);
|
955
|
+
}
|
956
|
+
previous.valid = valid;
|
957
|
+
validator.stopRequest(element, valid);
|
958
|
+
}
|
959
|
+
}, param));
|
960
|
+
return "pending";
|
961
|
+
} else if( this.pending[element.name] ) {
|
962
|
+
return "pending";
|
963
|
+
}
|
964
|
+
return previous.valid;
|
965
|
+
},
|
966
|
+
|
967
|
+
// http://docs.jquery.com/Plugins/Validation/Methods/minlength
|
968
|
+
minlength: function(value, element, param) {
|
969
|
+
return this.optional(element) || this.getLength($.trim(value), element) >= param;
|
970
|
+
},
|
971
|
+
|
972
|
+
// http://docs.jquery.com/Plugins/Validation/Methods/maxlength
|
973
|
+
maxlength: function(value, element, param) {
|
974
|
+
return this.optional(element) || this.getLength($.trim(value), element) <= param;
|
975
|
+
},
|
976
|
+
|
977
|
+
// http://docs.jquery.com/Plugins/Validation/Methods/rangelength
|
978
|
+
rangelength: function(value, element, param) {
|
979
|
+
var length = this.getLength($.trim(value), element);
|
980
|
+
return this.optional(element) || ( length >= param[0] && length <= param[1] );
|
981
|
+
},
|
982
|
+
|
983
|
+
// http://docs.jquery.com/Plugins/Validation/Methods/min
|
984
|
+
min: function( value, element, param ) {
|
985
|
+
return this.optional(element) || value >= param;
|
986
|
+
},
|
987
|
+
|
988
|
+
// http://docs.jquery.com/Plugins/Validation/Methods/max
|
989
|
+
max: function( value, element, param ) {
|
990
|
+
return this.optional(element) || value <= param;
|
991
|
+
},
|
992
|
+
|
993
|
+
// http://docs.jquery.com/Plugins/Validation/Methods/range
|
994
|
+
range: function( value, element, param ) {
|
995
|
+
return this.optional(element) || ( value >= param[0] && value <= param[1] );
|
996
|
+
},
|
997
|
+
|
998
|
+
// http://docs.jquery.com/Plugins/Validation/Methods/email
|
999
|
+
email: function(value, element) {
|
1000
|
+
// contributed by Scott Gonzalez: http://projects.scottsplayground.com/email_address_validation/
|
1001
|
+
return this.optional(element) || /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i.test(value);
|
1002
|
+
},
|
1003
|
+
|
1004
|
+
// http://docs.jquery.com/Plugins/Validation/Methods/url
|
1005
|
+
url: function(value, element) {
|
1006
|
+
// contributed by Scott Gonzalez: http://projects.scottsplayground.com/iri/
|
1007
|
+
return this.optional(element) || /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(value);
|
1008
|
+
},
|
1009
|
+
|
1010
|
+
// http://docs.jquery.com/Plugins/Validation/Methods/date
|
1011
|
+
date: function(value, element) {
|
1012
|
+
return this.optional(element) || !/Invalid|NaN/.test(new Date(value));
|
1013
|
+
},
|
1014
|
+
|
1015
|
+
// http://docs.jquery.com/Plugins/Validation/Methods/dateISO
|
1016
|
+
dateISO: function(value, element) {
|
1017
|
+
return this.optional(element) || /^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(value);
|
1018
|
+
},
|
1019
|
+
|
1020
|
+
// http://docs.jquery.com/Plugins/Validation/Methods/number
|
1021
|
+
number: function(value, element) {
|
1022
|
+
return this.optional(element) || /^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d+)?$/.test(value);
|
1023
|
+
},
|
1024
|
+
|
1025
|
+
// http://docs.jquery.com/Plugins/Validation/Methods/digits
|
1026
|
+
digits: function(value, element) {
|
1027
|
+
return this.optional(element) || /^\d+$/.test(value);
|
1028
|
+
},
|
1029
|
+
|
1030
|
+
// http://docs.jquery.com/Plugins/Validation/Methods/creditcard
|
1031
|
+
// based on http://en.wikipedia.org/wiki/Luhn
|
1032
|
+
creditcard: function(value, element) {
|
1033
|
+
if ( this.optional(element) )
|
1034
|
+
return "dependency-mismatch";
|
1035
|
+
// accept only digits and dashes
|
1036
|
+
if (/[^0-9-]+/.test(value))
|
1037
|
+
return false;
|
1038
|
+
var nCheck = 0,
|
1039
|
+
nDigit = 0,
|
1040
|
+
bEven = false;
|
1041
|
+
|
1042
|
+
value = value.replace(/\D/g, "");
|
1043
|
+
|
1044
|
+
for (var n = value.length - 1; n >= 0; n--) {
|
1045
|
+
var cDigit = value.charAt(n);
|
1046
|
+
var nDigit = parseInt(cDigit, 10);
|
1047
|
+
if (bEven) {
|
1048
|
+
if ((nDigit *= 2) > 9)
|
1049
|
+
nDigit -= 9;
|
1050
|
+
}
|
1051
|
+
nCheck += nDigit;
|
1052
|
+
bEven = !bEven;
|
1053
|
+
}
|
1054
|
+
|
1055
|
+
return (nCheck % 10) == 0;
|
1056
|
+
},
|
1057
|
+
|
1058
|
+
// http://docs.jquery.com/Plugins/Validation/Methods/accept
|
1059
|
+
accept: function(value, element, param) {
|
1060
|
+
param = typeof param == "string" ? param.replace(/,/g, '|') : "png|jpe?g|gif";
|
1061
|
+
return this.optional(element) || value.match(new RegExp(".(" + param + ")$", "i"));
|
1062
|
+
},
|
1063
|
+
|
1064
|
+
// http://docs.jquery.com/Plugins/Validation/Methods/equalTo
|
1065
|
+
equalTo: function(value, element, param) {
|
1066
|
+
// bind to the blur event of the target in order to revalidate whenever the target field is updated
|
1067
|
+
// TODO find a way to bind the event just once, avoiding the unbind-rebind overhead
|
1068
|
+
var target = $(param).unbind(".validate-equalTo").bind("blur.validate-equalTo", function() {
|
1069
|
+
$(element).valid();
|
1070
|
+
});
|
1071
|
+
return value == target.val();
|
1072
|
+
}
|
1073
|
+
|
1074
|
+
}
|
1075
|
+
|
1076
|
+
});
|
1077
|
+
|
1078
|
+
// deprecated, use $.validator.format instead
|
1079
|
+
$.format = $.validator.format;
|
1080
|
+
|
1081
|
+
})(jQuery);
|
1082
|
+
|
1083
|
+
// ajax mode: abort
|
1084
|
+
// usage: $.ajax({ mode: "abort"[, port: "uniqueport"]});
|
1085
|
+
// if mode:"abort" is used, the previous request on that port (port can be undefined) is aborted via XMLHttpRequest.abort()
|
1086
|
+
;(function($) {
|
1087
|
+
var ajax = $.ajax;
|
1088
|
+
var pendingRequests = {};
|
1089
|
+
$.ajax = function(settings) {
|
1090
|
+
// create settings for compatibility with ajaxSetup
|
1091
|
+
settings = $.extend(settings, $.extend({}, $.ajaxSettings, settings));
|
1092
|
+
var port = settings.port;
|
1093
|
+
if (settings.mode == "abort") {
|
1094
|
+
if ( pendingRequests[port] ) {
|
1095
|
+
pendingRequests[port].abort();
|
1096
|
+
}
|
1097
|
+
return (pendingRequests[port] = ajax.apply(this, arguments));
|
1098
|
+
}
|
1099
|
+
return ajax.apply(this, arguments);
|
1100
|
+
};
|
1101
|
+
})(jQuery);
|
1102
|
+
|
1103
|
+
// provides cross-browser focusin and focusout events
|
1104
|
+
// IE has native support, in other browsers, use event caputuring (neither bubbles)
|
1105
|
+
|
1106
|
+
// provides delegate(type: String, delegate: Selector, handler: Callback) plugin for easier event delegation
|
1107
|
+
// handler is only called when $(event.target).is(delegate), in the scope of the jquery-object for event.target
|
1108
|
+
;(function($) {
|
1109
|
+
// only implement if not provided by jQuery core (since 1.4)
|
1110
|
+
// TODO verify if jQuery 1.4's implementation is compatible with older jQuery special-event APIs
|
1111
|
+
if (!jQuery.event.special.focusin && !jQuery.event.special.focusout && document.addEventListener) {
|
1112
|
+
$.each({
|
1113
|
+
focus: 'focusin',
|
1114
|
+
blur: 'focusout'
|
1115
|
+
}, function( original, fix ){
|
1116
|
+
$.event.special[fix] = {
|
1117
|
+
setup:function() {
|
1118
|
+
this.addEventListener( original, handler, true );
|
1119
|
+
},
|
1120
|
+
teardown:function() {
|
1121
|
+
this.removeEventListener( original, handler, true );
|
1122
|
+
},
|
1123
|
+
handler: function(e) {
|
1124
|
+
arguments[0] = $.event.fix(e);
|
1125
|
+
arguments[0].type = fix;
|
1126
|
+
return $.event.handle.apply(this, arguments);
|
1127
|
+
}
|
1128
|
+
};
|
1129
|
+
function handler(e) {
|
1130
|
+
e = $.event.fix(e);
|
1131
|
+
e.type = fix;
|
1132
|
+
return $.event.handle.call(this, e);
|
1133
|
+
}
|
1134
|
+
});
|
1135
|
+
};
|
1136
|
+
$.extend($.fn, {
|
1137
|
+
validateDelegate: function(delegate, type, handler) {
|
1138
|
+
return this.bind(type, function(event) {
|
1139
|
+
var target = $(event.target);
|
1140
|
+
if (target.is(delegate)) {
|
1141
|
+
return handler.apply(target, arguments);
|
1142
|
+
}
|
1143
|
+
});
|
1144
|
+
}
|
1145
|
+
});
|
1146
|
+
})(jQuery);
|