guardsjs-rails 0.7.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,8 @@
1
+ require "guardsjs-rails/version"
2
+
3
+ module GuardsJS
4
+ module Rails
5
+ class Engine < ::Rails::Engine
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,5 @@
1
+ module GuardsJS
2
+ module Rails
3
+ VERSION = "0.7.2"
4
+ end
5
+ end
@@ -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);