guardsjs-rails 0.7.2
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/lib/guardsjs-rails.rb +8 -0
- data/lib/guardsjs-rails/version.rb +5 -0
- data/vendor/assets/javascripts/guards.js +1342 -0
- metadata +71 -0
@@ -0,0 +1,1342 @@
|
|
1
|
+
/*!
|
2
|
+
* Guards JavaScript jQuery Plugin v0.7.2
|
3
|
+
* https://github.com/on-site/guards.js
|
4
|
+
*
|
5
|
+
* Copyright 2010-2013, On-Site.com, http://www.on-site.com/
|
6
|
+
* Licensed under the MIT license.
|
7
|
+
*
|
8
|
+
* Includes code for email and phone number validation from the jQuery
|
9
|
+
* Validation plugin. http://docs.jquery.com/Plugins/Validation
|
10
|
+
*
|
11
|
+
* Date: Mon Feb 25 03:47:45 2013 -0800
|
12
|
+
*/
|
13
|
+
|
14
|
+
/**
|
15
|
+
* This plugin is initially inspired by the standard Validation jQuery
|
16
|
+
* plugin (http://docs.jquery.com/Plugins/Validation).
|
17
|
+
*
|
18
|
+
* To guard forms with this plugin, you must specify a set of guards
|
19
|
+
* via $.guards.add(selector).using(guard) or
|
20
|
+
* $.guard(selector).using(guard). These guards are then invoked from
|
21
|
+
* the first one specified to the last one specified.
|
22
|
+
*
|
23
|
+
* Example usage:
|
24
|
+
*
|
25
|
+
* $(function() {
|
26
|
+
* // Change the default error tag wrapper to a div.
|
27
|
+
* $.guards.defaults.tag = "div";
|
28
|
+
*
|
29
|
+
* // Enable the submit guard hook for the form with the "myForm" id.
|
30
|
+
* $("#myForm").enableGuards();
|
31
|
+
*
|
32
|
+
* // Guard that fields with "required" class have a value.
|
33
|
+
* $.guard(".required").using("required");
|
34
|
+
*
|
35
|
+
* // Guard that the text fields don't have the value "invalid" or "bad".
|
36
|
+
* $.guard(":text").using(function(value, element) {
|
37
|
+
* return $.inArray(value, ["invalid", "bad"]) == -1;
|
38
|
+
* }).message("Don't use the keyword 'invalid' or 'bad'.");
|
39
|
+
*
|
40
|
+
* // Guard that fields with "email" class specify at least one
|
41
|
+
* // value, but only show 1 error message if none is specified (but
|
42
|
+
* // still highlight all of the fields).
|
43
|
+
* $.guard(".email").using("oneRequired")
|
44
|
+
* .message("Please specify at least one email.").grouped();
|
45
|
+
*/
|
46
|
+
(function($) {
|
47
|
+
$.guard = function(selector) {
|
48
|
+
return $.guards.add(selector);
|
49
|
+
};
|
50
|
+
|
51
|
+
$.guard.version = "0.7.2";
|
52
|
+
|
53
|
+
$.Guards = function() {
|
54
|
+
var self = this;
|
55
|
+
this._guards = [];
|
56
|
+
|
57
|
+
this.options = {
|
58
|
+
stackErrors: false
|
59
|
+
};
|
60
|
+
|
61
|
+
this.constants = {
|
62
|
+
notChecked: ""
|
63
|
+
};
|
64
|
+
|
65
|
+
var defineGuard = function(aggregator, validator) {
|
66
|
+
return function() {
|
67
|
+
var args = $.makeArray(arguments);
|
68
|
+
return function(value, element) {
|
69
|
+
return self[aggregator](value, function(v) {
|
70
|
+
return self[validator].apply(self, $.merge([v], args));
|
71
|
+
});
|
72
|
+
};
|
73
|
+
};
|
74
|
+
};
|
75
|
+
|
76
|
+
var minMaxMessage = function(formatting, minMaxFormat) {
|
77
|
+
return function(options) {
|
78
|
+
if (self.isNullOrUndefined(options)) {
|
79
|
+
options = {};
|
80
|
+
}
|
81
|
+
|
82
|
+
if (!$.isFunction(minMaxFormat)) {
|
83
|
+
minMaxFormat = function(x) { return x; };
|
84
|
+
}
|
85
|
+
|
86
|
+
var minDefined = !self.isNullOrUndefined(options.min);
|
87
|
+
var maxDefined = !self.isNullOrUndefined(options.max);
|
88
|
+
|
89
|
+
if (minDefined && maxDefined) {
|
90
|
+
return self.format(formatting.minAndMax, minMaxFormat(options.min), minMaxFormat(options.max));
|
91
|
+
}
|
92
|
+
|
93
|
+
if (minDefined) {
|
94
|
+
return self.format(formatting.min, minMaxFormat(options.min));
|
95
|
+
}
|
96
|
+
|
97
|
+
if (maxDefined) {
|
98
|
+
return self.format(formatting.max, minMaxFormat(options.max));
|
99
|
+
}
|
100
|
+
|
101
|
+
if (formatting.invalid) {
|
102
|
+
return formatting.invalid;
|
103
|
+
}
|
104
|
+
|
105
|
+
return self.defaults.messages.undefined;
|
106
|
+
};
|
107
|
+
};
|
108
|
+
|
109
|
+
var arrayMessage = function(formatting) {
|
110
|
+
return function(array) {
|
111
|
+
return self.format(formatting, $.map(array, function(x, i) { return $.trim("" + x); }).join(", "));
|
112
|
+
};
|
113
|
+
};
|
114
|
+
|
115
|
+
this.defaults = {
|
116
|
+
grouped: false,
|
117
|
+
guard: "required",
|
118
|
+
|
119
|
+
guards: {
|
120
|
+
allow: defineGuard("isAllValid", "isAllowed"),
|
121
|
+
always: defineGuard("isAllValid", "always"),
|
122
|
+
different: defineGuard("passThrough", "isDifferent"),
|
123
|
+
disallow: defineGuard("isAllValid", "isDisallowed"),
|
124
|
+
email: defineGuard("isAllValid", "isValidEmail"),
|
125
|
+
"float": defineGuard("isAllValid", "isValidFloat"),
|
126
|
+
"int": defineGuard("isAllValid", "isValidInt"),
|
127
|
+
moneyUS: defineGuard("isAllValid", "isValidMoneyUS"),
|
128
|
+
never: defineGuard("isAllValid", "never"),
|
129
|
+
oneRequired: defineGuard("isAnyValid", "isPresent"),
|
130
|
+
phoneUS: defineGuard("isAllValid", "isValidPhoneUS"),
|
131
|
+
required: defineGuard("isAllValid", "isPresent"),
|
132
|
+
same: defineGuard("passThrough", "isSame"),
|
133
|
+
string: defineGuard("isAllValid", "isValidString")
|
134
|
+
},
|
135
|
+
|
136
|
+
invalidClass: "invalid-field",
|
137
|
+
messageClass: "error-message",
|
138
|
+
|
139
|
+
messages: {
|
140
|
+
allow: arrayMessage("Please enter one of: #{0}."),
|
141
|
+
always: "There was an error.",
|
142
|
+
different: "These values must all be different.",
|
143
|
+
disallow: arrayMessage("Please don't enter: #{0}."),
|
144
|
+
email: "Please enter a valid E-mail address.",
|
145
|
+
|
146
|
+
"float": minMaxMessage({
|
147
|
+
minAndMax: "Please enter a number from #{0} to #{1}.",
|
148
|
+
min: "Please enter a number no less than #{0}.",
|
149
|
+
max: "Please enter a number no greater than #{0}.",
|
150
|
+
invalid: "Please enter a number."
|
151
|
+
}),
|
152
|
+
|
153
|
+
"int": minMaxMessage({
|
154
|
+
minAndMax: "Please enter a number from #{0} to #{1}.",
|
155
|
+
min: "Please enter a number no less than #{0}.",
|
156
|
+
max: "Please enter a number no greater than #{0}.",
|
157
|
+
invalid: "Please enter a number."
|
158
|
+
}),
|
159
|
+
|
160
|
+
moneyUS: minMaxMessage({
|
161
|
+
minAndMax: "Please enter a dollar amount from #{0} to #{1}.",
|
162
|
+
min: "Please enter a dollar amount no less than #{0}.",
|
163
|
+
max: "Please enter a dollar amount no greater than #{0}.",
|
164
|
+
invalid: "Please enter a dollar amount."
|
165
|
+
}, function(x) { return x.toFixed(2); }),
|
166
|
+
|
167
|
+
never: "There was an error.",
|
168
|
+
oneRequired: "Specify at least one.",
|
169
|
+
phoneUS: "Please enter a valid phone number.",
|
170
|
+
required: "This field is required.",
|
171
|
+
same: "These values must all match.",
|
172
|
+
|
173
|
+
string: minMaxMessage({
|
174
|
+
minAndMax: "Please enter a string with length #{0} to #{1}.",
|
175
|
+
min: "Please enter a string with length at least #{0}.",
|
176
|
+
max: "Please enter a string with length no greater than #{0}."
|
177
|
+
}),
|
178
|
+
|
179
|
+
"undefined": "Please fix this field."
|
180
|
+
},
|
181
|
+
|
182
|
+
style: {
|
183
|
+
field: {
|
184
|
+
"background-color": "#ffff66"
|
185
|
+
},
|
186
|
+
|
187
|
+
message: {
|
188
|
+
color: "#ff0000",
|
189
|
+
"margin-left": "10px"
|
190
|
+
}
|
191
|
+
},
|
192
|
+
|
193
|
+
tag: "span",
|
194
|
+
|
195
|
+
target: function(errorElement) {
|
196
|
+
var last = $(this).filter(":last");
|
197
|
+
|
198
|
+
if (last.is(":radio,:checkbox")) {
|
199
|
+
last = $(last[0].nextSibling);
|
200
|
+
}
|
201
|
+
|
202
|
+
errorElement.insertAfter(last);
|
203
|
+
return false;
|
204
|
+
}
|
205
|
+
};
|
206
|
+
};
|
207
|
+
|
208
|
+
$.Guards.prototype.version = "0.7.2";
|
209
|
+
|
210
|
+
// Really old jQuery doesn't have isArray, so use this alias
|
211
|
+
// instead.
|
212
|
+
$.Guards.prototype.isArray = $.isArray;
|
213
|
+
|
214
|
+
if (!$.Guards.prototype.isArray) {
|
215
|
+
var ARRAY_CONSTRUCTOR = [].constructor;
|
216
|
+
var JQUERY_CONSTRUCTOR = jQuery;
|
217
|
+
|
218
|
+
$.Guards.prototype.isArray = function(obj) {
|
219
|
+
// Simplistic, but good enough for guards.
|
220
|
+
return obj.constructor == ARRAY_CONSTRUCTOR || obj.constructor == JQUERY_CONSTRUCTOR;
|
221
|
+
};
|
222
|
+
}
|
223
|
+
|
224
|
+
// Alias for console.log, but check that such a thing exists.
|
225
|
+
$.Guards.prototype.log = function(message) {
|
226
|
+
if (console && console.log) {
|
227
|
+
console.log(message);
|
228
|
+
}
|
229
|
+
};
|
230
|
+
|
231
|
+
// Utility method to trigger live events, but works against any
|
232
|
+
// jQuery version that supports live events.
|
233
|
+
$.Guards.prototype.on = function(selector, event, callback) {
|
234
|
+
if ($.fn.on) {
|
235
|
+
$(document).on(event, selector, callback);
|
236
|
+
} else if ($.fn.delegate) {
|
237
|
+
$(document).delegate(selector, event, callback);
|
238
|
+
} else if ($.fn.live) {
|
239
|
+
$(selector).live(event, callback);
|
240
|
+
} else {
|
241
|
+
this.log("Could not bind live handlers, probably because jQuery is too old.");
|
242
|
+
}
|
243
|
+
};
|
244
|
+
|
245
|
+
// Implementation of $.enableGuards(selector);
|
246
|
+
$.Guards.prototype.enableGuards = function(selector) {
|
247
|
+
var self = this;
|
248
|
+
|
249
|
+
this.on(selector, "submit", function() {
|
250
|
+
return self.guard($(this));
|
251
|
+
});
|
252
|
+
};
|
253
|
+
|
254
|
+
// Implementation of $.liveGuard(selector);
|
255
|
+
$.Guards.prototype.liveGuard = function(selector) {
|
256
|
+
var self = this;
|
257
|
+
this.enableGuards(selector);
|
258
|
+
|
259
|
+
this.on(selector, "change blur", function(e) {
|
260
|
+
var $element = $(e.target);
|
261
|
+
|
262
|
+
if (!$element.is(":guardable")) {
|
263
|
+
return;
|
264
|
+
}
|
265
|
+
|
266
|
+
self.applyGuards(function(guard) {
|
267
|
+
if (guard.isGrouped()) {
|
268
|
+
if (guard.appliesTo($element)) {
|
269
|
+
return $element.parents("form:first").find(":guardable");
|
270
|
+
} else {
|
271
|
+
return false;
|
272
|
+
}
|
273
|
+
} else {
|
274
|
+
return $element;
|
275
|
+
}
|
276
|
+
});
|
277
|
+
});
|
278
|
+
};
|
279
|
+
|
280
|
+
/**
|
281
|
+
* Format all arguments into the first argument. This is a
|
282
|
+
* convenience function similar to the C sprintf function, though
|
283
|
+
* only with simple replacements. Replacements are formatted like
|
284
|
+
* #{i} where i is a zero based index into the additional
|
285
|
+
* arguments passed in to format beyond the first.
|
286
|
+
*
|
287
|
+
* Additional parameters not used will be ignored.
|
288
|
+
*
|
289
|
+
* Including formatting requests for parameters that don't exist
|
290
|
+
* will throw an exception.
|
291
|
+
*
|
292
|
+
* The first argument must be the string that needs to be
|
293
|
+
* formatted. Additional arguments are formatted into that
|
294
|
+
* string.
|
295
|
+
*
|
296
|
+
* If any of the arguments to the format string include a string
|
297
|
+
* that matches the #{i} format, the result could be erroneous.
|
298
|
+
*
|
299
|
+
* Example: $.guards.format("#{2} #{0} #{1}", "hello", "world", 3); // "3 hello world"
|
300
|
+
* Example: $.guards.format("#{0} #{1}", "hello", "world", 3); // "hello world".
|
301
|
+
* Example: $.guards.format("#{2} #{0} #{1}", "hello", "world"); // throws exception
|
302
|
+
*/
|
303
|
+
$.Guards.prototype.format = function() {
|
304
|
+
var str = arguments[0];
|
305
|
+
|
306
|
+
if (arguments.length > 1) {
|
307
|
+
for (var i = 1; i < arguments.length; i++) {
|
308
|
+
var regex = "\\#\\{" + (i - 1) + "\\}";
|
309
|
+
str = str.replace(new RegExp(regex, "g"), arguments[i]);
|
310
|
+
}
|
311
|
+
}
|
312
|
+
|
313
|
+
if (/\#\{\d+\}/.test(str)) {
|
314
|
+
throw new Error("Unmatched formatting found!");
|
315
|
+
}
|
316
|
+
|
317
|
+
return str;
|
318
|
+
};
|
319
|
+
|
320
|
+
/**
|
321
|
+
* Add a style element to the document head which will style
|
322
|
+
* elements with errors and their error messages. This will use
|
323
|
+
* $.guards.defaults.style.field and
|
324
|
+
* $.guards.defaults.style.message to determine what styling to
|
325
|
+
* use. These defaults are initialized to a yellow background for
|
326
|
+
* the invalid fields, and red color with a small left margin for
|
327
|
+
* error messages. The selectors used to style these are
|
328
|
+
* determined by $.guards.defaults.invalidClass and
|
329
|
+
* $.guards.defaults.messageClass.
|
330
|
+
*
|
331
|
+
* There are 2 optional arguments allowed. The first is a
|
332
|
+
* selector scope to use, and the second is overrides for styling.
|
333
|
+
* Either, both or neither arguments are allowed.
|
334
|
+
*
|
335
|
+
* With a changed selector scope, the selector for the styles is
|
336
|
+
* scoped to the given value. This can be useful for different
|
337
|
+
* styling on different forms. Note that the keys to the object
|
338
|
+
* in the "field" and "message" keys are used as css styles, and
|
339
|
+
* the values to those keys are the values for those styles.
|
340
|
+
*
|
341
|
+
* The custom style overrides can be used to change the field,
|
342
|
+
* message or both styles.
|
343
|
+
*
|
344
|
+
* Example: $.guards.style();
|
345
|
+
* Example: $.guards.style("#myForm");
|
346
|
+
* Example: $.guards.style({ field: { "color": "#ff0000" } });
|
347
|
+
* Example: $.guards.style({ message: { "color": "#ff6666" } });
|
348
|
+
* Example: $.guards.style("#myForm", { field: { "color": "#ff0000" }, message: { "color": "#ff6666" } });
|
349
|
+
*/
|
350
|
+
$.Guards.prototype.style = function() {
|
351
|
+
$("head").append(this.styleHtml.apply(this, arguments));
|
352
|
+
};
|
353
|
+
|
354
|
+
/**
|
355
|
+
* Retrieve the style html as a string to use for the
|
356
|
+
* $.guards.style() function. The documentation for that function
|
357
|
+
* applies to this as well.
|
358
|
+
*/
|
359
|
+
$.Guards.prototype.styleHtml = function() {
|
360
|
+
var fieldStyle = {};
|
361
|
+
var messageStyle = {};
|
362
|
+
var fieldSelector = "." + this.defaults.invalidClass;
|
363
|
+
var messageSelector = "." + this.defaults.messageClass;
|
364
|
+
var selectorScope, styles;
|
365
|
+
|
366
|
+
if (this.defaults.style && this.defaults.style.field) {
|
367
|
+
fieldStyle = this.defaults.style.field;
|
368
|
+
}
|
369
|
+
|
370
|
+
if (this.defaults.style && this.defaults.style.message) {
|
371
|
+
messageStyle = this.defaults.style.message;
|
372
|
+
}
|
373
|
+
|
374
|
+
if (arguments.length == 1) {
|
375
|
+
if (typeof(arguments[0]) == "string") {
|
376
|
+
selectorScope = arguments[0];
|
377
|
+
} else {
|
378
|
+
styles = arguments[0];
|
379
|
+
}
|
380
|
+
} else if (arguments.length == 2) {
|
381
|
+
selectorScope = arguments[0];
|
382
|
+
styles = arguments[1];
|
383
|
+
}
|
384
|
+
|
385
|
+
if (styles && styles.field) {
|
386
|
+
fieldStyle = styles.field;
|
387
|
+
}
|
388
|
+
|
389
|
+
if (styles && styles.message) {
|
390
|
+
messageStyle = styles.message;
|
391
|
+
}
|
392
|
+
|
393
|
+
var result = "<style>\n";
|
394
|
+
|
395
|
+
var addStyles = function(selector, styles) {
|
396
|
+
result += " " + selector + " {";
|
397
|
+
|
398
|
+
if (styles) {
|
399
|
+
$.each(styles, function(key, value) {
|
400
|
+
result += " " + key + ": " + value + ";";
|
401
|
+
});
|
402
|
+
}
|
403
|
+
|
404
|
+
result += " }\n";
|
405
|
+
};
|
406
|
+
|
407
|
+
if (selectorScope) {
|
408
|
+
fieldSelector = selectorScope + " " + fieldSelector;
|
409
|
+
messageSelector = selectorScope + " " + messageSelector;
|
410
|
+
}
|
411
|
+
|
412
|
+
addStyles(fieldSelector, fieldStyle);
|
413
|
+
addStyles(messageSelector, messageStyle);
|
414
|
+
result += "</style>";
|
415
|
+
return result;
|
416
|
+
};
|
417
|
+
|
418
|
+
/**
|
419
|
+
* This guard test method is intended to always fail, thus it
|
420
|
+
* returns false no matter what.
|
421
|
+
*/
|
422
|
+
$.Guards.prototype.always = function(value) {
|
423
|
+
return false;
|
424
|
+
};
|
425
|
+
|
426
|
+
/**
|
427
|
+
* Return whether or not the value exists in the given allowed
|
428
|
+
* list. The allowed parameter must be an array of valid values.
|
429
|
+
* Blank is considered invalid unless it exists in the list.
|
430
|
+
* Whitespace is ignored.
|
431
|
+
*/
|
432
|
+
$.Guards.prototype.isAllowed = function(value, allowed) {
|
433
|
+
value = $.trim(value);
|
434
|
+
return $.inArray(value, $.map(allowed, function(x, i) { return $.trim("" + x); })) != -1;
|
435
|
+
};
|
436
|
+
|
437
|
+
/**
|
438
|
+
* If the given values is an array, this will return false if the
|
439
|
+
* given fn returns false for any value in the array. If the
|
440
|
+
* given values is not an array, the result of calling the given
|
441
|
+
* fn on that value is returned directly.
|
442
|
+
*
|
443
|
+
* Example: $.guards.isAllValid([true, false, true], function(x) { return x; }); // false
|
444
|
+
* Example: $.guards.isAllValid(true, function(x) { return x; }); // true
|
445
|
+
*/
|
446
|
+
$.Guards.prototype.isAllValid = function(values, fn) {
|
447
|
+
if (this.isArray(values)) {
|
448
|
+
var result = true;
|
449
|
+
|
450
|
+
$.each(values, function(i, x) {
|
451
|
+
if (!fn(x)) {
|
452
|
+
result = false;
|
453
|
+
return false;
|
454
|
+
}
|
455
|
+
});
|
456
|
+
|
457
|
+
return result;
|
458
|
+
}
|
459
|
+
|
460
|
+
return fn(values);
|
461
|
+
};
|
462
|
+
|
463
|
+
/**
|
464
|
+
* If the given values is an array, this will return true if the
|
465
|
+
* given fn returns true for any value in the array. If the given
|
466
|
+
* values is not an array, the result of calling the given fn on
|
467
|
+
* that value is returned directly.
|
468
|
+
*
|
469
|
+
* Example: $.guards.isAllValid([false, false, true], function(x) { return x; }); // true
|
470
|
+
* Example: $.guards.isAllValid(false, function(x) { return x; }); // false
|
471
|
+
*/
|
472
|
+
$.Guards.prototype.isAnyValid = function(values, fn) {
|
473
|
+
if (this.isArray(values)) {
|
474
|
+
var result = false;
|
475
|
+
|
476
|
+
$.each(values, function(i, x) {
|
477
|
+
if (fn(x)) {
|
478
|
+
result = true;
|
479
|
+
return false;
|
480
|
+
}
|
481
|
+
});
|
482
|
+
|
483
|
+
return result;
|
484
|
+
}
|
485
|
+
|
486
|
+
return fn(values);
|
487
|
+
};
|
488
|
+
|
489
|
+
/**
|
490
|
+
* Return true if the value is null, undefined, an empty string,
|
491
|
+
* or a string of just spaces.
|
492
|
+
*/
|
493
|
+
$.Guards.prototype.isBlank = function(value) {
|
494
|
+
return this.isNullOrUndefined(value) || $.trim(value) == "";
|
495
|
+
};
|
496
|
+
|
497
|
+
/**
|
498
|
+
* Return whether all the values in the given array are different.
|
499
|
+
*/
|
500
|
+
$.Guards.prototype.isDifferent = function(values) {
|
501
|
+
if (values.length < 2) {
|
502
|
+
return true;
|
503
|
+
}
|
504
|
+
|
505
|
+
var found = {};
|
506
|
+
var result = true;
|
507
|
+
|
508
|
+
$.each(values, function(i, x) {
|
509
|
+
if (found[x] === true) {
|
510
|
+
result = false;
|
511
|
+
return false;
|
512
|
+
}
|
513
|
+
|
514
|
+
found[x] = true;
|
515
|
+
});
|
516
|
+
|
517
|
+
return result;
|
518
|
+
};
|
519
|
+
|
520
|
+
/**
|
521
|
+
* Return whether or not the value doesn't exist in the given
|
522
|
+
* disallowed list. The disallowed parameter must be an array of
|
523
|
+
* invalid values. Blank is considered valid unless it exists in
|
524
|
+
* the list. Whitespace is ignored.
|
525
|
+
*/
|
526
|
+
$.Guards.prototype.isDisallowed = function(value, disallowed) {
|
527
|
+
return !this.isAllowed(value, disallowed);
|
528
|
+
};
|
529
|
+
|
530
|
+
/**
|
531
|
+
* Return true if the value is null or undefined.
|
532
|
+
*/
|
533
|
+
$.Guards.prototype.isNullOrUndefined = function(value) {
|
534
|
+
return value === null || value === undefined;
|
535
|
+
};
|
536
|
+
|
537
|
+
/**
|
538
|
+
* Return the negation of calling isBlank(value).
|
539
|
+
*/
|
540
|
+
$.Guards.prototype.isPresent = function(value) {
|
541
|
+
return !this.isBlank(value);
|
542
|
+
};
|
543
|
+
|
544
|
+
/**
|
545
|
+
* Return whether all the values in the given array are the same.
|
546
|
+
*/
|
547
|
+
$.Guards.prototype.isSame = function(values) {
|
548
|
+
if (values.length < 2) {
|
549
|
+
return true;
|
550
|
+
}
|
551
|
+
|
552
|
+
var value = values[0];
|
553
|
+
var result = true;
|
554
|
+
|
555
|
+
$.each(values, function(i, x) {
|
556
|
+
if (x != value) {
|
557
|
+
result = false;
|
558
|
+
return false;
|
559
|
+
}
|
560
|
+
});
|
561
|
+
|
562
|
+
return result;
|
563
|
+
};
|
564
|
+
|
565
|
+
/**
|
566
|
+
* Return true if the given value is greater than or equal to
|
567
|
+
* options.min (if options.min is defined) and less than or equal
|
568
|
+
* to options.max (if options.max is defined).
|
569
|
+
*/
|
570
|
+
$.Guards.prototype.isInRange = function(value, options) {
|
571
|
+
if (this.isNullOrUndefined(options)) {
|
572
|
+
options = {};
|
573
|
+
}
|
574
|
+
|
575
|
+
var bigEnough = this.isNullOrUndefined(options.min) || value >= options.min;
|
576
|
+
var smallEnough = this.isNullOrUndefined(options.max) || value <= options.max;
|
577
|
+
return bigEnough && smallEnough;
|
578
|
+
};
|
579
|
+
|
580
|
+
/**
|
581
|
+
* Return whether or not the value is a valid integer.
|
582
|
+
* Appropriate options are min, max, both or neither. Blank is
|
583
|
+
* valid as a number.
|
584
|
+
*/
|
585
|
+
$.Guards.prototype.isValidInt = function(value, options) {
|
586
|
+
value = $.trim(value);
|
587
|
+
|
588
|
+
if (value == "") {
|
589
|
+
return true;
|
590
|
+
}
|
591
|
+
|
592
|
+
if (!/^(-|\+)?\d+$/.test(value)) {
|
593
|
+
return false;
|
594
|
+
}
|
595
|
+
|
596
|
+
value = parseInt(value, 10);
|
597
|
+
return this.isInRange(value, options);
|
598
|
+
};
|
599
|
+
|
600
|
+
/**
|
601
|
+
* Return whether or not the value is a valid float. Appropriate
|
602
|
+
* options are min, max, both or neither. Blank is valid as a
|
603
|
+
* number.
|
604
|
+
*/
|
605
|
+
$.Guards.prototype.isValidFloat = function(value, options) {
|
606
|
+
value = $.trim(value);
|
607
|
+
|
608
|
+
if (value == "") {
|
609
|
+
return true;
|
610
|
+
}
|
611
|
+
|
612
|
+
if (!/^(-|\+)?(\d+)?\.?\d+$/.test(value)) {
|
613
|
+
return false;
|
614
|
+
}
|
615
|
+
|
616
|
+
value = parseFloat(value);
|
617
|
+
return this.isInRange(value, options);
|
618
|
+
};
|
619
|
+
|
620
|
+
/**
|
621
|
+
* Validates the given value is a valid US money value. It
|
622
|
+
* optionally accepts min and max to specify the minimum or
|
623
|
+
* maximum values. Blank is a valid money.
|
624
|
+
*/
|
625
|
+
$.Guards.prototype.isValidMoneyUS = function(value, options) {
|
626
|
+
value = $.trim(value);
|
627
|
+
|
628
|
+
if (value == "") {
|
629
|
+
return true;
|
630
|
+
}
|
631
|
+
|
632
|
+
if (!/^\$?(-|\+)?\$?([\d,]+)?\.?\d+$/.test(value)) {
|
633
|
+
return false;
|
634
|
+
}
|
635
|
+
|
636
|
+
// Only allow 1 $.
|
637
|
+
var $i = value.indexOf("$");
|
638
|
+
if ($i >= 0 && value.indexOf("$", $i + 1) >= 0) {
|
639
|
+
return false;
|
640
|
+
}
|
641
|
+
|
642
|
+
// Ensure if there are commas they are every 3 digits
|
643
|
+
if (value.indexOf(",") >= 0 && !/^\$?(-|\+)?\$?[1-9]\d{0,2}(,\d{3,3})+(\.\d+)?$/.test(value)) {
|
644
|
+
return false;
|
645
|
+
}
|
646
|
+
|
647
|
+
// Ensure no more than 2 digits after decimal
|
648
|
+
if (value.indexOf(".") >= 0 && /\.\d{3,}$/.test(value)) {
|
649
|
+
return false;
|
650
|
+
}
|
651
|
+
|
652
|
+
value = parseFloat(value.replace(/[\$,]/g, ""));
|
653
|
+
return this.isInRange(value, options);
|
654
|
+
};
|
655
|
+
|
656
|
+
/**
|
657
|
+
* Validates the given value is a valid email. If options is
|
658
|
+
* passed with allowDisplay as true, display emails will be
|
659
|
+
* considered valid. A display email differs from a regular email
|
660
|
+
* in that it can be contained with < and > with some text ahead
|
661
|
+
* of that. Thus "John Doe <jdoe@example.com>" would be valid.
|
662
|
+
*/
|
663
|
+
$.Guards.prototype.isValidEmail = function(value, options) {
|
664
|
+
if (options && options.allowDisplay) {
|
665
|
+
var result = /.*\<([^>]+)\>\s*$/.exec(value);
|
666
|
+
|
667
|
+
if (result) {
|
668
|
+
value = result[1];
|
669
|
+
}
|
670
|
+
}
|
671
|
+
|
672
|
+
return value == "" || /^((([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);
|
673
|
+
};
|
674
|
+
|
675
|
+
/**
|
676
|
+
* Validates the given value is a valid US phone number.
|
677
|
+
*/
|
678
|
+
$.Guards.prototype.isValidPhoneUS = function(value) {
|
679
|
+
value = value.replace(/\s+/g, "");
|
680
|
+
return value == "" || value.length > 9 &&
|
681
|
+
value.match(/^(1-?)?(\([2-9]\d{2}\)|[2-9]\d{2})-?[2-9]\d{2}-?\d{4}$/);
|
682
|
+
};
|
683
|
+
|
684
|
+
/**
|
685
|
+
* Return whether or not the value is a valid string. Appropriate
|
686
|
+
* options are min or max (or both). Whitespace is not
|
687
|
+
* considered.
|
688
|
+
*/
|
689
|
+
$.Guards.prototype.isValidString = function(value, options) {
|
690
|
+
value = $.trim(value);
|
691
|
+
return this.isValidInt("" + value.length, options);
|
692
|
+
};
|
693
|
+
|
694
|
+
/**
|
695
|
+
* This guard test method is intended to never fail, thus it
|
696
|
+
* returns true no matter what. It is intended to be used to set
|
697
|
+
* up a guard that is triggered manually via triggerError().
|
698
|
+
*/
|
699
|
+
$.Guards.prototype.never = function(value) {
|
700
|
+
return true;
|
701
|
+
};
|
702
|
+
|
703
|
+
/**
|
704
|
+
* This is a utility function to act like isAnyValid or
|
705
|
+
* isAllValid, except instead of aggregating the function results,
|
706
|
+
* it passes the arguments on to the function and returns the
|
707
|
+
* results. It makes the argument an array always.
|
708
|
+
*
|
709
|
+
* Example: $.guards.passThrough([true, false, true], function(x) { return x[1]; }); // false
|
710
|
+
* Example: $.guards.passThrough(true, function(x) { return x[0]; }); // true
|
711
|
+
*/
|
712
|
+
$.Guards.prototype.passThrough = function(values, fn) {
|
713
|
+
if (!this.isArray(values)) {
|
714
|
+
values = [values];
|
715
|
+
}
|
716
|
+
|
717
|
+
return fn(values);
|
718
|
+
};
|
719
|
+
|
720
|
+
/**
|
721
|
+
* Guard all elements with the specified jQuery selector. Using
|
722
|
+
* is implicitly called with $.guards.defaults.guard, which
|
723
|
+
* defaults to "required". Note that it is simpler to use
|
724
|
+
* $.guard(selector) instead of $.guards.add(selector).
|
725
|
+
*
|
726
|
+
* Example: $.guards.add(".validPhone").using("phoneUS");
|
727
|
+
* Example: $.guards.add(".custom").using(function(value, element) {
|
728
|
+
* return value != "invalid";
|
729
|
+
* }).message("Don't use the keyword 'invalid'.");
|
730
|
+
* Example: $.guards.add(".custom").grouped().using(function(values, elements) {
|
731
|
+
* return $.inArray("invalid", values) == -1;
|
732
|
+
* }).target("#custom-error-location").tag("div")
|
733
|
+
* .message("Don't use the keyword 'invalid'.");
|
734
|
+
*/
|
735
|
+
$.Guards.prototype.add = function(selector) {
|
736
|
+
var guard = new $.Guard(selector, this);
|
737
|
+
this._guards.push(guard);
|
738
|
+
return guard;
|
739
|
+
};
|
740
|
+
|
741
|
+
/**
|
742
|
+
* Clear all errors on the form's guard fields, then invoke each
|
743
|
+
* guard on the fields in order and guard them, adding errors
|
744
|
+
* along the way as needed. Once done, focus the first visible
|
745
|
+
* field with an error.
|
746
|
+
*/
|
747
|
+
$.Guards.prototype.guard = function(form) {
|
748
|
+
var fields = form.guardableFields().clearErrors();
|
749
|
+
var result = this.applyGuards(function(guard) { return fields; });
|
750
|
+
fields.filter(":visible:has-error").eq(0).focus();
|
751
|
+
return result;
|
752
|
+
};
|
753
|
+
|
754
|
+
/**
|
755
|
+
* Apply all the guards to the fields returned from the given
|
756
|
+
* callback. The callback will receive the guard, and is expected
|
757
|
+
* to return the fields to guard against. If it returns false,
|
758
|
+
* that guard is skipped and does not affect the return value.
|
759
|
+
*/
|
760
|
+
$.Guards.prototype.applyGuards = function(callback) {
|
761
|
+
var result = true;
|
762
|
+
var self = this;
|
763
|
+
|
764
|
+
$.each(this._guards, function(index, guard) {
|
765
|
+
var fields = callback(guard);
|
766
|
+
|
767
|
+
if (fields !== false && !self.test(guard, fields)) {
|
768
|
+
result = false;
|
769
|
+
}
|
770
|
+
});
|
771
|
+
|
772
|
+
return result;
|
773
|
+
};
|
774
|
+
|
775
|
+
/**
|
776
|
+
* Use the given guard to test the given guarded fields. Errors
|
777
|
+
* will be applied if the field doesn't have an error yet.
|
778
|
+
*/
|
779
|
+
$.Guards.prototype.test = function(guard, fields) {
|
780
|
+
if (guard._grouped) {
|
781
|
+
return guard.test(fields);
|
782
|
+
}
|
783
|
+
|
784
|
+
var result = true;
|
785
|
+
|
786
|
+
fields.each(function() {
|
787
|
+
if (!guard.test(this)) {
|
788
|
+
result = false;
|
789
|
+
}
|
790
|
+
});
|
791
|
+
|
792
|
+
return result;
|
793
|
+
};
|
794
|
+
|
795
|
+
$.Guard = function(selector, guards) {
|
796
|
+
this._guards = guards || $.guards;
|
797
|
+
this._selector = selector;
|
798
|
+
this._grouped = this._guards.defaults.grouped;
|
799
|
+
this._tag = this._guards.defaults.tag;
|
800
|
+
this._messageClass = this._guards.defaults.messageClass;
|
801
|
+
this._invalidClass = this._guards.defaults.invalidClass;
|
802
|
+
this._target = this._guards.defaults.target;
|
803
|
+
this.using(this._guards.defaults.guard);
|
804
|
+
};
|
805
|
+
|
806
|
+
/**
|
807
|
+
* Guard inputs using a specified guard. The guard may be either
|
808
|
+
* a string or a function. When it is a string, it must match one
|
809
|
+
* of the pre-defined guards defined in $.guards.defaults.guards.
|
810
|
+
* The function is expected to have 2 arguments. The first is the
|
811
|
+
* value of the element being guarded, and the second is the
|
812
|
+
* actual element. If grouped is true, it will be an array of all
|
813
|
+
* matched values and all matched elements (the order of values
|
814
|
+
* will match the order of elements). Radio buttons are passed as
|
815
|
+
* separate values and elements, but the value of each will be the
|
816
|
+
* same. Specifically, the value of the checked radio button is
|
817
|
+
* the value used, unless none are checked, in which case
|
818
|
+
* $.guards.constants.notChecked will be used (which is predefined
|
819
|
+
* as an empty string).
|
820
|
+
*
|
821
|
+
* Note that the message is implicitly set when this method is
|
822
|
+
* called. If the guard is a string, the message will be set to
|
823
|
+
* $.guards.defaults.messages[guard]. If it is a function, it
|
824
|
+
* will be set to $.guards.defaults.messages.undefined.
|
825
|
+
*
|
826
|
+
* Example: $.guard(".required").using("required");
|
827
|
+
* Example: $.guard(".required").using(function(value, element) {
|
828
|
+
* return $.inArray("invalid", values) == -1;
|
829
|
+
* });
|
830
|
+
*/
|
831
|
+
$.Guard.prototype.using = function(guard) {
|
832
|
+
if (typeof(guard) == "string") {
|
833
|
+
var args = [];
|
834
|
+
|
835
|
+
if (arguments.length > 1) {
|
836
|
+
args = $.makeArray(arguments).slice(1);
|
837
|
+
}
|
838
|
+
|
839
|
+
var fn = this._guards.defaults.guards[guard];
|
840
|
+
|
841
|
+
if (this._guards.isNullOrUndefined(fn)) {
|
842
|
+
throw new Error("There is no standard guard named '" + guard + "'");
|
843
|
+
}
|
844
|
+
|
845
|
+
this._guard = fn.apply(this._guards.defaults.guards, args);
|
846
|
+
var message = this._guards.defaults.messages[guard];
|
847
|
+
|
848
|
+
if ($.isFunction(message)) {
|
849
|
+
message = message.apply(this._guards.defaults.messages, args);
|
850
|
+
}
|
851
|
+
|
852
|
+
return this.message(message);
|
853
|
+
}
|
854
|
+
|
855
|
+
this._guard = guard;
|
856
|
+
return this.message(this._guards.defaults.messages.undefined);
|
857
|
+
};
|
858
|
+
|
859
|
+
/**
|
860
|
+
* Specify a precondition for this guard. The precondition should
|
861
|
+
* be a function that accepts the element and element value as the
|
862
|
+
* parameters, like a custom guard function. The precondition is
|
863
|
+
* executed before the guard when any given input is about to be
|
864
|
+
* guarded. If the precondition returns false explicitly, the
|
865
|
+
* guard will not be executed and the field will be considered
|
866
|
+
* valid. Any other return value means the precondition passed
|
867
|
+
* (even no return). If the guard is grouped, the parameters will
|
868
|
+
* be the array of values and elements (like for a custom guard
|
869
|
+
* function).
|
870
|
+
*
|
871
|
+
* // Only require this if #other_element is checked.
|
872
|
+
* Example: $.guard(".required").using("required").precondition(function(value, element) {
|
873
|
+
* return $("#other_element").is(":checked");
|
874
|
+
* });
|
875
|
+
*
|
876
|
+
* @since 0.4
|
877
|
+
*/
|
878
|
+
$.Guard.prototype.precondition = function(fn) {
|
879
|
+
this._precondition = fn;
|
880
|
+
return this;
|
881
|
+
};
|
882
|
+
|
883
|
+
/**
|
884
|
+
* Return whether or not this guard is grouped.
|
885
|
+
*/
|
886
|
+
$.Guard.prototype.isGrouped = function() {
|
887
|
+
return this._grouped;
|
888
|
+
};
|
889
|
+
|
890
|
+
/**
|
891
|
+
* Specify whether to group element guarding by passing all values
|
892
|
+
* and elements at once instead of one at a time. When grouped,
|
893
|
+
* only 1 error message is added, and it is added after the last
|
894
|
+
* element. This defaults to $.guards.defaults.grouped. If an
|
895
|
+
* argument is passed, the value is used as the grouped value,
|
896
|
+
* otherwise invoking this method will set grouped to true.
|
897
|
+
*
|
898
|
+
* Example: $.guard(".required").using("required").grouped();
|
899
|
+
* Example: $.guard(".required").using("required").grouped(true);
|
900
|
+
*/
|
901
|
+
$.Guard.prototype.grouped = function() {
|
902
|
+
if (arguments.length == 0) {
|
903
|
+
return this.grouped(true);
|
904
|
+
}
|
905
|
+
|
906
|
+
this._grouped = arguments[0];
|
907
|
+
return this;
|
908
|
+
};
|
909
|
+
|
910
|
+
/**
|
911
|
+
* Set the type of tag to surround the error message with
|
912
|
+
* (defaults to $.guards.defaults.tag, which defaults to span).
|
913
|
+
*
|
914
|
+
* Example: $.guard(".required").using("required").tag("div");
|
915
|
+
*/
|
916
|
+
$.Guard.prototype.tag = function(tag) {
|
917
|
+
this._tag = tag;
|
918
|
+
return this.resetMessageFn();
|
919
|
+
};
|
920
|
+
|
921
|
+
$.Guard.prototype.messageClass = function(messageClass) {
|
922
|
+
this._messageClass = messageClass;
|
923
|
+
return this.resetMessageFn();
|
924
|
+
};
|
925
|
+
|
926
|
+
/**
|
927
|
+
* Set the error message to display on errors. If using is called
|
928
|
+
* with a string, this is implicitly invoked using
|
929
|
+
* $.guards.defaults.messages[usingValue]. If using is called
|
930
|
+
* with a function, this is implicitly invoked using
|
931
|
+
* $.guards.defaults.messages.undefined.
|
932
|
+
*
|
933
|
+
* Example: $.guard(".required").using("required").message("Enter something!");
|
934
|
+
*/
|
935
|
+
$.Guard.prototype.message = function(message) {
|
936
|
+
this._message = message;
|
937
|
+
return this.resetMessageFn();
|
938
|
+
};
|
939
|
+
|
940
|
+
$.Guard.prototype.invalidClass = function(invalidClass) {
|
941
|
+
this._invalidClass = invalidClass;
|
942
|
+
return this;
|
943
|
+
};
|
944
|
+
|
945
|
+
$.Guard.prototype.resetMessageFn = function() {
|
946
|
+
var self = this;
|
947
|
+
return this.messageFn(function() {
|
948
|
+
return $('<' + self._tag + ' class="' + self._messageClass + '"/>').html(self._message);
|
949
|
+
});
|
950
|
+
};
|
951
|
+
|
952
|
+
$.Guard.prototype.messageFn = function(messageFn) {
|
953
|
+
this._messageFn = messageFn;
|
954
|
+
return this;
|
955
|
+
};
|
956
|
+
|
957
|
+
$.Guard.prototype.errorElement = function() {
|
958
|
+
return this._messageFn();
|
959
|
+
};
|
960
|
+
|
961
|
+
$.Guard.prototype.attachError = function(elements, errorElement) {
|
962
|
+
if (this._target && $.isFunction(this._target)) {
|
963
|
+
var result = this._target.call(elements, errorElement);
|
964
|
+
|
965
|
+
if (result !== false) {
|
966
|
+
errorElement.appendTo($(result).eq(0));
|
967
|
+
}
|
968
|
+
} else if (this._target) {
|
969
|
+
errorElement.appendTo($(this._target).eq(0));
|
970
|
+
} else {
|
971
|
+
throw new Error("The target must be a function or selector!");
|
972
|
+
}
|
973
|
+
};
|
974
|
+
|
975
|
+
/**
|
976
|
+
* Set the target for where error messages should be appended to.
|
977
|
+
* By default, the error is placed after the error element, but
|
978
|
+
* when a target is specified, the error is appended within. The
|
979
|
+
* target may be either a selector, function, element or set of
|
980
|
+
* elements, however, only the first element is used as the target
|
981
|
+
* location for errors. If a function is specified, it will be
|
982
|
+
* called when there is a new error with the invalid element (or
|
983
|
+
* set of elements if it is a grouped guard) as the "this"
|
984
|
+
* reference. The returned value should be a single element,
|
985
|
+
* though if an array of elements is returned (or a jQuery
|
986
|
+
* selected set of elements), only the first element will be used
|
987
|
+
* as the target. Alternatively the function can take a single
|
988
|
+
* argument that specifies the error element to add to the DOM,
|
989
|
+
* and the function is expected to add the element and return
|
990
|
+
* false (indicating that it has taken care of adding the error
|
991
|
+
* element).
|
992
|
+
*
|
993
|
+
* The default target is a function that appends the error after
|
994
|
+
* the last element and returns false. The default can be changed
|
995
|
+
* via $.guards.defaults.target.
|
996
|
+
*
|
997
|
+
* Example: $.guard(".required").using("required").target("#my-errors");
|
998
|
+
* Example: $.guard(".required").using("required").target(function() { return $(this).nextAll(".error:eq(0)"); });
|
999
|
+
* Example: $.guard(".required").using("required").target(function(errorElement) {
|
1000
|
+
* errorElement.appendTo($("#myErrors"));
|
1001
|
+
* return false;
|
1002
|
+
* });
|
1003
|
+
*/
|
1004
|
+
$.Guard.prototype.target = function(target) {
|
1005
|
+
this._target = target;
|
1006
|
+
return this;
|
1007
|
+
};
|
1008
|
+
|
1009
|
+
/**
|
1010
|
+
* Determine if this guard applies to the given element (or
|
1011
|
+
* elements).
|
1012
|
+
*/
|
1013
|
+
$.Guard.prototype.appliesTo = function(element) {
|
1014
|
+
return $(element).filter(this._selector).size() > 0;
|
1015
|
+
};
|
1016
|
+
|
1017
|
+
/**
|
1018
|
+
* Using this guard, test the given element. If this guard is
|
1019
|
+
* grouped, the element is expected to actually be all field
|
1020
|
+
* elements. Returns false but doesn't apply the guard if there
|
1021
|
+
* are already errors detected on the element(s). Returns true if
|
1022
|
+
* the selector defined for this guard doesn't apply to this
|
1023
|
+
* element(s). Otherwise, applies the guard and adds an error if
|
1024
|
+
* it fails.
|
1025
|
+
*/
|
1026
|
+
$.Guard.prototype.test = function(element) {
|
1027
|
+
var $elements = $(element).filter(this._selector);
|
1028
|
+
|
1029
|
+
if ($elements.size() == 0) {
|
1030
|
+
return true;
|
1031
|
+
}
|
1032
|
+
|
1033
|
+
if (!this._guards.options.stackErrors && $elements.hasErrors()) {
|
1034
|
+
return false;
|
1035
|
+
}
|
1036
|
+
|
1037
|
+
var result;
|
1038
|
+
|
1039
|
+
// Grouped expects a group of elements, while non-grouped
|
1040
|
+
// expects a single element.
|
1041
|
+
if (this._grouped) {
|
1042
|
+
var values = [];
|
1043
|
+
var elements = [];
|
1044
|
+
|
1045
|
+
$elements.each(function() {
|
1046
|
+
values.push($(this).inputValue(this._guards));
|
1047
|
+
elements.push(this);
|
1048
|
+
});
|
1049
|
+
|
1050
|
+
if (this._precondition && this._precondition(values, elements) === false) {
|
1051
|
+
result = true;
|
1052
|
+
} else {
|
1053
|
+
result = this._guard(values, elements);
|
1054
|
+
}
|
1055
|
+
} else {
|
1056
|
+
var value = $elements.inputValue(this._guards);
|
1057
|
+
|
1058
|
+
if (this._precondition && this._precondition(value, element) === false) {
|
1059
|
+
result = true;
|
1060
|
+
} else {
|
1061
|
+
result = this._guard(value, element);
|
1062
|
+
}
|
1063
|
+
}
|
1064
|
+
|
1065
|
+
if (!result) {
|
1066
|
+
this.triggerError($elements);
|
1067
|
+
}
|
1068
|
+
|
1069
|
+
return result;
|
1070
|
+
};
|
1071
|
+
|
1072
|
+
/**
|
1073
|
+
* Explicitly trigger the error for this guard on all the elements
|
1074
|
+
* provided to this function. The elements are wrapped with a
|
1075
|
+
* jQuery object, so they may be a single element, a list of
|
1076
|
+
* elements, a jQuery selected set of elements, or even a valid
|
1077
|
+
* jQuery selector. Note that the elements don't have to be valid
|
1078
|
+
* for this guard to be applied.
|
1079
|
+
*/
|
1080
|
+
$.Guard.prototype.triggerError = function(elements) {
|
1081
|
+
if (this._grouped) {
|
1082
|
+
$(elements).addSingleError(this);
|
1083
|
+
} else {
|
1084
|
+
$(elements).addError(this);
|
1085
|
+
}
|
1086
|
+
|
1087
|
+
return this;
|
1088
|
+
}
|
1089
|
+
|
1090
|
+
$.GuardError = function(guard, element, errorElement, linked) {
|
1091
|
+
this._guard = guard;
|
1092
|
+
this._element = element;
|
1093
|
+
this._errorElement = errorElement;
|
1094
|
+
this._linked = linked;
|
1095
|
+
this._cleared = false;
|
1096
|
+
};
|
1097
|
+
|
1098
|
+
/**
|
1099
|
+
* Clear this error and any errors linked with it (grouped guards
|
1100
|
+
* and radio buttons cause all elements involved to be linked).
|
1101
|
+
*/
|
1102
|
+
$.GuardError.prototype.clear = function() {
|
1103
|
+
if (this._cleared) {
|
1104
|
+
return;
|
1105
|
+
}
|
1106
|
+
|
1107
|
+
this._errorElement.remove();
|
1108
|
+
var index = $.inArray(this, this._element.errors);
|
1109
|
+
|
1110
|
+
if (index >= 0) {
|
1111
|
+
this._element.errors.splice(index, 1);
|
1112
|
+
}
|
1113
|
+
|
1114
|
+
if (!$(this._element).hasErrorsWithInvalidClass(this._guard._invalidClass)) {
|
1115
|
+
$(this._element).removeClass(this._guard._invalidClass);
|
1116
|
+
}
|
1117
|
+
|
1118
|
+
this._cleared = true;
|
1119
|
+
|
1120
|
+
while (this._linked.length > 0) {
|
1121
|
+
this._linked.shift().clear();
|
1122
|
+
}
|
1123
|
+
};
|
1124
|
+
|
1125
|
+
/**
|
1126
|
+
* Find any applicable fields for this selected item. Applicable
|
1127
|
+
* fields are any inputs, textareas or selects.
|
1128
|
+
*/
|
1129
|
+
$.fn.guardableFields = function() {
|
1130
|
+
return this.find(":guardable");
|
1131
|
+
};
|
1132
|
+
|
1133
|
+
/**
|
1134
|
+
* Return the result of guarding the selected form.
|
1135
|
+
*/
|
1136
|
+
$.fn.guard = function() {
|
1137
|
+
return $.guards.guard(this);
|
1138
|
+
};
|
1139
|
+
|
1140
|
+
/**
|
1141
|
+
* Explicitly trigger the given guard's error all the selected
|
1142
|
+
* elements. Note that the selected elements don't have to be
|
1143
|
+
* valid for this guard to be applied. This is equivalent to
|
1144
|
+
* calling guard.triggerError($this);
|
1145
|
+
*/
|
1146
|
+
$.fn.triggerError = function(guard) {
|
1147
|
+
guard.triggerError(this);
|
1148
|
+
};
|
1149
|
+
|
1150
|
+
/**
|
1151
|
+
* Add a single error message, but mark every selected element as
|
1152
|
+
* in error pointing to the single error message. This differs
|
1153
|
+
* from addError because addError will add a new error message for
|
1154
|
+
* each selected element instead of just 1.
|
1155
|
+
*/
|
1156
|
+
$.fn.addSingleError = function(guard) {
|
1157
|
+
if (this.size() == 0) {
|
1158
|
+
$.guards.log("Attempted to add error to nothing.");
|
1159
|
+
return this;
|
1160
|
+
}
|
1161
|
+
|
1162
|
+
var element = guard.errorElement();
|
1163
|
+
guard.attachError(this, element);
|
1164
|
+
this.addClass(guard._invalidClass);
|
1165
|
+
var linked = [];
|
1166
|
+
|
1167
|
+
return this.each(function() {
|
1168
|
+
if (!this.errors) {
|
1169
|
+
this.errors = [];
|
1170
|
+
}
|
1171
|
+
|
1172
|
+
var error = new $.GuardError(guard, this, element, linked);
|
1173
|
+
linked.push(error);
|
1174
|
+
this.errors.push(error);
|
1175
|
+
});
|
1176
|
+
};
|
1177
|
+
|
1178
|
+
/**
|
1179
|
+
* Add an error message to each of the selected elements, with an
|
1180
|
+
* optional error target to place it. The target can be a
|
1181
|
+
* selector, though it will use the first selected element as the
|
1182
|
+
* target.
|
1183
|
+
*/
|
1184
|
+
$.fn.addError = function(guard) {
|
1185
|
+
var radiosAdded = {};
|
1186
|
+
|
1187
|
+
return this.each(function() {
|
1188
|
+
var $this = $(this);
|
1189
|
+
|
1190
|
+
if ($this.is(":radio")) {
|
1191
|
+
var name = $this.attr("name");
|
1192
|
+
|
1193
|
+
if (radiosAdded[name]) {
|
1194
|
+
return;
|
1195
|
+
}
|
1196
|
+
|
1197
|
+
radiosAdded[name] = true;
|
1198
|
+
var radios = $("input[name='" + name + "']:radio", $this.parents("form"));
|
1199
|
+
radios.addSingleError(guard);
|
1200
|
+
} else {
|
1201
|
+
$this.addSingleError(guard);
|
1202
|
+
}
|
1203
|
+
});
|
1204
|
+
};
|
1205
|
+
|
1206
|
+
/**
|
1207
|
+
* Obtain all errors attached to the selected elements.
|
1208
|
+
*/
|
1209
|
+
$.fn.errors = function() {
|
1210
|
+
var result = [];
|
1211
|
+
|
1212
|
+
this.each(function() {
|
1213
|
+
if (this.errors && this.errors.length > 0) {
|
1214
|
+
result.push.apply(result, this.errors);
|
1215
|
+
}
|
1216
|
+
});
|
1217
|
+
|
1218
|
+
return result;
|
1219
|
+
};
|
1220
|
+
|
1221
|
+
/**
|
1222
|
+
* Clear errors attached to the selected elements.
|
1223
|
+
*/
|
1224
|
+
$.fn.clearErrors = function() {
|
1225
|
+
$.each(this.errors(), function(index, error) {
|
1226
|
+
error.clear();
|
1227
|
+
});
|
1228
|
+
|
1229
|
+
return this;
|
1230
|
+
};
|
1231
|
+
|
1232
|
+
/**
|
1233
|
+
* Determine if any errors exist in the selected elements.
|
1234
|
+
*/
|
1235
|
+
$.fn.hasErrors = function() {
|
1236
|
+
return this.errors().length > 0;
|
1237
|
+
};
|
1238
|
+
|
1239
|
+
$.fn.hasErrorsWithInvalidClass = function(invalidClass) {
|
1240
|
+
var result = false;
|
1241
|
+
|
1242
|
+
$.each(this.errors(), function(i, error) {
|
1243
|
+
if (error._guard._invalidClass == invalidClass) {
|
1244
|
+
result = true;
|
1245
|
+
return false;
|
1246
|
+
}
|
1247
|
+
});
|
1248
|
+
|
1249
|
+
return result;
|
1250
|
+
};
|
1251
|
+
|
1252
|
+
/**
|
1253
|
+
* Obtain the value of the first selected input. This differs
|
1254
|
+
* from val() in that it will properly get the value of a set of
|
1255
|
+
* radio buttons.
|
1256
|
+
*/
|
1257
|
+
$.fn.inputValue = function(guards) {
|
1258
|
+
guards = guards || $.guards;
|
1259
|
+
|
1260
|
+
if (this.is(":radio")) {
|
1261
|
+
var checked = $("input[name='" + this.attr("name") + "']:radio:checked", this.parents("form"));
|
1262
|
+
|
1263
|
+
if (checked.size() == 0) {
|
1264
|
+
return guards.constants.notChecked;
|
1265
|
+
}
|
1266
|
+
|
1267
|
+
return checked.val();
|
1268
|
+
}
|
1269
|
+
|
1270
|
+
if (this.is(":checkbox")) {
|
1271
|
+
if (this.is(":checked")) {
|
1272
|
+
return this.val();
|
1273
|
+
}
|
1274
|
+
|
1275
|
+
return guards.constants.notChecked;
|
1276
|
+
}
|
1277
|
+
|
1278
|
+
return this.val();
|
1279
|
+
};
|
1280
|
+
|
1281
|
+
/**
|
1282
|
+
* Enable guards of this form by attaching a submit button to it
|
1283
|
+
* that returns the result of calling guard(). This will block
|
1284
|
+
* any other submit event handlers and prevent the form from being
|
1285
|
+
* submitted if guarding fails.
|
1286
|
+
*/
|
1287
|
+
$.fn.enableGuards = function() {
|
1288
|
+
return this.submit(function() {
|
1289
|
+
return $(this).guard();
|
1290
|
+
});
|
1291
|
+
};
|
1292
|
+
|
1293
|
+
/**
|
1294
|
+
* Enable guards for any form that matches the given selector.
|
1295
|
+
* This uses live events to catch submit on forms matching the
|
1296
|
+
* selector.
|
1297
|
+
*/
|
1298
|
+
$.enableGuards = function(selector) {
|
1299
|
+
$.guards.enableGuards(selector);
|
1300
|
+
};
|
1301
|
+
|
1302
|
+
/**
|
1303
|
+
* Live guard the form(s) in the given selector. This will bind
|
1304
|
+
* live on change and blur events that will guard the elements
|
1305
|
+
* when they change. It will also guard the form when it is
|
1306
|
+
* submitted.
|
1307
|
+
*/
|
1308
|
+
$.liveGuard = function(selector) {
|
1309
|
+
$.guards.liveGuard(selector);
|
1310
|
+
};
|
1311
|
+
|
1312
|
+
$.extend($.expr[":"], {
|
1313
|
+
"has-error": function(x) {
|
1314
|
+
return new Boolean(x.errors && x.errors.length > 0).valueOf();
|
1315
|
+
},
|
1316
|
+
"guardable": function(x) {
|
1317
|
+
return x.tagName.toLowerCase() == "input" || x.tagName.toLowerCase() == "textarea" || x.tagName.toLowerCase() == "select";
|
1318
|
+
}
|
1319
|
+
});
|
1320
|
+
|
1321
|
+
$.guards = new $.Guards();
|
1322
|
+
|
1323
|
+
$(function() {
|
1324
|
+
// Clear errors when the user expresses intent to fix the
|
1325
|
+
// errors.
|
1326
|
+
var clearFn = function() { $(this).clearErrors(); };
|
1327
|
+
$.guards.on(":has-error", "change", clearFn);
|
1328
|
+
$.guards.on(":has-error:radio,:has-error:checkbox", "mouseup", clearFn);
|
1329
|
+
$.guards.on("select:has-error", "mousedown", clearFn);
|
1330
|
+
|
1331
|
+
// Make sure we don't clear it if there was no error when the
|
1332
|
+
// keydown happened, otherwise a submit on enter will have the
|
1333
|
+
// error flash and then go away on the keyup.
|
1334
|
+
$.guards.on(":has-error", "keydown", function() { this.clearable = true; });
|
1335
|
+
$.guards.on(":has-error", "keyup", function() {
|
1336
|
+
if (this.clearable) {
|
1337
|
+
this.clearable = false;
|
1338
|
+
$(this).clearErrors();
|
1339
|
+
}
|
1340
|
+
});
|
1341
|
+
});
|
1342
|
+
})(jQuery);
|