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.
@@ -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);