Project

General

Profile

1
/* jqBootstrapValidation
2
 * A plugin for automating validation on Twitter Bootstrap formatted forms.
3
 *
4
 * v1.3.6
5
 *
6
 * License: MIT <http://opensource.org/licenses/mit-license.php> - see LICENSE file
7
 *
8
 * http://ReactiveRaven.github.com/jqBootstrapValidation/
9
 */
10

    
11
(function( $ ){
12

    
13
	var createdElements = [];
14

    
15
	var defaults = {
16
		options: {
17
			prependExistingHelpBlock: false,
18
			sniffHtml: true, // sniff for 'required', 'maxlength', etc
19
			preventSubmit: true, // stop the form submit event from firing if validation fails
20
			submitError: false, // function called if there is an error when trying to submit
21
			submitSuccess: false, // function called just before a successful submit event is sent to the server
22
            semanticallyStrict: false, // set to true to tidy up generated HTML output
23
			autoAdd: {
24
				helpBlocks: true
25
			},
26
            filter: function () {
27
                // return $(this).is(":visible"); // only validate elements you can see
28
                return true; // validate everything
29
            }
30
		},
31
    methods: {
32
      init : function( options ) {
33

    
34
        var settings = $.extend(true, {}, defaults);
35

    
36
        settings.options = $.extend(true, settings.options, options);
37

    
38
        var $siblingElements = this;
39

    
40
        var uniqueForms = $.unique(
41
          $siblingElements.map( function () {
42
            return $(this).parents("form")[0];
43
          }).toArray()
44
        );
45

    
46
        $(uniqueForms).bind("submit", function (e) {
47
          var $form = $(this);
48
          var warningsFound = 0;
49
          var $inputs = $form.find("input,textarea,select").not("[type=submit],[type=image]").filter(settings.options.filter);
50
          $inputs.trigger("submit.validation").trigger("validationLostFocus.validation");
51

    
52
          $inputs.each(function (i, el) {
53
            var $this = $(el),
54
              $controlGroup = $this.parents(".control-group").first();
55
            if (
56
              $controlGroup.hasClass("warning")
57
            ) {
58
              $controlGroup.removeClass("warning").addClass("error");
59
              warningsFound++;
60
            }
61
          });
62

    
63
          $inputs.trigger("validationLostFocus.validation");
64

    
65
          if (warningsFound) {
66
            if (settings.options.preventSubmit) {
67
              e.preventDefault();
68
            }
69
            $form.addClass("error");
70
            if ($.isFunction(settings.options.submitError)) {
71
              settings.options.submitError($form, e, $inputs.jqBootstrapValidation("collectErrors", true));
72
            }
73
          } else {
74
            $form.removeClass("error");
75
            if ($.isFunction(settings.options.submitSuccess)) {
76
              settings.options.submitSuccess($form, e);
77
            }
78
          }
79
        });
80

    
81
        return this.each(function(){
82

    
83
          // Get references to everything we're interested in
84
          var $this = $(this),
85
            $controlGroup = $this.parents(".control-group").first(),
86
            $helpBlock = $controlGroup.find(".help-block").first(),
87
            $form = $this.parents("form").first(),
88
            validatorNames = [];
89

    
90
          // create message container if not exists
91
          if (!$helpBlock.length && settings.options.autoAdd && settings.options.autoAdd.helpBlocks) {
92
              $helpBlock = $('<div class="help-block" />');
93
              $controlGroup.find('.controls').append($helpBlock);
94
							createdElements.push($helpBlock[0]);
95
          }
96

    
97
          // =============================================================
98
          //                                     SNIFF HTML FOR VALIDATORS
99
          // =============================================================
100

    
101
          // *snort sniff snuffle*
102

    
103
          if (settings.options.sniffHtml) {
104
            var message = "";
105
            // ---------------------------------------------------------
106
            //                                                   PATTERN
107
            // ---------------------------------------------------------
108
            if ($this.attr("pattern") !== undefined) {
109
              message = "Not in the expected format<!-- data-validation-pattern-message to override -->";
110
              if ($this.data("validationPatternMessage")) {
111
                message = $this.data("validationPatternMessage");
112
              }
113
              $this.data("validationPatternMessage", message);
114
              $this.data("validationPatternRegex", $this.attr("pattern"));
115
            }
116
            // ---------------------------------------------------------
117
            //                                                       MAX
118
            // ---------------------------------------------------------
119
            if ($this.attr("max") !== undefined || $this.attr("aria-valuemax") !== undefined) {
120
              var max = ($this.attr("max") !== undefined ? $this.attr("max") : $this.attr("aria-valuemax"));
121
              message = "Too high: Maximum of '" + max + "'<!-- data-validation-max-message to override -->";
122
              if ($this.data("validationMaxMessage")) {
123
                message = $this.data("validationMaxMessage");
124
              }
125
              $this.data("validationMaxMessage", message);
126
              $this.data("validationMaxMax", max);
127
            }
128
            // ---------------------------------------------------------
129
            //                                                       MIN
130
            // ---------------------------------------------------------
131
            if ($this.attr("min") !== undefined || $this.attr("aria-valuemin") !== undefined) {
132
              var min = ($this.attr("min") !== undefined ? $this.attr("min") : $this.attr("aria-valuemin"));
133
              message = "Too low: Minimum of '" + min + "'<!-- data-validation-min-message to override -->";
134
              if ($this.data("validationMinMessage")) {
135
                message = $this.data("validationMinMessage");
136
              }
137
              $this.data("validationMinMessage", message);
138
              $this.data("validationMinMin", min);
139
            }
140
            // ---------------------------------------------------------
141
            //                                                 MAXLENGTH
142
            // ---------------------------------------------------------
143
            if ($this.attr("maxlength") !== undefined) {
144
              message = "Too long: Maximum of '" + $this.attr("maxlength") + "' characters<!-- data-validation-maxlength-message to override -->";
145
              if ($this.data("validationMaxlengthMessage")) {
146
                message = $this.data("validationMaxlengthMessage");
147
              }
148
              $this.data("validationMaxlengthMessage", message);
149
              $this.data("validationMaxlengthMaxlength", $this.attr("maxlength"));
150
            }
151
            // ---------------------------------------------------------
152
            //                                                 MINLENGTH
153
            // ---------------------------------------------------------
154
            if ($this.attr("minlength") !== undefined) {
155
              message = "Too short: Minimum of '" + $this.attr("minlength") + "' characters<!-- data-validation-minlength-message to override -->";
156
              if ($this.data("validationMinlengthMessage")) {
157
                message = $this.data("validationMinlengthMessage");
158
              }
159
              $this.data("validationMinlengthMessage", message);
160
              $this.data("validationMinlengthMinlength", $this.attr("minlength"));
161
            }
162
            // ---------------------------------------------------------
163
            //                                                  REQUIRED
164
            // ---------------------------------------------------------
165
            if ($this.attr("required") !== undefined || $this.attr("aria-required") !== undefined) {
166
              message = settings.builtInValidators.required.message;
167
              if ($this.data("validationRequiredMessage")) {
168
                message = $this.data("validationRequiredMessage");
169
              }
170
              $this.data("validationRequiredMessage", message);
171
            }
172
            // ---------------------------------------------------------
173
            //                                                    NUMBER
174
            // ---------------------------------------------------------
175
            if ($this.attr("type") !== undefined && $this.attr("type").toLowerCase() === "number") {
176
              message = settings.builtInValidators.number.message;
177
              if ($this.data("validationNumberMessage")) {
178
                message = $this.data("validationNumberMessage");
179
              }
180
              $this.data("validationNumberMessage", message);
181
            }
182
            // ---------------------------------------------------------
183
            //                                                     EMAIL
184
            // ---------------------------------------------------------
185
            if ($this.attr("type") !== undefined && $this.attr("type").toLowerCase() === "email") {
186
              message = "Not a valid email address<!-- data-validator-validemail-message to override -->";
187
              if ($this.data("validationValidemailMessage")) {
188
                message = $this.data("validationValidemailMessage");
189
              } else if ($this.data("validationEmailMessage")) {
190
                message = $this.data("validationEmailMessage");
191
              }
192
              $this.data("validationValidemailMessage", message);
193
            }
194
            // ---------------------------------------------------------
195
            //                                                MINCHECKED
196
            // ---------------------------------------------------------
197
            if ($this.attr("minchecked") !== undefined) {
198
              message = "Not enough options checked; Minimum of '" + $this.attr("minchecked") + "' required<!-- data-validation-minchecked-message to override -->";
199
              if ($this.data("validationMincheckedMessage")) {
200
                message = $this.data("validationMincheckedMessage");
201
              }
202
              $this.data("validationMincheckedMessage", message);
203
              $this.data("validationMincheckedMinchecked", $this.attr("minchecked"));
204
            }
205
            // ---------------------------------------------------------
206
            //                                                MAXCHECKED
207
            // ---------------------------------------------------------
208
            if ($this.attr("maxchecked") !== undefined) {
209
              message = "Too many options checked; Maximum of '" + $this.attr("maxchecked") + "' required<!-- data-validation-maxchecked-message to override -->";
210
              if ($this.data("validationMaxcheckedMessage")) {
211
                message = $this.data("validationMaxcheckedMessage");
212
              }
213
              $this.data("validationMaxcheckedMessage", message);
214
              $this.data("validationMaxcheckedMaxchecked", $this.attr("maxchecked"));
215
            }
216
          }
217

    
218
          // =============================================================
219
          //                                       COLLECT VALIDATOR NAMES
220
          // =============================================================
221

    
222
          // Get named validators
223
          if ($this.data("validation") !== undefined) {
224
            validatorNames = $this.data("validation").split(",");
225
          }
226

    
227
          // Get extra ones defined on the element's data attributes
228
          $.each($this.data(), function (i, el) {
229
            var parts = i.replace(/([A-Z])/g, ",$1").split(",");
230
            if (parts[0] === "validation" && parts[1]) {
231
              validatorNames.push(parts[1]);
232
            }
233
          });
234

    
235
          // =============================================================
236
          //                                     NORMALISE VALIDATOR NAMES
237
          // =============================================================
238

    
239
          var validatorNamesToInspect = validatorNames;
240
          var newValidatorNamesToInspect = [];
241

    
242
          do // repeatedly expand 'shortcut' validators into their real validators
243
          {
244
            // Uppercase only the first letter of each name
245
            $.each(validatorNames, function (i, el) {
246
              validatorNames[i] = formatValidatorName(el);
247
            });
248

    
249
            // Remove duplicate validator names
250
            validatorNames = $.unique(validatorNames);
251

    
252
            // Pull out the new validator names from each shortcut
253
            newValidatorNamesToInspect = [];
254
            $.each(validatorNamesToInspect, function(i, el) {
255
              if ($this.data("validation" + el + "Shortcut") !== undefined) {
256
                // Are these custom validators?
257
                // Pull them out!
258
                $.each($this.data("validation" + el + "Shortcut").split(","), function(i2, el2) {
259
                  newValidatorNamesToInspect.push(el2);
260
                });
261
              } else if (settings.builtInValidators[el.toLowerCase()]) {
262
                // Is this a recognised built-in?
263
                // Pull it out!
264
                var validator = settings.builtInValidators[el.toLowerCase()];
265
                if (validator.type.toLowerCase() === "shortcut") {
266
                  $.each(validator.shortcut.split(","), function (i, el) {
267
                    el = formatValidatorName(el);
268
                    newValidatorNamesToInspect.push(el);
269
                    validatorNames.push(el);
270
                  });
271
                }
272
              }
273
            });
274

    
275
            validatorNamesToInspect = newValidatorNamesToInspect;
276

    
277
          } while (validatorNamesToInspect.length > 0)
278

    
279
          // =============================================================
280
          //                                       SET UP VALIDATOR ARRAYS
281
          // =============================================================
282

    
283
          var validators = {};
284

    
285
          $.each(validatorNames, function (i, el) {
286
            // Set up the 'override' message
287
            var message = $this.data("validation" + el + "Message");
288
            var hasOverrideMessage = (message !== undefined);
289
            var foundValidator = false;
290
            message =
291
              (
292
                message
293
                  ? message
294
                  : "'" + el + "' validation failed <!-- Add attribute 'data-validation-" + el.toLowerCase() + "-message' to input to change this message -->"
295
              )
296
            ;
297

    
298
            $.each(
299
              settings.validatorTypes,
300
              function (validatorType, validatorTemplate) {
301
                if (validators[validatorType] === undefined) {
302
                  validators[validatorType] = [];
303
                }
304
                if (!foundValidator && $this.data("validation" + el + formatValidatorName(validatorTemplate.name)) !== undefined) {
305
                  validators[validatorType].push(
306
                    $.extend(
307
                      true,
308
                      {
309
                        name: formatValidatorName(validatorTemplate.name),
310
                        message: message
311
                      },
312
                      validatorTemplate.init($this, el)
313
                    )
314
                  );
315
                  foundValidator = true;
316
                }
317
              }
318
            );
319

    
320
            if (!foundValidator && settings.builtInValidators[el.toLowerCase()]) {
321

    
322
              var validator = $.extend(true, {}, settings.builtInValidators[el.toLowerCase()]);
323
              if (hasOverrideMessage) {
324
                validator.message = message;
325
              }
326
              var validatorType = validator.type.toLowerCase();
327

    
328
              if (validatorType === "shortcut") {
329
                foundValidator = true;
330
              } else {
331
                $.each(
332
                  settings.validatorTypes,
333
                  function (validatorTemplateType, validatorTemplate) {
334
                    if (validators[validatorTemplateType] === undefined) {
335
                      validators[validatorTemplateType] = [];
336
                    }
337
                    if (!foundValidator && validatorType === validatorTemplateType.toLowerCase()) {
338
                      $this.data("validation" + el + formatValidatorName(validatorTemplate.name), validator[validatorTemplate.name.toLowerCase()]);
339
                      validators[validatorType].push(
340
                        $.extend(
341
                          validator,
342
                          validatorTemplate.init($this, el)
343
                        )
344
                      );
345
                      foundValidator = true;
346
                    }
347
                  }
348
                );
349
              }
350
            }
351

    
352
            if (! foundValidator) {
353
              $.error("Cannot find validation info for '" + el + "'");
354
            }
355
          });
356

    
357
          // =============================================================
358
          //                                         STORE FALLBACK VALUES
359
          // =============================================================
360

    
361
          $helpBlock.data(
362
            "original-contents",
363
            (
364
              $helpBlock.data("original-contents")
365
                ? $helpBlock.data("original-contents")
366
                : $helpBlock.html()
367
            )
368
          );
369

    
370
          $helpBlock.data(
371
            "original-role",
372
            (
373
              $helpBlock.data("original-role")
374
                ? $helpBlock.data("original-role")
375
                : $helpBlock.attr("role")
376
            )
377
          );
378

    
379
          $controlGroup.data(
380
            "original-classes",
381
            (
382
              $controlGroup.data("original-clases")
383
                ? $controlGroup.data("original-classes")
384
                : $controlGroup.attr("class")
385
            )
386
          );
387

    
388
          $this.data(
389
            "original-aria-invalid",
390
            (
391
              $this.data("original-aria-invalid")
392
                ? $this.data("original-aria-invalid")
393
                : $this.attr("aria-invalid")
394
            )
395
          );
396

    
397
          // =============================================================
398
          //                                                    VALIDATION
399
          // =============================================================
400

    
401
          $this.bind(
402
            "validation.validation",
403
            function (event, params) {
404

    
405
              var value = getValue($this);
406

    
407
              // Get a list of the errors to apply
408
              var errorsFound = [];
409

    
410
              $.each(validators, function (validatorType, validatorTypeArray) {
411
                if (value || value.length || (params && params.includeEmpty) || (!!settings.validatorTypes[validatorType].blockSubmit && params && !!params.submitting)) {
412
                  $.each(validatorTypeArray, function (i, validator) {
413
                    if (settings.validatorTypes[validatorType].validate($this, value, validator)) {
414
                      errorsFound.push(validator.message);
415
                    }
416
                  });
417
                }
418
              });
419

    
420
              return errorsFound;
421
            }
422
          );
423

    
424
          $this.bind(
425
            "getValidators.validation",
426
            function () {
427
              return validators;
428
            }
429
          );
430

    
431
          // =============================================================
432
          //                                             WATCH FOR CHANGES
433
          // =============================================================
434
          $this.bind(
435
            "submit.validation",
436
            function () {
437
              return $this.triggerHandler("change.validation", {submitting: true});
438
            }
439
          );
440
          $this.bind(
441
            [
442
              "keyup",
443
              "focus",
444
              "blur",
445
              "click",
446
              "keydown",
447
              "keypress",
448
              "change"
449
            ].join(".validation ") + ".validation",
450
            function (e, params) {
451

    
452
              var value = getValue($this);
453

    
454
              var errorsFound = [];
455

    
456
              $controlGroup.find("input,textarea,select").each(function (i, el) {
457
                var oldCount = errorsFound.length;
458
                $.each($(el).triggerHandler("validation.validation", params), function (j, message) {
459
                  errorsFound.push(message);
460
                });
461
                if (errorsFound.length > oldCount) {
462
                  $(el).attr("aria-invalid", "true");
463
                } else {
464
                  var original = $this.data("original-aria-invalid");
465
                  $(el).attr("aria-invalid", (original !== undefined ? original : false));
466
                }
467
              });
468

    
469
              $form.find("input,select,textarea").not($this).not("[name=\"" + $this.attr("name") + "\"]").trigger("validationLostFocus.validation");
470

    
471
              errorsFound = $.unique(errorsFound.sort());
472

    
473
              // Were there any errors?
474
              if (errorsFound.length) {
475
                // Better flag it up as a warning.
476
                $controlGroup.removeClass("success error").addClass("warning");
477

    
478
                // How many errors did we find?
479
                if (settings.options.semanticallyStrict && errorsFound.length === 1) {
480
                  // Only one? Being strict? Just output it.
481
                  $helpBlock.html(errorsFound[0] + 
482
                    ( settings.options.prependExistingHelpBlock ? $helpBlock.data("original-contents") : "" ));
483
                } else {
484
                  // Multiple? Being sloppy? Glue them together into an UL.
485
                  $helpBlock.html("<ul role=\"alert\"><li>" + errorsFound.join("</li><li>") + "</li></ul>" +
486
                    ( settings.options.prependExistingHelpBlock ? $helpBlock.data("original-contents") : "" ));
487
                }
488
              } else {
489
                $controlGroup.removeClass("warning error success");
490
                if (value.length > 0) {
491
                  $controlGroup.addClass("success");
492
                }
493
                $helpBlock.html($helpBlock.data("original-contents"));
494
              }
495

    
496
              if (e.type === "blur") {
497
                $controlGroup.removeClass("success");
498
              }
499
            }
500
          );
501
          $this.bind("validationLostFocus.validation", function () {
502
            $controlGroup.removeClass("success");
503
          });
504
        });
505
      },
506
      destroy : function( ) {
507

    
508
        return this.each(
509
          function() {
510

    
511
            var
512
              $this = $(this),
513
              $controlGroup = $this.parents(".control-group").first(),
514
              $helpBlock = $controlGroup.find(".help-block").first();
515

    
516
            // remove our events
517
            $this.unbind('.validation'); // events are namespaced.
518
            // reset help text
519
            $helpBlock.html($helpBlock.data("original-contents"));
520
            // reset classes
521
            $controlGroup.attr("class", $controlGroup.data("original-classes"));
522
            // reset aria
523
            $this.attr("aria-invalid", $this.data("original-aria-invalid"));
524
            // reset role
525
            $helpBlock.attr("role", $this.data("original-role"));
526
						// remove all elements we created
527
						if (createdElements.indexOf($helpBlock[0]) > -1) {
528
							$helpBlock.remove();
529
						}
530

    
531
          }
532
        );
533

    
534
      },
535
      collectErrors : function(includeEmpty) {
536

    
537
        var errorMessages = {};
538
        this.each(function (i, el) {
539
          var $el = $(el);
540
          var name = $el.attr("name");
541
          var errors = $el.triggerHandler("validation.validation", {includeEmpty: true});
542
          errorMessages[name] = $.extend(true, errors, errorMessages[name]);
543
        });
544

    
545
        $.each(errorMessages, function (i, el) {
546
          if (el.length === 0) {
547
            delete errorMessages[i];
548
          }
549
        });
550

    
551
        return errorMessages;
552

    
553
      },
554
      hasErrors: function() {
555

    
556
        var errorMessages = [];
557

    
558
        this.each(function (i, el) {
559
          errorMessages = errorMessages.concat(
560
            $(el).triggerHandler("getValidators.validation") ? $(el).triggerHandler("validation.validation", {submitting: true}) : []
561
          );
562
        });
563

    
564
        return (errorMessages.length > 0);
565
      },
566
      override : function (newDefaults) {
567
        defaults = $.extend(true, defaults, newDefaults);
568
      }
569
    },
570
		validatorTypes: {
571
      callback: {
572
        name: "callback",
573
        init: function ($this, name) {
574
          return {
575
            validatorName: name,
576
            callback: $this.data("validation" + name + "Callback"),
577
            lastValue: $this.val(),
578
            lastValid: true,
579
            lastFinished: true
580
          };
581
        },
582
        validate: function ($this, value, validator) {
583
          if (validator.lastValue === value && validator.lastFinished) {
584
            return !validator.lastValid;
585
          }
586

    
587
          if (validator.lastFinished === true)
588
          {
589
            validator.lastValue = value;
590
            validator.lastValid = true;
591
            validator.lastFinished = false;
592

    
593
            var rrjqbvValidator = validator;
594
            var rrjqbvThis = $this;
595
            executeFunctionByName(
596
              validator.callback,
597
              window,
598
              $this,
599
              value,
600
              function (data) {
601
                if (rrjqbvValidator.lastValue === data.value) {
602
                  rrjqbvValidator.lastValid = data.valid;
603
                  if (data.message) {
604
                    rrjqbvValidator.message = data.message;
605
                  }
606
                  rrjqbvValidator.lastFinished = true;
607
                  rrjqbvThis.data("validation" + rrjqbvValidator.validatorName + "Message", rrjqbvValidator.message);
608
                  // Timeout is set to avoid problems with the events being considered 'already fired'
609
                  setTimeout(function () {
610
                    rrjqbvThis.trigger("change.validation");
611
                  }, 1); // doesn't need a long timeout, just long enough for the event bubble to burst
612
                }
613
              }
614
            );
615
          }
616

    
617
          return false;
618

    
619
        }
620
      },
621
      ajax: {
622
        name: "ajax",
623
        init: function ($this, name) {
624
          return {
625
            validatorName: name,
626
            url: $this.data("validation" + name + "Ajax"),
627
            lastValue: $this.val(),
628
            lastValid: true,
629
            lastFinished: true
630
          };
631
        },
632
        validate: function ($this, value, validator) {
633
          if (""+validator.lastValue === ""+value && validator.lastFinished === true) {
634
            return validator.lastValid === false;
635
          }
636

    
637
          if (validator.lastFinished === true)
638
          {
639
            validator.lastValue = value;
640
            validator.lastValid = true;
641
            validator.lastFinished = false;
642
            $.ajax({
643
              url: validator.url,
644
              data: "value=" + value + "&field=" + $this.attr("name"),
645
              dataType: "json",
646
              success: function (data) {
647
                if (""+validator.lastValue === ""+data.value) {
648
                  validator.lastValid = !!(data.valid);
649
                  if (data.message) {
650
                    validator.message = data.message;
651
                  }
652
                  validator.lastFinished = true;
653
                  $this.data("validation" + validator.validatorName + "Message", validator.message);
654
                  // Timeout is set to avoid problems with the events being considered 'already fired'
655
                  setTimeout(function () {
656
                    $this.trigger("change.validation");
657
                  }, 1); // doesn't need a long timeout, just long enough for the event bubble to burst
658
                }
659
              },
660
              failure: function () {
661
                validator.lastValid = true;
662
                validator.message = "ajax call failed";
663
                validator.lastFinished = true;
664
                $this.data("validation" + validator.validatorName + "Message", validator.message);
665
                // Timeout is set to avoid problems with the events being considered 'already fired'
666
                setTimeout(function () {
667
                  $this.trigger("change.validation");
668
                }, 1); // doesn't need a long timeout, just long enough for the event bubble to burst
669
              }
670
            });
671
          }
672

    
673
          return false;
674

    
675
        }
676
      },
677
			regex: {
678
				name: "regex",
679
				init: function ($this, name) {
680
					return {regex: regexFromString($this.data("validation" + name + "Regex"))};
681
				},
682
				validate: function ($this, value, validator) {
683
					return (!validator.regex.test(value) && ! validator.negative)
684
						|| (validator.regex.test(value) && validator.negative);
685
				}
686
			},
687
			required: {
688
				name: "required",
689
				init: function ($this, name) {
690
					return {};
691
				},
692
				validate: function ($this, value, validator) {
693
					return !!(value.length === 0  && ! validator.negative)
694
						|| !!(value.length > 0 && validator.negative);
695
				},
696
        blockSubmit: true
697
			},
698
			match: {
699
				name: "match",
700
				init: function ($this, name) {
701
					var element = $this.parents("form").first().find("[name=\"" + $this.data("validation" + name + "Match") + "\"]").first();
702
					element.bind("validation.validation", function () {
703
						$this.trigger("change.validation", {submitting: true});
704
					});
705
					return {"element": element};
706
				},
707
				validate: function ($this, value, validator) {
708
					return (value !== validator.element.val() && ! validator.negative)
709
						|| (value === validator.element.val() && validator.negative);
710
				},
711
        blockSubmit: true
712
			},
713
			max: {
714
				name: "max",
715
				init: function ($this, name) {
716
					return {max: $this.data("validation" + name + "Max")};
717
				},
718
				validate: function ($this, value, validator) {
719
					return (parseFloat(value, 10) > parseFloat(validator.max, 10) && ! validator.negative)
720
						|| (parseFloat(value, 10) <= parseFloat(validator.max, 10) && validator.negative);
721
				}
722
			},
723
			min: {
724
				name: "min",
725
				init: function ($this, name) {
726
					return {min: $this.data("validation" + name + "Min")};
727
				},
728
				validate: function ($this, value, validator) {
729
					return (parseFloat(value) < parseFloat(validator.min) && ! validator.negative)
730
						|| (parseFloat(value) >= parseFloat(validator.min) && validator.negative);
731
				}
732
			},
733
			maxlength: {
734
				name: "maxlength",
735
				init: function ($this, name) {
736
					return {maxlength: $this.data("validation" + name + "Maxlength")};
737
				},
738
				validate: function ($this, value, validator) {
739
					return ((value.length > validator.maxlength) && ! validator.negative)
740
						|| ((value.length <= validator.maxlength) && validator.negative);
741
				}
742
			},
743
			minlength: {
744
				name: "minlength",
745
				init: function ($this, name) {
746
					return {minlength: $this.data("validation" + name + "Minlength")};
747
				},
748
				validate: function ($this, value, validator) {
749
					return ((value.length < validator.minlength) && ! validator.negative)
750
						|| ((value.length >= validator.minlength) && validator.negative);
751
				}
752
			},
753
			maxchecked: {
754
				name: "maxchecked",
755
				init: function ($this, name) {
756
					var elements = $this.parents("form").first().find("[name=\"" + $this.attr("name") + "\"]");
757
					elements.bind("click.validation", function () {
758
						$this.trigger("change.validation", {includeEmpty: true});
759
					});
760
					return {maxchecked: $this.data("validation" + name + "Maxchecked"), elements: elements};
761
				},
762
				validate: function ($this, value, validator) {
763
					return (validator.elements.filter(":checked").length > validator.maxchecked && ! validator.negative)
764
						|| (validator.elements.filter(":checked").length <= validator.maxchecked && validator.negative);
765
				},
766
        blockSubmit: true
767
			},
768
			minchecked: {
769
				name: "minchecked",
770
				init: function ($this, name) {
771
					var elements = $this.parents("form").first().find("[name=\"" + $this.attr("name") + "\"]");
772
					elements.bind("click.validation", function () {
773
						$this.trigger("change.validation", {includeEmpty: true});
774
					});
775
					return {minchecked: $this.data("validation" + name + "Minchecked"), elements: elements};
776
				},
777
				validate: function ($this, value, validator) {
778
					return (validator.elements.filter(":checked").length < validator.minchecked && ! validator.negative)
779
						|| (validator.elements.filter(":checked").length >= validator.minchecked && validator.negative);
780
				},
781
        blockSubmit: true
782
			}
783
		},
784
		builtInValidators: {
785
			email: {
786
				name: "Email",
787
				type: "shortcut",
788
				shortcut: "validemail"
789
			},
790
			validemail: {
791
				name: "Validemail",
792
				type: "regex",
793
				regex: "[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\\.[A-Za-z]{2,4}",
794
				message: "Not a valid email address<!-- data-validator-validemail-message to override -->"
795
			},
796
			passwordagain: {
797
				name: "Passwordagain",
798
				type: "match",
799
				match: "password",
800
				message: "Does not match the given password<!-- data-validator-paswordagain-message to override -->"
801
			},
802
			positive: {
803
				name: "Positive",
804
				type: "shortcut",
805
				shortcut: "number,positivenumber"
806
			},
807
			negative: {
808
				name: "Negative",
809
				type: "shortcut",
810
				shortcut: "number,negativenumber"
811
			},
812
			number: {
813
				name: "Number",
814
				type: "regex",
815
				regex: "([+-]?\\\d+(\\\.\\\d*)?([eE][+-]?[0-9]+)?)?",
816
				message: "Must be a number<!-- data-validator-number-message to override -->"
817
			},
818
			integer: {
819
				name: "Integer",
820
				type: "regex",
821
				regex: "[+-]?\\\d+",
822
				message: "No decimal places allowed<!-- data-validator-integer-message to override -->"
823
			},
824
			positivenumber: {
825
				name: "Positivenumber",
826
				type: "min",
827
				min: 0,
828
				message: "Must be a positive number<!-- data-validator-positivenumber-message to override -->"
829
			},
830
			negativenumber: {
831
				name: "Negativenumber",
832
				type: "max",
833
				max: 0,
834
				message: "Must be a negative number<!-- data-validator-negativenumber-message to override -->"
835
			},
836
			required: {
837
				name: "Required",
838
				type: "required",
839
				message: "This is required<!-- data-validator-required-message to override -->"
840
			},
841
			checkone: {
842
				name: "Checkone",
843
				type: "minchecked",
844
				minchecked: 1,
845
				message: "Check at least one option<!-- data-validation-checkone-message to override -->"
846
			}
847
		}
848
	};
849

    
850
	var formatValidatorName = function (name) {
851
		return name
852
			.toLowerCase()
853
			.replace(
854
				/(^|\s)([a-z])/g ,
855
				function(m,p1,p2) {
856
					return p1+p2.toUpperCase();
857
				}
858
			)
859
		;
860
	};
861

    
862
	var getValue = function ($this) {
863
		// Extract the value we're talking about
864
		var value = $this.val();
865
		var type = $this.attr("type");
866
		if (type === "checkbox") {
867
			value = ($this.is(":checked") ? value : "");
868
		}
869
		if (type === "radio") {
870
			value = ($('input[name="' + $this.attr("name") + '"]:checked').length > 0 ? value : "");
871
		}
872
		return value;
873
	};
874

    
875
  function regexFromString(inputstring) {
876
		return new RegExp("^" + inputstring + "$");
877
	}
878

    
879
  /**
880
   * Thanks to Jason Bunting via StackOverflow.com
881
   *
882
   * http://stackoverflow.com/questions/359788/how-to-execute-a-javascript-function-when-i-have-its-name-as-a-string#answer-359910
883
   * Short link: http://tinyurl.com/executeFunctionByName
884
  **/
885
  function executeFunctionByName(functionName, context /*, args*/) {
886
    var args = Array.prototype.slice.call(arguments).splice(2);
887
    var namespaces = functionName.split(".");
888
    var func = namespaces.pop();
889
    for(var i = 0; i < namespaces.length; i++) {
890
      context = context[namespaces[i]];
891
    }
892
    return context[func].apply(this, args);
893
  }
894

    
895
	$.fn.jqBootstrapValidation = function( method ) {
896

    
897
		if ( defaults.methods[method] ) {
898
			return defaults.methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
899
		} else if ( typeof method === 'object' || ! method ) {
900
			return defaults.methods.init.apply( this, arguments );
901
		} else {
902
		$.error( 'Method ' +  method + ' does not exist on jQuery.jqBootstrapValidation' );
903
			return null;
904
		}
905

    
906
	};
907

    
908
  $.jqBootstrapValidation = function (options) {
909
    $(":input").not("[type=image],[type=submit]").jqBootstrapValidation.apply(this,arguments);
910
  };
911

    
912
})( jQuery );
(10-10/18)