├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── Gruntfile.js ├── LICENSE-MIT ├── README.md ├── component.json ├── dist ├── jqBootstrapValidation-1.3.7.js ├── jqBootstrapValidation-1.3.7.min.js └── jqBootstrapValidation.zip ├── libs ├── jquery-loader.js ├── jquery │ ├── jquery-1.8.2.js │ ├── jquery-1.9.1.js │ ├── jquery-1.9.1.min.js │ ├── jquery-migrate.1.2.0.js │ └── jquery.js └── qunit │ ├── qunit.css │ └── qunit.js ├── package.json ├── src └── jqBootstrapValidation.js └── test ├── issues ├── 39 │ ├── 39.html │ └── test.js ├── 47 │ ├── 47.html │ └── test.js └── 50 │ ├── 50.html │ └── test.js ├── jqBootstrapValidation.html ├── jqBootstrapValidation_helpers.js ├── jqBootstrapValidation_test.js └── notravis └── 67 ├── 67.html └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | tmp/ 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | before_script: 5 | - npm install -g grunt-cli 6 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Important notes 4 | Please don't edit files in the `dist` subdirectory as they are generated via grunt. You'll find source code in the `src` subdirectory! 5 | 6 | ## Modifying the code 7 | First, ensure that you have the latest [Node.js](http://nodejs.org/) and [npm](http://npmjs.org/) installed. 8 | 9 | Test that grunt is installed globally by running `grunt --version` at the command-line. If grunt isn't installed globally, run `npm install -g grunt` to install the latest version. _You may need to run `sudo npm install -g grunt`._ 10 | 11 | _Note that in Windows, you may have to run `grunt.cmd` instead of `grunt`._ 12 | 13 | 1. Fork and clone the repo. 14 | 1. Run `npm install` to install all dependencies (including grunt). 15 | 1. Run `grunt` to grunt this project. 16 | 17 | Assuming that you don't see any red, you're ready to go. Just be sure to run `grunt` after making any changes, to ensure that nothing is broken. 18 | 19 | ## Submitting pull requests 20 | 21 | 1. Create a new branch, please don't work in your `master` branch directly. 22 | 1. Add failing tests for the change you want to make. Run `grunt` to see the tests fail. 23 | 1. Fix stuff. 24 | 1. Run `grunt` to see if the tests pass. Repeat steps 2-4 until done. 25 | 1. Open `test/*.html` unit test file(s) in actual browser to ensure tests pass everywhere. 26 | 1. Update the documentation to reflect any changes. 27 | 1. Push to your fork and submit a pull request. 28 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | /*global module:false*/ 2 | module.exports = function(grunt) { 3 | 4 | grunt.loadNpmTasks('grunt-contrib-concat'); 5 | grunt.loadNpmTasks('grunt-contrib-compress'); 6 | grunt.loadNpmTasks('grunt-contrib-clean'); 7 | grunt.loadNpmTasks('grunt-contrib-uglify'); 8 | grunt.loadNpmTasks('grunt-contrib-watch'); 9 | grunt.loadNpmTasks('grunt-contrib-qunit'); 10 | grunt.loadNpmTasks('grunt-contrib-jshint'); 11 | 12 | // Project configuration. 13 | grunt.initConfig({ 14 | pkg: grunt.file.readJSON('package.json'), 15 | meta: { 16 | banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' + 17 | '<%= grunt.template.today("yyyy-mm-dd") %>\n' + 18 | '<%= pkg.homepage ? "* " + pkg.homepage + "\\n" : "" %>' + 19 | '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' + 20 | ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */\n', 21 | component: { 22 | name: '<%= pkg.name %>', 23 | version: '<%= pkg.version %>', 24 | main: ['./dist/<%= pkg.name %>-<%= pkg.version %>.min.js'], 25 | dependencies: { 26 | jquery: '*' 27 | } 28 | }, 29 | }, 30 | concat: { 31 | dist: { 32 | options: { 33 | banner: '<%= meta.banner %>', 34 | stripBanners: true 35 | }, 36 | files: { 37 | 'dist/<%= pkg.name %>-<%= pkg.version %>.js': 'src/<%= pkg.name %>.js' 38 | } 39 | } 40 | }, 41 | uglify: { 42 | dist: { 43 | options: { 44 | banner: '<%= meta.banner %>', 45 | }, 46 | files: { 47 | 'dist/<%= pkg.name %>-<%= pkg.version %>.min.js': 'dist/<%= pkg.name %>-<%= pkg.version %>.js' 48 | } 49 | } 50 | }, 51 | qunit: { 52 | local: ['test/notravis/**/*.html'], 53 | travis: ['test/issues/**/*.html', 'test/jqBootstrapValidation.html'] 54 | }, 55 | watch: { 56 | files: '<%= jshint.files %>', 57 | tasks: ['jshint', 'qunit'] 58 | }, 59 | jshint: { 60 | options: { 61 | curly: true, 62 | eqeqeq: true, 63 | immed: true, 64 | latedef: true, 65 | newcap: true, 66 | noarg: true, 67 | sub: true, 68 | undef: true, 69 | boss: true, 70 | eqnull: true, 71 | browser: true, 72 | globals: { 73 | jQuery: true 74 | }, 75 | }, 76 | files: ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js'] 77 | }, 78 | compress: { 79 | dist: { 80 | options: { 81 | archive: 'dist/<%= pkg.name %>.zip' 82 | }, 83 | expand: true, 84 | src: 'dist/*.js', 85 | flatten: true 86 | } 87 | }, 88 | clean: { 89 | folder: "dist/" 90 | } 91 | }); 92 | 93 | // Default task. 94 | grunt.registerTask('default', ['jshint', 'qunit', 'clean', 'concat', 'uglify', 'compress']); 95 | 96 | // Travis CI task. 97 | grunt.registerTask('travis', ['jshint', 'qunit:travis']); 98 | 99 | grunt.registerTask('component', 'Buld component.json', function () { 100 | var opts = grunt.config('meta.component'); 101 | 102 | grunt.file.write('component.json', JSON.stringify(opts, true, 2) + '\n'); 103 | }); 104 | 105 | }; 106 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 David Godfrey 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | jqBootstapValidation 2 | ==================== 3 | 4 | A JQuery validation framework for bootstrap forms. 5 | 6 | Displays validation errors in `help-block` elements as users type. 7 | 8 | [![Build Status](https://travis-ci.org/ReactiveRaven/jqBootstrapValidation.png?branch=master)](https://travis-ci.org/ReactiveRaven/jqBootstrapValidation) 9 | 10 | How to use? 11 | ----------- 12 | The documentation is [here](https://reactiveraven.github.io/jqBootstrapValidation/). 13 | 14 | Developers 15 | ------------------ 16 | Want to contribute? Great! Check out [CONTRIBUTING.md] 17 | 18 | 19 | 20 | 21 | [![endorse](http://api.coderwall.com/reactiveraven/endorsecount.png)](http://coderwall.com/reactiveraven) 22 | -------------------------------------------------------------------------------- /component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jqBootstrapValidation", 3 | "version": "1.3.7", 4 | "main": ["./dist/jqBootstrapValidation-1.3.7.min.js"], 5 | "dependencies": { 6 | "jquery": "*" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /dist/jqBootstrapValidation-1.3.7.js: -------------------------------------------------------------------------------- 1 | /*! jqBootstrapValidation - v1.3.7 - 2013-05-07 2 | * http://reactiveraven.github.com/jqBootstrapValidation 3 | * Copyright (c) 2013 David Godfrey; Licensed MIT */ 4 | 5 | (function( $ ){ 6 | 7 | var createdElements = []; 8 | 9 | var defaults = { 10 | options: { 11 | prependExistingHelpBlock: false, 12 | sniffHtml: true, // sniff for 'required', 'maxlength', etc 13 | preventSubmit: true, // stop the form submit event from firing if validation fails 14 | submitError: false, // function called if there is an error when trying to submit 15 | submitSuccess: false, // function called just before a successful submit event is sent to the server 16 | semanticallyStrict: false, // set to true to tidy up generated HTML output 17 | bindEvents: [], 18 | autoAdd: { 19 | helpBlocks: true 20 | }, 21 | filter: function () { 22 | // return $(this).is(":visible"); // only validate elements you can see 23 | return true; // validate everything 24 | } 25 | }, 26 | methods: { 27 | init : function( options ) { 28 | 29 | // Get a clean copy of the defaults for extending 30 | var settings = $.extend(true, {}, defaults); 31 | // Set up the options based on the input 32 | settings.options = $.extend(true, settings.options, options); 33 | 34 | var $siblingElements = this; 35 | 36 | var uniqueForms = $.unique( 37 | $siblingElements.map( function () { 38 | return $(this).parents("form")[0]; 39 | }).toArray() 40 | ); 41 | 42 | $(uniqueForms).bind("submit.validationSubmit", function (e) { 43 | var $form = $(this); 44 | var warningsFound = 0; 45 | // Get all inputs 46 | var $allInputs = $form.find("input,textarea,select").not("[type=submit],[type=image]").filter(settings.options.filter); 47 | var $allControlGroups = $form.find(".control-group"); 48 | 49 | // Only trigger validation on the ones that actually _have_ validation 50 | var $inputsWithValidators = $allInputs.filter(function () { 51 | return $(this).triggerHandler("getValidatorCount.validation") > 0; 52 | }); 53 | $inputsWithValidators.trigger("submit.validation"); 54 | 55 | // But all of them are out-of-focus now, because we're submitting. 56 | $allInputs.trigger("validationLostFocus.validation"); 57 | 58 | // Okay, now check each controlgroup for errors (or warnings) 59 | $allControlGroups.each(function (i, el) { 60 | var $controlGroup = $(el); 61 | if ($controlGroup.hasClass("warning") || $controlGroup.hasClass("error")) { 62 | $controlGroup.removeClass("warning").addClass("error"); 63 | warningsFound++; 64 | } 65 | }); 66 | 67 | if (warningsFound) { 68 | // If we found any warnings, maybe we should prevent the submit 69 | // event, and trigger 'submitError' (if they're set up) 70 | if (settings.options.preventSubmit) { 71 | e.preventDefault(); 72 | e.stopImmediatePropagation(); 73 | } 74 | $form.addClass("error"); 75 | if ($.isFunction(settings.options.submitError)) { 76 | settings.options.submitError($form, e, $inputsWithValidators.jqBootstrapValidation("collectErrors", true)); 77 | } 78 | } else { 79 | // Woo! No errors! We can pass the submit event to submitSuccess 80 | // (if it has been set up) 81 | $form.removeClass("error"); 82 | if ($.isFunction(settings.options.submitSuccess)) { 83 | settings.options.submitSuccess($form, e); 84 | } 85 | } 86 | }); 87 | 88 | return this.each(function(){ 89 | 90 | // Get references to everything we're interested in 91 | var $this = $(this), 92 | $controlGroup = $this.parents(".control-group").first(), 93 | $helpBlock = $controlGroup.find(".help-block").first(), 94 | $form = $this.parents("form").first(), 95 | validatorNames = []; 96 | 97 | // create message container if not exists 98 | if (!$helpBlock.length && settings.options.autoAdd && settings.options.autoAdd.helpBlocks) { 99 | $helpBlock = $('
'); 100 | $controlGroup.find('.controls').append($helpBlock); 101 | createdElements.push($helpBlock[0]); 102 | } 103 | 104 | // ============================================================= 105 | // SNIFF HTML FOR VALIDATORS 106 | // ============================================================= 107 | 108 | // *snort sniff snuffle* 109 | 110 | if (settings.options.sniffHtml) { 111 | var message; 112 | // --------------------------------------------------------- 113 | // PATTERN 114 | // --------------------------------------------------------- 115 | if ($this.data("validationPatternPattern")) { 116 | $this.attr("pattern", $this.data("validationPatternPattern")); 117 | } 118 | if ($this.attr("pattern") !== undefined) { 119 | message = "Not in the expected format"; 120 | if ($this.data("validationPatternMessage")) { 121 | message = $this.data("validationPatternMessage"); 122 | } 123 | $this.data("validationPatternMessage", message); 124 | $this.data("validationPatternRegex", $this.attr("pattern")); 125 | } 126 | // --------------------------------------------------------- 127 | // MAX 128 | // --------------------------------------------------------- 129 | if ($this.attr("max") !== undefined || $this.attr("aria-valuemax") !== undefined) { 130 | var max = ($this.attr("max") !== undefined ? $this.attr("max") : $this.attr("aria-valuemax")); 131 | message = "Too high: Maximum of '" + max + "'"; 132 | if ($this.data("validationMaxMessage")) { 133 | message = $this.data("validationMaxMessage"); 134 | } 135 | $this.data("validationMaxMessage", message); 136 | $this.data("validationMaxMax", max); 137 | } 138 | // --------------------------------------------------------- 139 | // MIN 140 | // --------------------------------------------------------- 141 | if ($this.attr("min") !== undefined || $this.attr("aria-valuemin") !== undefined) { 142 | var min = ($this.attr("min") !== undefined ? $this.attr("min") : $this.attr("aria-valuemin")); 143 | message = "Too low: Minimum of '" + min + "'"; 144 | if ($this.data("validationMinMessage")) { 145 | message = $this.data("validationMinMessage"); 146 | } 147 | $this.data("validationMinMessage", message); 148 | $this.data("validationMinMin", min); 149 | } 150 | // --------------------------------------------------------- 151 | // MAXLENGTH 152 | // --------------------------------------------------------- 153 | if ($this.attr("maxlength") !== undefined) { 154 | message = "Too long: Maximum of '" + $this.attr("maxlength") + "' characters"; 155 | if ($this.data("validationMaxlengthMessage")) { 156 | message = $this.data("validationMaxlengthMessage"); 157 | } 158 | $this.data("validationMaxlengthMessage", message); 159 | $this.data("validationMaxlengthMaxlength", $this.attr("maxlength")); 160 | } 161 | // --------------------------------------------------------- 162 | // MINLENGTH 163 | // --------------------------------------------------------- 164 | if ($this.attr("minlength") !== undefined) { 165 | message = "Too short: Minimum of '" + $this.attr("minlength") + "' characters"; 166 | if ($this.data("validationMinlengthMessage")) { 167 | message = $this.data("validationMinlengthMessage"); 168 | } 169 | $this.data("validationMinlengthMessage", message); 170 | $this.data("validationMinlengthMinlength", $this.attr("minlength")); 171 | } 172 | // --------------------------------------------------------- 173 | // REQUIRED 174 | // --------------------------------------------------------- 175 | if ($this.attr("required") !== undefined || $this.attr("aria-required") !== undefined) { 176 | message = settings.builtInValidators.required.message; 177 | if ($this.data("validationRequiredMessage")) { 178 | message = $this.data("validationRequiredMessage"); 179 | } 180 | $this.data("validationRequiredMessage", message); 181 | } 182 | // --------------------------------------------------------- 183 | // NUMBER 184 | // --------------------------------------------------------- 185 | if ($this.attr("type") !== undefined && $this.attr("type").toLowerCase() === "number") { 186 | message = settings.validatorTypes.number.message; // TODO: fix this 187 | if ($this.data("validationNumberMessage")) { 188 | message = $this.data("validationNumberMessage"); 189 | } 190 | $this.data("validationNumberMessage", message); 191 | 192 | var step = settings.validatorTypes.number.step; // TODO: and this 193 | if ($this.data("validationNumberStep")) { 194 | step = $this.data("validationNumberStep"); 195 | } 196 | $this.data("validationNumberStep", step); 197 | 198 | var decimal = settings.validatorTypes.number.decimal; 199 | if ($this.data("validationNumberDecimal")) { 200 | decimal = $this.data("validationNumberDecimal"); 201 | } 202 | $this.data("validationNumberDecimal", decimal); 203 | } 204 | // --------------------------------------------------------- 205 | // EMAIL 206 | // --------------------------------------------------------- 207 | if ($this.attr("type") !== undefined && $this.attr("type").toLowerCase() === "email") { 208 | message = "Not a valid email address"; 209 | if ($this.data("validationEmailMessage")) { 210 | message = $this.data("validationEmailMessage"); 211 | } 212 | $this.data("validationEmailMessage", message); 213 | } 214 | // --------------------------------------------------------- 215 | // MINCHECKED 216 | // --------------------------------------------------------- 217 | if ($this.attr("minchecked") !== undefined) { 218 | message = "Not enough options checked; Minimum of '" + $this.attr("minchecked") + "' required"; 219 | if ($this.data("validationMincheckedMessage")) { 220 | message = $this.data("validationMincheckedMessage"); 221 | } 222 | $this.data("validationMincheckedMessage", message); 223 | $this.data("validationMincheckedMinchecked", $this.attr("minchecked")); 224 | } 225 | // --------------------------------------------------------- 226 | // MAXCHECKED 227 | // --------------------------------------------------------- 228 | if ($this.attr("maxchecked") !== undefined) { 229 | message = "Too many options checked; Maximum of '" + $this.attr("maxchecked") + "' required"; 230 | if ($this.data("validationMaxcheckedMessage")) { 231 | message = $this.data("validationMaxcheckedMessage"); 232 | } 233 | $this.data("validationMaxcheckedMessage", message); 234 | $this.data("validationMaxcheckedMaxchecked", $this.attr("maxchecked")); 235 | } 236 | } 237 | 238 | // ============================================================= 239 | // COLLECT VALIDATOR NAMES 240 | // ============================================================= 241 | 242 | // Get named validators 243 | if ($this.data("validation") !== undefined) { 244 | validatorNames = $this.data("validation").split(","); 245 | } 246 | 247 | // Get extra ones defined on the element's data attributes 248 | $.each($this.data(), function (i, el) { 249 | var parts = i.replace(/([A-Z])/g, ",$1").split(","); 250 | if (parts[0] === "validation" && parts[1]) { 251 | validatorNames.push(parts[1]); 252 | } 253 | }); 254 | 255 | // ============================================================= 256 | // NORMALISE VALIDATOR NAMES 257 | // ============================================================= 258 | 259 | var validatorNamesToInspect = validatorNames; 260 | var newValidatorNamesToInspect = []; 261 | 262 | var uppercaseEachValidatorName = function (i, el) { 263 | validatorNames[i] = formatValidatorName(el); 264 | }; 265 | 266 | var inspectValidators = function(i, el) { 267 | if ($this.data("validation" + el + "Shortcut") !== undefined) { 268 | // Are these custom validators? 269 | // Pull them out! 270 | $.each($this.data("validation" + el + "Shortcut").split(","), function(i2, el2) { 271 | newValidatorNamesToInspect.push(el2); 272 | }); 273 | } else if (settings.builtInValidators[el.toLowerCase()]) { 274 | // Is this a recognised built-in? 275 | // Pull it out! 276 | var validator = settings.builtInValidators[el.toLowerCase()]; 277 | if (validator.type.toLowerCase() === "shortcut") { 278 | $.each(validator.shortcut.split(","), function (i, el) { 279 | el = formatValidatorName(el); 280 | newValidatorNamesToInspect.push(el); 281 | validatorNames.push(el); 282 | }); 283 | } 284 | } 285 | }; 286 | 287 | do // repeatedly expand 'shortcut' validators into their real validators 288 | { 289 | // Uppercase only the first letter of each name 290 | $.each(validatorNames, uppercaseEachValidatorName); 291 | 292 | // Remove duplicate validator names 293 | validatorNames = $.unique(validatorNames); 294 | 295 | // Pull out the new validator names from each shortcut 296 | newValidatorNamesToInspect = []; 297 | $.each(validatorNamesToInspect, inspectValidators); 298 | 299 | validatorNamesToInspect = newValidatorNamesToInspect; 300 | 301 | } while (validatorNamesToInspect.length > 0); 302 | 303 | // ============================================================= 304 | // SET UP VALIDATOR ARRAYS 305 | // ============================================================= 306 | 307 | /* We're gonna generate something like 308 | * 309 | * { 310 | * "regex": [ 311 | * { -- a validator object here --}, 312 | * { -- a validator object here --} 313 | * ], 314 | * "required": [ 315 | * { -- a validator object here --}, 316 | * { -- a validator object here --} 317 | * ] 318 | * } 319 | * 320 | * with a few more entries. 321 | * 322 | * Because we only add a few validators to each field, most of the 323 | * keys will be empty arrays with no validator objects in them, and 324 | * thats fine. 325 | */ 326 | 327 | var validators = {}; 328 | 329 | $.each(validatorNames, function (i, el) { 330 | // Set up the 'override' message 331 | var message = $this.data("validation" + el + "Message"); 332 | var hasOverrideMessage = !!message; 333 | var foundValidator = false; 334 | if (!message) { 335 | message = "'" + el + "' validation failed "; 336 | } 337 | 338 | $.each( 339 | settings.validatorTypes, 340 | function (validatorType, validatorTemplate) { 341 | if (validators[validatorType] === undefined) { 342 | validators[validatorType] = []; 343 | } 344 | if (!foundValidator && $this.data("validation" + el + formatValidatorName(validatorTemplate.name)) !== undefined) { 345 | var initted = validatorTemplate.init($this, el); 346 | if (hasOverrideMessage) { 347 | initted.message = message; 348 | } 349 | 350 | validators[validatorType].push( 351 | $.extend( 352 | true, 353 | { 354 | name: formatValidatorName(validatorTemplate.name), 355 | message: message 356 | }, 357 | initted 358 | ) 359 | ); 360 | foundValidator = true; 361 | } 362 | } 363 | ); 364 | 365 | if (!foundValidator && settings.builtInValidators[el.toLowerCase()]) { 366 | 367 | var validator = $.extend(true, {}, settings.builtInValidators[el.toLowerCase()]); 368 | if (hasOverrideMessage) { 369 | validator.message = message; 370 | } 371 | var validatorType = validator.type.toLowerCase(); 372 | 373 | if (validatorType === "shortcut") { 374 | foundValidator = true; 375 | } else { 376 | $.each( 377 | settings.validatorTypes, 378 | function (validatorTemplateType, validatorTemplate) { 379 | if (validators[validatorTemplateType] === undefined) { 380 | validators[validatorTemplateType] = []; 381 | } 382 | if (!foundValidator && validatorType === validatorTemplateType.toLowerCase()) { 383 | $this.data( 384 | "validation" + el + formatValidatorName(validatorTemplate.name), 385 | validator[validatorTemplate.name.toLowerCase()] 386 | ); 387 | validators[validatorType].push( 388 | $.extend( 389 | validator, 390 | validatorTemplate.init($this, el) 391 | ) 392 | ); 393 | foundValidator = true; 394 | } 395 | } 396 | ); 397 | } 398 | } 399 | 400 | if (! foundValidator) { 401 | $.error("Cannot find validation info for '" + el + "'"); 402 | } 403 | }); 404 | 405 | // ============================================================= 406 | // STORE FALLBACK VALUES 407 | // ============================================================= 408 | 409 | $helpBlock.data( 410 | "original-contents", 411 | ( 412 | $helpBlock.data("original-contents") ? 413 | $helpBlock.data("original-contents") : 414 | $helpBlock.html() 415 | ) 416 | ); 417 | 418 | $helpBlock.data( 419 | "original-role", 420 | ( 421 | $helpBlock.data("original-role") ? 422 | $helpBlock.data("original-role") : 423 | $helpBlock.attr("role") 424 | ) 425 | ); 426 | 427 | $controlGroup.data( 428 | "original-classes", 429 | ( 430 | $controlGroup.data("original-clases") ? 431 | $controlGroup.data("original-classes") : 432 | $controlGroup.attr("class") 433 | ) 434 | ); 435 | 436 | $this.data( 437 | "original-aria-invalid", 438 | ( 439 | $this.data("original-aria-invalid") ? 440 | $this.data("original-aria-invalid") : 441 | $this.attr("aria-invalid") 442 | ) 443 | ); 444 | 445 | // ============================================================= 446 | // VALIDATION 447 | // ============================================================= 448 | 449 | $this.bind( 450 | "validation.validation", 451 | function (event, params) { 452 | 453 | var value = getValue($this); 454 | 455 | // Get a list of the errors to apply 456 | var errorsFound = []; 457 | 458 | $.each(validators, function (validatorType, validatorTypeArray) { 459 | if ( 460 | value || // has a truthy value 461 | value.length || // not an empty string 462 | ( // am including empty values 463 | ( 464 | params && 465 | params.includeEmpty 466 | ) || 467 | !!settings.validatorTypes[validatorType].includeEmpty 468 | ) || 469 | ( // validator is blocking submit 470 | !!settings.validatorTypes[validatorType].blockSubmit && 471 | params && 472 | !!params.submitting 473 | ) 474 | ) 475 | { 476 | $.each( 477 | validatorTypeArray, 478 | function (i, validator) { 479 | if (settings.validatorTypes[validatorType].validate($this, value, validator)) { 480 | errorsFound.push(validator.message); 481 | } 482 | } 483 | ); 484 | } 485 | }); 486 | 487 | return errorsFound; 488 | } 489 | ); 490 | 491 | $this.bind( 492 | "getValidators.validation", 493 | function () { 494 | return validators; 495 | } 496 | ); 497 | 498 | var numValidators = 0; 499 | 500 | $.each(validators, function (i, el) { 501 | numValidators += el.length; 502 | }); 503 | 504 | $this.bind("getValidatorCount.validation", function () { 505 | return numValidators; 506 | }); 507 | 508 | // ============================================================= 509 | // WATCH FOR CHANGES 510 | // ============================================================= 511 | $this.bind( 512 | "submit.validation", 513 | function () { 514 | return $this.triggerHandler("change.validation", {submitting: true}); 515 | } 516 | ); 517 | $this.bind( 518 | ( 519 | settings.options.bindEvents.length > 0 ? 520 | settings.options.bindEvents : 521 | [ 522 | "keyup", 523 | "focus", 524 | "blur", 525 | "click", 526 | "keydown", 527 | "keypress", 528 | "change" 529 | ] 530 | ).concat(["revalidate"]).join(".validation ") + ".validation", 531 | function (e, params) { 532 | 533 | var value = getValue($this); 534 | 535 | var errorsFound = []; 536 | 537 | if (params && !!params.submitting) { 538 | $controlGroup.data("jqbvIsSubmitting", true); 539 | } else if (e.type !== "revalidate") { 540 | $controlGroup.data("jqbvIsSubmitting", false); 541 | } 542 | 543 | var formIsSubmitting = !!$controlGroup.data("jqbvIsSubmitting"); 544 | 545 | $controlGroup.find("input,textarea,select").each(function (i, el) { 546 | var oldCount = errorsFound.length; 547 | $.each($(el).triggerHandler("validation.validation", params), function (j, message) { 548 | errorsFound.push(message); 549 | }); 550 | if (errorsFound.length > oldCount) { 551 | $(el).attr("aria-invalid", "true"); 552 | } else { 553 | var original = $this.data("original-aria-invalid"); 554 | $(el).attr("aria-invalid", (original !== undefined ? original : false)); 555 | } 556 | }); 557 | 558 | $form.find("input,select,textarea").not($this).not("[name=\"" + $this.attr("name") + "\"]").trigger("validationLostFocus.validation"); 559 | 560 | errorsFound = $.unique(errorsFound.sort()); 561 | 562 | // Were there any errors? 563 | if (errorsFound.length) { 564 | // Better flag it up as a warning. 565 | $controlGroup.removeClass("success error warning").addClass(formIsSubmitting ? "error" : "warning"); 566 | 567 | // How many errors did we find? 568 | if (settings.options.semanticallyStrict && errorsFound.length === 1) { 569 | // Only one? Being strict? Just output it. 570 | $helpBlock.html(errorsFound[0] + 571 | ( settings.options.prependExistingHelpBlock ? $helpBlock.data("original-contents") : "" )); 572 | } else { 573 | // Multiple? Being sloppy? Glue them together into an UL. 574 | $helpBlock.html("" + 575 | ( settings.options.prependExistingHelpBlock ? $helpBlock.data("original-contents") : "" )); 576 | } 577 | } else { 578 | $controlGroup.removeClass("warning error success"); 579 | if (value.length > 0) { 580 | $controlGroup.addClass("success"); 581 | } 582 | $helpBlock.html($helpBlock.data("original-contents")); 583 | } 584 | 585 | if (e.type === "blur") { 586 | $controlGroup.removeClass("success"); 587 | } 588 | } 589 | ); 590 | $this.bind("validationLostFocus.validation", function () { 591 | $controlGroup.removeClass("success"); 592 | }); 593 | }); 594 | }, 595 | destroy : function( ) { 596 | 597 | return this.each( 598 | function() { 599 | 600 | var 601 | $this = $(this), 602 | $controlGroup = $this.parents(".control-group").first(), 603 | $helpBlock = $controlGroup.find(".help-block").first(), 604 | $form = $this.parents("form").first(); 605 | 606 | // remove our events 607 | $this.unbind('.validation'); // events are namespaced. 608 | $form.unbind(".validationSubmit"); 609 | // reset help text 610 | $helpBlock.html($helpBlock.data("original-contents")); 611 | // reset classes 612 | $controlGroup.attr("class", $controlGroup.data("original-classes")); 613 | // reset aria 614 | $this.attr("aria-invalid", $this.data("original-aria-invalid")); 615 | // reset role 616 | $helpBlock.attr("role", $this.data("original-role")); 617 | // remove all elements we created 618 | if ($.inArray($helpBlock[0], createdElements) > -1) { 619 | $helpBlock.remove(); 620 | } 621 | 622 | } 623 | ); 624 | 625 | }, 626 | collectErrors : function(includeEmpty) { 627 | 628 | var errorMessages = {}; 629 | this.each(function (i, el) { 630 | var $el = $(el); 631 | var name = $el.attr("name"); 632 | var errors = $el.triggerHandler("validation.validation", {includeEmpty: true}); 633 | errorMessages[name] = $.extend(true, errors, errorMessages[name]); 634 | }); 635 | 636 | $.each(errorMessages, function (i, el) { 637 | if (el.length === 0) { 638 | delete errorMessages[i]; 639 | } 640 | }); 641 | 642 | return errorMessages; 643 | 644 | }, 645 | hasErrors: function() { 646 | 647 | var errorMessages = []; 648 | 649 | this.find('input,select,textarea').add(this).each(function (i, el) { 650 | errorMessages = errorMessages.concat( 651 | $(el).triggerHandler("getValidators.validation") ? $(el).triggerHandler("validation.validation", {submitting: true}) : [] 652 | ); 653 | }); 654 | 655 | return (errorMessages.length > 0); 656 | }, 657 | override : function (newDefaults) { 658 | defaults = $.extend(true, defaults, newDefaults); 659 | } 660 | }, 661 | validatorTypes: { 662 | callback: { 663 | name: "callback", 664 | init: function($this, name) { 665 | var result = { 666 | validatorName: name, 667 | callback: $this.data("validation" + name + "Callback"), 668 | lastValue: $this.val(), 669 | lastValid: true, 670 | lastFinished: true 671 | }; 672 | 673 | var message = "Not valid"; 674 | if ($this.data("validation" + name + "Message")) { 675 | message = $this.data("validation" + name + "Message"); 676 | } 677 | result.message = message; 678 | 679 | return result; 680 | }, 681 | validate: function($this, value, validator) { 682 | if (validator.lastValue === value && validator.lastFinished) { 683 | return !validator.lastValid; 684 | } 685 | 686 | if (validator.lastFinished === true) 687 | { 688 | validator.lastValue = value; 689 | validator.lastValid = true; 690 | validator.lastFinished = false; 691 | 692 | var rrjqbvValidator = validator; 693 | var rrjqbvThis = $this; 694 | executeFunctionByName( 695 | validator.callback, 696 | window, 697 | $this, 698 | value, 699 | function(data) { 700 | if (rrjqbvValidator.lastValue === data.value) { 701 | rrjqbvValidator.lastValid = data.valid; 702 | if (data.message) { 703 | rrjqbvValidator.message = data.message; 704 | } 705 | rrjqbvValidator.lastFinished = true; 706 | rrjqbvThis.data( 707 | "validation" + rrjqbvValidator.validatorName + "Message", 708 | rrjqbvValidator.message 709 | ); 710 | 711 | // Timeout is set to avoid problems with the events being considered 'already fired' 712 | setTimeout(function() { 713 | if (!$this.is(":focus") && $this.parents("form").first().data("jqbvIsSubmitting")) { 714 | rrjqbvThis.trigger("blur.validation"); 715 | } else { 716 | rrjqbvThis.trigger("revalidate.validation"); 717 | } 718 | }, 1); // doesn't need a long timeout, just long enough for the event bubble to burst 719 | } 720 | } 721 | ); 722 | } 723 | 724 | return false; 725 | 726 | } 727 | }, 728 | ajax: { 729 | name: "ajax", 730 | init: function ($this, name) { 731 | return { 732 | validatorName: name, 733 | url: $this.data("validation" + name + "Ajax"), 734 | lastValue: $this.val(), 735 | lastValid: true, 736 | lastFinished: true 737 | }; 738 | }, 739 | validate: function ($this, value, validator) { 740 | if (""+validator.lastValue === ""+value && validator.lastFinished === true) { 741 | return validator.lastValid === false; 742 | } 743 | 744 | if (validator.lastFinished === true) 745 | { 746 | validator.lastValue = value; 747 | validator.lastValid = true; 748 | validator.lastFinished = false; 749 | $.ajax({ 750 | url: validator.url, 751 | data: "value=" + encodeURIComponent(value) + "&field=" + $this.attr("name"), 752 | dataType: "json", 753 | success: function (data) { 754 | if (""+validator.lastValue === ""+data.value) { 755 | validator.lastValid = !!(data.valid); 756 | if (data.message) { 757 | validator.message = data.message; 758 | } 759 | validator.lastFinished = true; 760 | $this.data("validation" + validator.validatorName + "Message", validator.message); 761 | // Timeout is set to avoid problems with the events being considered 'already fired' 762 | setTimeout(function () { 763 | $this.trigger("revalidate.validation"); 764 | }, 1); // doesn't need a long timeout, just long enough for the event bubble to burst 765 | } 766 | }, 767 | failure: function () { 768 | validator.lastValid = true; 769 | validator.message = "ajax call failed"; 770 | validator.lastFinished = true; 771 | $this.data("validation" + validator.validatorName + "Message", validator.message); 772 | // Timeout is set to avoid problems with the events being considered 'already fired' 773 | setTimeout(function () { 774 | $this.trigger("revalidate.validation"); 775 | }, 1); // doesn't need a long timeout, just long enough for the event bubble to burst 776 | } 777 | }); 778 | } 779 | 780 | return false; 781 | 782 | } 783 | }, 784 | regex: { 785 | name: "regex", 786 | init: function ($this, name) { 787 | var result = {}; 788 | var regexString = $this.data("validation" + name + "Regex"); 789 | result.regex = regexFromString(regexString); 790 | if (regexString === undefined) { 791 | $.error("Can't find regex for '" + name + "' validator on '" + $this.attr("name") + "'"); 792 | } 793 | 794 | var message = "Not in the expected format"; 795 | if ($this.data("validation" + name + "Message")) { 796 | message = $this.data("validation" + name + "Message"); 797 | } 798 | 799 | result.message = message; 800 | 801 | result.originalName = name; 802 | return result; 803 | }, 804 | validate: function ($this, value, validator) { 805 | return (!validator.regex.test(value) && ! validator.negative) || 806 | (validator.regex.test(value) && validator.negative); 807 | } 808 | }, 809 | email: { 810 | name: "email", 811 | init: function ($this, name) { 812 | var result = {}; 813 | result.regex = regexFromString('[a-zA-Z0-9.!#$%&\u2019*+/=?^_`{|}~-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}'); 814 | 815 | var message = "Not a valid email address"; 816 | if ($this.data("validation" + name + "Message")) { 817 | message = $this.data("validation" + name + "Message"); 818 | } 819 | 820 | result.message = message; 821 | 822 | result.originalName = name; 823 | return result; 824 | }, 825 | validate: function ($this, value, validator) { 826 | return (!validator.regex.test(value) && ! validator.negative) || 827 | (validator.regex.test(value) && validator.negative); 828 | } 829 | }, 830 | required: { 831 | name: "required", 832 | init: function ($this, name) { 833 | var message = "This is required"; 834 | if ($this.data("validation" + name + "Message")) { 835 | message = $this.data("validation" + name + "Message"); 836 | } 837 | 838 | return {message: message, includeEmpty: true}; 839 | }, 840 | validate: function ($this, value, validator) { 841 | return !!( 842 | (value.length === 0 && !validator.negative) || 843 | (value.length > 0 && validator.negative) 844 | ); 845 | }, 846 | blockSubmit: true 847 | }, 848 | match: { 849 | name: "match", 850 | init: function ($this, name) { 851 | var elementName = $this.data("validation" + name + "Match"); 852 | var $form = $this.parents("form").first(); 853 | var $element = $form.find("[name=\"" + elementName + "\"]").first(); 854 | $element.bind("validation.validation", function () { 855 | $this.trigger("revalidate.validation", {submitting: true}); 856 | }); 857 | var result = {}; 858 | result.element = $element; 859 | 860 | if ($element.length === 0) { 861 | $.error("Can't find field '" + elementName + "' to match '" + $this.attr("name") + "' against in '" + name + "' validator"); 862 | } 863 | 864 | var message = "Must match"; 865 | var $label = null; 866 | if (($label = $form.find("label[for=\"" + elementName + "\"]")).length) { 867 | message += " '" + $label.text() + "'"; 868 | } else if (($label = $element.parents(".control-group").first().find("label")).length) { 869 | message += " '" + $label.first().text() + "'"; 870 | } 871 | 872 | if ($this.data("validation" + name + "Message")) { 873 | message = $this.data("validation" + name + "Message"); 874 | } 875 | 876 | result.message = message; 877 | 878 | return result; 879 | }, 880 | validate: function ($this, value, validator) { 881 | return (value !== validator.element.val() && ! validator.negative) || 882 | (value === validator.element.val() && validator.negative); 883 | }, 884 | blockSubmit: true, 885 | includeEmpty: true 886 | }, 887 | max: { 888 | name: "max", 889 | init: function ($this, name) { 890 | var result = {}; 891 | 892 | result.max = $this.data("validation" + name + "Max"); 893 | 894 | result.message = "Too high: Maximum of '" + result.max + "'"; 895 | if ($this.data("validation" + name + "Message")) { 896 | result.message = $this.data("validation" + name + "Message"); 897 | } 898 | 899 | return result; 900 | }, 901 | validate: function ($this, value, validator) { 902 | return (parseFloat(value, 10) > parseFloat(validator.max, 10) && ! validator.negative) || 903 | (parseFloat(value, 10) <= parseFloat(validator.max, 10) && validator.negative); 904 | } 905 | }, 906 | min: { 907 | name: "min", 908 | init: function ($this, name) { 909 | var result = {}; 910 | 911 | result.min = $this.data("validation" + name + "Min"); 912 | 913 | result.message = "Too low: Minimum of '" + result.min + "'"; 914 | if ($this.data("validation" + name + "Message")) { 915 | result.message = $this.data("validation" + name + "Message"); 916 | } 917 | 918 | return result; 919 | }, 920 | validate: function ($this, value, validator) { 921 | return (parseFloat(value) < parseFloat(validator.min) && ! validator.negative) || 922 | (parseFloat(value) >= parseFloat(validator.min) && validator.negative); 923 | } 924 | }, 925 | maxlength: { 926 | name: "maxlength", 927 | init: function ($this, name) { 928 | var result = {}; 929 | 930 | result.maxlength = $this.data("validation" + name + "Maxlength"); 931 | 932 | result.message = "Too long: Maximum of '" + result.maxlength + "' characters"; 933 | if ($this.data("validation" + name + "Message")) { 934 | result.message = $this.data("validation" + name + "Message"); 935 | } 936 | 937 | return result; 938 | }, 939 | validate: function ($this, value, validator) { 940 | return ((value.length > validator.maxlength) && ! validator.negative) || 941 | ((value.length <= validator.maxlength) && validator.negative); 942 | } 943 | }, 944 | minlength: { 945 | name: "minlength", 946 | init: function ($this, name) { 947 | var result = {}; 948 | 949 | result.minlength = $this.data("validation" + name + "Minlength"); 950 | 951 | result.message = "Too short: Minimum of '" + result.minlength + "' characters"; 952 | if ($this.data("validation" + name + "Message")) { 953 | result.message = $this.data("validation" + name + "Message"); 954 | } 955 | 956 | return result; 957 | }, 958 | validate: function ($this, value, validator) { 959 | return ((value.length < validator.minlength) && ! validator.negative) || 960 | ((value.length >= validator.minlength) && validator.negative); 961 | } 962 | }, 963 | maxchecked: { 964 | name: "maxchecked", 965 | init: function ($this, name) { 966 | var result = {}; 967 | 968 | var elements = $this.parents("form").first().find("[name=\"" + $this.attr("name") + "\"]"); 969 | elements.bind("change.validation click.validation", function () { 970 | $this.trigger("revalidate.validation", {includeEmpty: true}); 971 | }); 972 | 973 | result.elements = elements; 974 | result.maxchecked = $this.data("validation" + name + "Maxchecked"); 975 | 976 | var message = "Too many: Max '" + result.maxchecked + "' checked"; 977 | if ($this.data("validation" + name + "Message")) { 978 | message = $this.data("validation" + name + "Message"); 979 | } 980 | result.message = message; 981 | 982 | return result; 983 | }, 984 | validate: function ($this, value, validator) { 985 | return (validator.elements.filter(":checked").length > validator.maxchecked && ! validator.negative) || 986 | (validator.elements.filter(":checked").length <= validator.maxchecked && validator.negative); 987 | }, 988 | blockSubmit: true 989 | }, 990 | minchecked: { 991 | name: "minchecked", 992 | init: function ($this, name) { 993 | var result = {}; 994 | 995 | var elements = $this.parents("form").first().find("[name=\"" + $this.attr("name") + "\"]"); 996 | elements.bind("change.validation click.validation", function () { 997 | $this.trigger("revalidate.validation", {includeEmpty: true}); 998 | }); 999 | 1000 | result.elements = elements; 1001 | result.minchecked = $this.data("validation" + name + "Minchecked"); 1002 | 1003 | var message = "Too few: Min '" + result.minchecked + "' checked"; 1004 | if ($this.data("validation" + name + "Message")) { 1005 | message = $this.data("validation" + name + "Message"); 1006 | } 1007 | result.message = message; 1008 | 1009 | return result; 1010 | }, 1011 | validate: function ($this, value, validator) { 1012 | return (validator.elements.filter(":checked").length < validator.minchecked && ! validator.negative) || 1013 | (validator.elements.filter(":checked").length >= validator.minchecked && validator.negative); 1014 | }, 1015 | blockSubmit: true, 1016 | includeEmpty: true 1017 | }, 1018 | number: { 1019 | name: "number", 1020 | init: function ($this, name) { 1021 | var result = {}; 1022 | result.step = 1; 1023 | if ($this.attr("step")) { 1024 | result.step = $this.attr("step"); 1025 | } 1026 | if ($this.data("validation" + name + "Step")) { 1027 | result.step = $this.data("validation" + name + "Step"); 1028 | } 1029 | 1030 | result.decimal = "."; 1031 | if ($this.data("validation" + name + "Decimal")) { 1032 | result.decimal = $this.data("validation" + name + "Decimal"); 1033 | } 1034 | 1035 | result.thousands = ""; 1036 | if ($this.data("validation" + name + "Thousands")) { 1037 | result.thousands = $this.data("validation" + name + "Thousands"); 1038 | } 1039 | 1040 | result.regex = regexFromString("([+-]?\\d+(\\" + result.decimal + "\\d+)?)?"); 1041 | 1042 | result.message = "Must be a number"; 1043 | var dataMessage = $this.data("validation" + name + "Message"); 1044 | if (dataMessage) { 1045 | result.message = dataMessage; 1046 | } 1047 | 1048 | return result; 1049 | }, 1050 | validate: function ($this, value, validator) { 1051 | var globalValue = value.replace(validator.decimal, ".").replace(validator.thousands, ""); 1052 | var multipliedValue = parseFloat(globalValue); 1053 | var multipliedStep = parseFloat(validator.step); 1054 | while (multipliedStep % 1 !== 0) { 1055 | /* thanks to @jkey #57 */ 1056 | multipliedStep = parseFloat(multipliedStep.toPrecision(12)) * 10; 1057 | multipliedValue = parseFloat(multipliedValue.toPrecision(12)) * 10; 1058 | } 1059 | var regexResult = validator.regex.test(value); 1060 | var stepResult = parseFloat(multipliedValue) % parseFloat(multipliedStep) === 0; 1061 | var typeResult = !isNaN(parseFloat(globalValue)) && isFinite(globalValue); 1062 | var result = !(regexResult && stepResult && typeResult); 1063 | return result; 1064 | }, 1065 | message: "Must be a number" 1066 | } 1067 | }, 1068 | builtInValidators: { 1069 | email: { 1070 | name: "Email", 1071 | type: "email" 1072 | }, 1073 | passwordagain: { 1074 | name: "Passwordagain", 1075 | type: "match", 1076 | match: "password", 1077 | message: "Does not match the given password" 1078 | }, 1079 | positive: { 1080 | name: "Positive", 1081 | type: "shortcut", 1082 | shortcut: "number,positivenumber" 1083 | }, 1084 | negative: { 1085 | name: "Negative", 1086 | type: "shortcut", 1087 | shortcut: "number,negativenumber" 1088 | }, 1089 | integer: { 1090 | name: "Integer", 1091 | type: "regex", 1092 | regex: "[+-]?\\d+", 1093 | message: "No decimal places allowed" 1094 | }, 1095 | positivenumber: { 1096 | name: "Positivenumber", 1097 | type: "min", 1098 | min: 0, 1099 | message: "Must be a positive number" 1100 | }, 1101 | negativenumber: { 1102 | name: "Negativenumber", 1103 | type: "max", 1104 | max: 0, 1105 | message: "Must be a negative number" 1106 | }, 1107 | required: { 1108 | name: "Required", 1109 | type: "required", 1110 | message: "This is required" 1111 | }, 1112 | checkone: { 1113 | name: "Checkone", 1114 | type: "minchecked", 1115 | minchecked: 1, 1116 | message: "Check at least one option" 1117 | }, 1118 | number: { 1119 | name: "Number", 1120 | type: "number", 1121 | decimal: ".", 1122 | step: "1" 1123 | }, 1124 | pattern: { 1125 | name: "Pattern", 1126 | type: "regex", 1127 | message: "Not in expected format" 1128 | } 1129 | } 1130 | }; 1131 | 1132 | var formatValidatorName = function (name) { 1133 | return name 1134 | .toLowerCase() 1135 | .replace( 1136 | /(^|\s)([a-z])/g , 1137 | function(m,p1,p2) { 1138 | return p1+p2.toUpperCase(); 1139 | } 1140 | ) 1141 | ; 1142 | }; 1143 | 1144 | var getValue = function ($this) { 1145 | // Extract the value we're talking about 1146 | var value = null; 1147 | var type = $this.attr("type"); 1148 | if (type === "checkbox") { 1149 | value = ($this.is(":checked") ? value : ""); 1150 | var checkboxParent = $this.parents("form").first() || $this.parents(".control-group").first(); 1151 | if (checkboxParent) { 1152 | value = checkboxParent.find("input[name='" + $this.attr("name") + "']:checked").map(function (i, el) { return $(el).val(); }).toArray().join(","); 1153 | } 1154 | } 1155 | else if (type === "radio") { 1156 | value = ($('input[name="' + $this.attr("name") + '"]:checked').length > 0 ? $this.val() : ""); 1157 | var radioParent = $this.parents("form").first() || $this.parents(".control-group").first(); 1158 | if (radioParent) { 1159 | value = radioParent.find("input[name='" + $this.attr("name") + "']:checked").map(function (i, el) { return $(el).val(); }).toArray().join(","); 1160 | } 1161 | } 1162 | else { 1163 | value = $this.val(); 1164 | } 1165 | return value; 1166 | }; 1167 | 1168 | function regexFromString(inputstring) { 1169 | return new RegExp("^" + inputstring + "$"); 1170 | } 1171 | 1172 | /** 1173 | * Thanks to Jason Bunting / Alex Nazarov via StackOverflow.com 1174 | * 1175 | * http://stackoverflow.com/a/4351575 1176 | **/ 1177 | function executeFunctionByName(functionName, context /*, args */) { 1178 | var args = Array.prototype.slice.call(arguments, 2); 1179 | var namespaces = functionName.split("."); 1180 | var func = namespaces.pop(); 1181 | for (var i = 0; i < namespaces.length; i++) { 1182 | context = context[namespaces[i]]; 1183 | } 1184 | return context[func].apply(context, args); 1185 | } 1186 | 1187 | $.fn.jqBootstrapValidation = function( method ) { 1188 | 1189 | if ( defaults.methods[method] ) { 1190 | return defaults.methods[method].apply( this, Array.prototype.slice.call( arguments, 1 )); 1191 | } else if ( typeof method === 'object' || ! method ) { 1192 | return defaults.methods.init.apply( this, arguments ); 1193 | } else { 1194 | $.error( 'Method ' + method + ' does not exist on jQuery.jqBootstrapValidation' ); 1195 | return null; 1196 | } 1197 | 1198 | }; 1199 | 1200 | $.jqBootstrapValidation = function (options) { 1201 | $(":input").not("[type=image],[type=submit]").jqBootstrapValidation.apply(this,arguments); 1202 | }; 1203 | 1204 | })( jQuery ); -------------------------------------------------------------------------------- /dist/jqBootstrapValidation-1.3.7.min.js: -------------------------------------------------------------------------------- 1 | /*! jqBootstrapValidation - v1.3.7 - 2013-05-07 2 | * http://reactiveraven.github.com/jqBootstrapValidation 3 | * Copyright (c) 2013 David Godfrey; Licensed MIT */ 4 | (function(e){function s(e){return new RegExp("^"+e+"$")}function o(e,t){var n=Array.prototype.slice.call(arguments,2),r=e.split("."),i=r.pop();for(var s=0;s0});u.trigger("submit.validation"),i.trigger("validationLostFocus.validation"),s.each(function(t,n){var i=e(n);if(i.hasClass("warning")||i.hasClass("error"))i.removeClass("warning").addClass("error"),r++}),r?(o.options.preventSubmit&&(t.preventDefault(),t.stopImmediatePropagation()),n.addClass("error"),e.isFunction(o.options.submitError)&&o.options.submitError(n,t,u.jqBootstrapValidation("collectErrors",!0))):(n.removeClass("error"),e.isFunction(o.options.submitSuccess)&&o.options.submitSuccess(n,t))}),this.each(function(){var n=e(this),s=n.parents(".control-group").first(),u=s.find(".help-block").first(),a=n.parents("form").first(),f=[];!u.length&&o.options.autoAdd&&o.options.autoAdd.helpBlocks&&(u=e('
'),s.find(".controls").append(u),t.push(u[0]));if(o.options.sniffHtml){var l;n.data("validationPatternPattern")&&n.attr("pattern",n.data("validationPatternPattern")),n.attr("pattern")!==undefined&&(l="Not in the expected format",n.data("validationPatternMessage")&&(l=n.data("validationPatternMessage")),n.data("validationPatternMessage",l),n.data("validationPatternRegex",n.attr("pattern")));if(n.attr("max")!==undefined||n.attr("aria-valuemax")!==undefined){var c=n.attr("max")!==undefined?n.attr("max"):n.attr("aria-valuemax");l="Too high: Maximum of '"+c+"'",n.data("validationMaxMessage")&&(l=n.data("validationMaxMessage")),n.data("validationMaxMessage",l),n.data("validationMaxMax",c)}if(n.attr("min")!==undefined||n.attr("aria-valuemin")!==undefined){var h=n.attr("min")!==undefined?n.attr("min"):n.attr("aria-valuemin");l="Too low: Minimum of '"+h+"'",n.data("validationMinMessage")&&(l=n.data("validationMinMessage")),n.data("validationMinMessage",l),n.data("validationMinMin",h)}n.attr("maxlength")!==undefined&&(l="Too long: Maximum of '"+n.attr("maxlength")+"' characters",n.data("validationMaxlengthMessage")&&(l=n.data("validationMaxlengthMessage")),n.data("validationMaxlengthMessage",l),n.data("validationMaxlengthMaxlength",n.attr("maxlength"))),n.attr("minlength")!==undefined&&(l="Too short: Minimum of '"+n.attr("minlength")+"' characters",n.data("validationMinlengthMessage")&&(l=n.data("validationMinlengthMessage")),n.data("validationMinlengthMessage",l),n.data("validationMinlengthMinlength",n.attr("minlength")));if(n.attr("required")!==undefined||n.attr("aria-required")!==undefined)l=o.builtInValidators.required.message,n.data("validationRequiredMessage")&&(l=n.data("validationRequiredMessage")),n.data("validationRequiredMessage",l);if(n.attr("type")!==undefined&&n.attr("type").toLowerCase()==="number"){l=o.validatorTypes.number.message,n.data("validationNumberMessage")&&(l=n.data("validationNumberMessage")),n.data("validationNumberMessage",l);var p=o.validatorTypes.number.step;n.data("validationNumberStep")&&(p=n.data("validationNumberStep")),n.data("validationNumberStep",p);var d=o.validatorTypes.number.decimal;n.data("validationNumberDecimal")&&(d=n.data("validationNumberDecimal")),n.data("validationNumberDecimal",d)}n.attr("type")!==undefined&&n.attr("type").toLowerCase()==="email"&&(l="Not a valid email address",n.data("validationEmailMessage")&&(l=n.data("validationEmailMessage")),n.data("validationEmailMessage",l)),n.attr("minchecked")!==undefined&&(l="Not enough options checked; Minimum of '"+n.attr("minchecked")+"' required",n.data("validationMincheckedMessage")&&(l=n.data("validationMincheckedMessage")),n.data("validationMincheckedMessage",l),n.data("validationMincheckedMinchecked",n.attr("minchecked"))),n.attr("maxchecked")!==undefined&&(l="Too many options checked; Maximum of '"+n.attr("maxchecked")+"' required",n.data("validationMaxcheckedMessage")&&(l=n.data("validationMaxcheckedMessage")),n.data("validationMaxcheckedMessage",l),n.data("validationMaxcheckedMaxchecked",n.attr("maxchecked")))}n.data("validation")!==undefined&&(f=n.data("validation").split(",")),e.each(n.data(),function(e,t){var n=e.replace(/([A-Z])/g,",$1").split(",");n[0]==="validation"&&n[1]&&f.push(n[1])});var v=f,m=[],g=function(e,t){f[e]=r(t)},y=function(t,i){if(n.data("validation"+i+"Shortcut")!==undefined)e.each(n.data("validation"+i+"Shortcut").split(","),function(e,t){m.push(t)});else if(o.builtInValidators[i.toLowerCase()]){var s=o.builtInValidators[i.toLowerCase()];s.type.toLowerCase()==="shortcut"&&e.each(s.shortcut.split(","),function(e,t){t=r(t),m.push(t),f.push(t)})}};do e.each(f,g),f=e.unique(f),m=[],e.each(v,y),v=m;while(v.length>0);var b={};e.each(f,function(t,i){var s=n.data("validation"+i+"Message"),u=!!s,a=!1;s||(s="'"+i+"' validation failed "),e.each(o.validatorTypes,function(t,o){b[t]===undefined&&(b[t]=[]);if(!a&&n.data("validation"+i+r(o.name))!==undefined){var f=o.init(n,i);u&&(f.message=s),b[t].push(e.extend(!0,{name:r(o.name),message:s},f)),a=!0}});if(!a&&o.builtInValidators[i.toLowerCase()]){var f=e.extend(!0,{},o.builtInValidators[i.toLowerCase()]);u&&(f.message=s);var l=f.type.toLowerCase();l==="shortcut"?a=!0:e.each(o.validatorTypes,function(t,s){b[t]===undefined&&(b[t]=[]),!a&&l===t.toLowerCase()&&(n.data("validation"+i+r(s.name),f[s.name.toLowerCase()]),b[l].push(e.extend(f,s.init(n,i))),a=!0)})}a||e.error("Cannot find validation info for '"+i+"'")}),u.data("original-contents",u.data("original-contents")?u.data("original-contents"):u.html()),u.data("original-role",u.data("original-role")?u.data("original-role"):u.attr("role")),s.data("original-classes",s.data("original-clases")?s.data("original-classes"):s.attr("class")),n.data("original-aria-invalid",n.data("original-aria-invalid")?n.data("original-aria-invalid"):n.attr("aria-invalid")),n.bind("validation.validation",function(t,r){var s=i(n),u=[];return e.each(b,function(t,i){(s||s.length||r&&r.includeEmpty||!!o.validatorTypes[t].includeEmpty||!!o.validatorTypes[t].blockSubmit&&r&&!!r.submitting)&&e.each(i,function(e,r){o.validatorTypes[t].validate(n,s,r)&&u.push(r.message)})}),u}),n.bind("getValidators.validation",function(){return b});var w=0;e.each(b,function(e,t){w+=t.length}),n.bind("getValidatorCount.validation",function(){return w}),n.bind("submit.validation",function(){return n.triggerHandler("change.validation",{submitting:!0})}),n.bind((o.options.bindEvents.length>0?o.options.bindEvents:["keyup","focus","blur","click","keydown","keypress","change"]).concat(["revalidate"]).join(".validation ")+".validation",function(t,r){var f=i(n),l=[];r&&!!r.submitting?s.data("jqbvIsSubmitting",!0):t.type!=="revalidate"&&s.data("jqbvIsSubmitting",!1);var c=!!s.data("jqbvIsSubmitting");s.find("input,textarea,select").each(function(t,i){var s=l.length;e.each(e(i).triggerHandler("validation.validation",r),function(e,t){l.push(t)});if(l.length>s)e(i).attr("aria-invalid","true");else{var o=n.data("original-aria-invalid");e(i).attr("aria-invalid",o!==undefined?o:!1)}}),a.find("input,select,textarea").not(n).not('[name="'+n.attr("name")+'"]').trigger("validationLostFocus.validation"),l=e.unique(l.sort()),l.length?(s.removeClass("success error warning").addClass(c?"error":"warning"),o.options.semanticallyStrict&&l.length===1?u.html(l[0]+(o.options.prependExistingHelpBlock?u.data("original-contents"):"")):u.html('
  • '+l.join("
  • ")+"
"+(o.options.prependExistingHelpBlock?u.data("original-contents"):""))):(s.removeClass("warning error success"),f.length>0&&s.addClass("success"),u.html(u.data("original-contents"))),t.type==="blur"&&s.removeClass("success")}),n.bind("validationLostFocus.validation",function(){s.removeClass("success")})})},destroy:function(){return this.each(function(){var n=e(this),r=n.parents(".control-group").first(),i=r.find(".help-block").first(),s=n.parents("form").first();n.unbind(".validation"),s.unbind(".validationSubmit"),i.html(i.data("original-contents")),r.attr("class",r.data("original-classes")),n.attr("aria-invalid",n.data("original-aria-invalid")),i.attr("role",n.data("original-role")),e.inArray(i[0],t)>-1&&i.remove()})},collectErrors:function(t){var n={};return this.each(function(t,r){var i=e(r),s=i.attr("name"),o=i.triggerHandler("validation.validation",{includeEmpty:!0});n[s]=e.extend(!0,o,n[s])}),e.each(n,function(e,t){t.length===0&&delete n[e]}),n},hasErrors:function(){var t=[];return this.find("input,select,textarea").add(this).each(function(n,r){t=t.concat(e(r).triggerHandler("getValidators.validation")?e(r).triggerHandler("validation.validation",{submitting:!0}):[])}),t.length>0},override:function(t){n=e.extend(!0,n,t)}},validatorTypes:{callback:{name:"callback",init:function(e,t){var n={validatorName:t,callback:e.data("validation"+t+"Callback"),lastValue:e.val(),lastValid:!0,lastFinished:!0},r="Not valid";return e.data("validation"+t+"Message")&&(r=e.data("validation"+t+"Message")),n.message=r,n},validate:function(e,t,n){if(n.lastValue===t&&n.lastFinished)return!n.lastValid;if(n.lastFinished===!0){n.lastValue=t,n.lastValid=!0,n.lastFinished=!1;var r=n,i=e;o(n.callback,window,e,t,function(t){r.lastValue===t.value&&(r.lastValid=t.valid,t.message&&(r.message=t.message),r.lastFinished=!0,i.data("validation"+r.validatorName+"Message",r.message),setTimeout(function(){!e.is(":focus")&&e.parents("form").first().data("jqbvIsSubmitting")?i.trigger("blur.validation"):i.trigger("revalidate.validation")},1))})}return!1}},ajax:{name:"ajax",init:function(e,t){return{validatorName:t,url:e.data("validation"+t+"Ajax"),lastValue:e.val(),lastValid:!0,lastFinished:!0}},validate:function(t,n,r){return""+r.lastValue==""+n&&r.lastFinished===!0?r.lastValid===!1:(r.lastFinished===!0&&(r.lastValue=n,r.lastValid=!0,r.lastFinished=!1,e.ajax({url:r.url,data:"value="+encodeURIComponent(n)+"&field="+t.attr("name"),dataType:"json",success:function(e){""+r.lastValue==""+e.value&&(r.lastValid=!!e.valid,e.message&&(r.message=e.message),r.lastFinished=!0,t.data("validation"+r.validatorName+"Message",r.message),setTimeout(function(){t.trigger("revalidate.validation")},1))},failure:function(){r.lastValid=!0,r.message="ajax call failed",r.lastFinished=!0,t.data("validation"+r.validatorName+"Message",r.message),setTimeout(function(){t.trigger("revalidate.validation")},1)}})),!1)}},regex:{name:"regex",init:function(t,n){var r={},i=t.data("validation"+n+"Regex");r.regex=s(i),i===undefined&&e.error("Can't find regex for '"+n+"' validator on '"+t.attr("name")+"'");var o="Not in the expected format";return t.data("validation"+n+"Message")&&(o=t.data("validation"+n+"Message")),r.message=o,r.originalName=n,r},validate:function(e,t,n){return!n.regex.test(t)&&!n.negative||n.regex.test(t)&&n.negative}},email:{name:"email",init:function(e,t){var n={};n.regex=s("[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}");var r="Not a valid email address";return e.data("validation"+t+"Message")&&(r=e.data("validation"+t+"Message")),n.message=r,n.originalName=t,n},validate:function(e,t,n){return!n.regex.test(t)&&!n.negative||n.regex.test(t)&&n.negative}},required:{name:"required",init:function(e,t){var n="This is required";return e.data("validation"+t+"Message")&&(n=e.data("validation"+t+"Message")),{message:n,includeEmpty:!0}},validate:function(e,t,n){return!!(t.length===0&&!n.negative||t.length>0&&n.negative)},blockSubmit:!0},match:{name:"match",init:function(t,n){var r=t.data("validation"+n+"Match"),i=t.parents("form").first(),s=i.find('[name="'+r+'"]').first();s.bind("validation.validation",function(){t.trigger("revalidate.validation",{submitting:!0})});var o={};o.element=s,s.length===0&&e.error("Can't find field '"+r+"' to match '"+t.attr("name")+"' against in '"+n+"' validator");var u="Must match",a=null;return(a=i.find('label[for="'+r+'"]')).length?u+=" '"+a.text()+"'":(a=s.parents(".control-group").first().find("label")).length&&(u+=" '"+a.first().text()+"'"),t.data("validation"+n+"Message")&&(u=t.data("validation"+n+"Message")),o.message=u,o},validate:function(e,t,n){return t!==n.element.val()&&!n.negative||t===n.element.val()&&n.negative},blockSubmit:!0,includeEmpty:!0},max:{name:"max",init:function(e,t){var n={};return n.max=e.data("validation"+t+"Max"),n.message="Too high: Maximum of '"+n.max+"'",e.data("validation"+t+"Message")&&(n.message=e.data("validation"+t+"Message")),n},validate:function(e,t,n){return parseFloat(t,10)>parseFloat(n.max,10)&&!n.negative||parseFloat(t,10)<=parseFloat(n.max,10)&&n.negative}},min:{name:"min",init:function(e,t){var n={};return n.min=e.data("validation"+t+"Min"),n.message="Too low: Minimum of '"+n.min+"'",e.data("validation"+t+"Message")&&(n.message=e.data("validation"+t+"Message")),n},validate:function(e,t,n){return parseFloat(t)=parseFloat(n.min)&&n.negative}},maxlength:{name:"maxlength",init:function(e,t){var n={};return n.maxlength=e.data("validation"+t+"Maxlength"),n.message="Too long: Maximum of '"+n.maxlength+"' characters",e.data("validation"+t+"Message")&&(n.message=e.data("validation"+t+"Message")),n},validate:function(e,t,n){return t.length>n.maxlength&&!n.negative||t.length<=n.maxlength&&n.negative}},minlength:{name:"minlength",init:function(e,t){var n={};return n.minlength=e.data("validation"+t+"Minlength"),n.message="Too short: Minimum of '"+n.minlength+"' characters",e.data("validation"+t+"Message")&&(n.message=e.data("validation"+t+"Message")),n},validate:function(e,t,n){return t.length=n.minlength&&n.negative}},maxchecked:{name:"maxchecked",init:function(e,t){var n={},r=e.parents("form").first().find('[name="'+e.attr("name")+'"]');r.bind("change.validation click.validation",function(){e.trigger("revalidate.validation",{includeEmpty:!0})}),n.elements=r,n.maxchecked=e.data("validation"+t+"Maxchecked");var i="Too many: Max '"+n.maxchecked+"' checked";return e.data("validation"+t+"Message")&&(i=e.data("validation"+t+"Message")),n.message=i,n},validate:function(e,t,n){return n.elements.filter(":checked").length>n.maxchecked&&!n.negative||n.elements.filter(":checked").length<=n.maxchecked&&n.negative},blockSubmit:!0},minchecked:{name:"minchecked",init:function(e,t){var n={},r=e.parents("form").first().find('[name="'+e.attr("name")+'"]');r.bind("change.validation click.validation",function(){e.trigger("revalidate.validation",{includeEmpty:!0})}),n.elements=r,n.minchecked=e.data("validation"+t+"Minchecked");var i="Too few: Min '"+n.minchecked+"' checked";return e.data("validation"+t+"Message")&&(i=e.data("validation"+t+"Message")),n.message=i,n},validate:function(e,t,n){return n.elements.filter(":checked").length=n.minchecked&&n.negative},blockSubmit:!0,includeEmpty:!0},number:{name:"number",init:function(e,t){var n={};n.step=1,e.attr("step")&&(n.step=e.attr("step")),e.data("validation"+t+"Step")&&(n.step=e.data("validation"+t+"Step")),n.decimal=".",e.data("validation"+t+"Decimal")&&(n.decimal=e.data("validation"+t+"Decimal")),n.thousands="",e.data("validation"+t+"Thousands")&&(n.thousands=e.data("validation"+t+"Thousands")),n.regex=s("([+-]?\\d+(\\"+n.decimal+"\\d+)?)?"),n.message="Must be a number";var r=e.data("validation"+t+"Message");return r&&(n.message=r),n},validate:function(e,t,n){var r=t.replace(n.decimal,".").replace(n.thousands,""),i=parseFloat(r),s=parseFloat(n.step);while(s%1!==0)s=parseFloat(s.toPrecision(12))*10,i=parseFloat(i.toPrecision(12))*10;var o=n.regex.test(t),u=parseFloat(i)%parseFloat(s)===0,a=!isNaN(parseFloat(r))&&isFinite(r),f=!(o&&u&&a);return f},message:"Must be a number"}},builtInValidators:{email:{name:"Email",type:"email"},passwordagain:{name:"Passwordagain",type:"match",match:"password",message:"Does not match the given password"},positive:{name:"Positive",type:"shortcut",shortcut:"number,positivenumber"},negative:{name:"Negative",type:"shortcut",shortcut:"number,negativenumber"},integer:{name:"Integer",type:"regex",regex:"[+-]?\\d+",message:"No decimal places allowed"},positivenumber:{name:"Positivenumber",type:"min",min:0,message:"Must be a positive number"},negativenumber:{name:"Negativenumber",type:"max",max:0,message:"Must be a negative number"},required:{name:"Required",type:"required",message:"This is required"},checkone:{name:"Checkone",type:"minchecked",minchecked:1,message:"Check at least one option"},number:{name:"Number",type:"number",decimal:".",step:"1"},pattern:{name:"Pattern",type:"regex",message:"Not in expected format"}}},r=function(e){return e.toLowerCase().replace(/(^|\s)([a-z])/g,function(e,t,n){return t+n.toUpperCase()})},i=function(t){var n=null,r=t.attr("type");if(r==="checkbox"){n=t.is(":checked")?n:"";var i=t.parents("form").first()||t.parents(".control-group").first();i&&(n=i.find("input[name='"+t.attr("name")+"']:checked").map(function(t,n){return e(n).val()}).toArray().join(","))}else if(r==="radio"){n=e('input[name="'+t.attr("name")+'"]:checked').length>0?t.val():"";var s=t.parents("form").first()||t.parents(".control-group").first();s&&(n=s.find("input[name='"+t.attr("name")+"']:checked").map(function(t,n){return e(n).val()}).toArray().join(","))}else n=t.val();return n};e.fn.jqBootstrapValidation=function(t){return n.methods[t]?n.methods[t].apply(this,Array.prototype.slice.call(arguments,1)):typeof t=="object"||!t?n.methods.init.apply(this,arguments):(e.error("Method "+t+" does not exist on jQuery.jqBootstrapValidation"),null)},e.jqBootstrapValidation=function(t){e(":input").not("[type=image],[type=submit]").jqBootstrapValidation.apply(this,arguments)}})(jQuery); -------------------------------------------------------------------------------- /dist/jqBootstrapValidation.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReactiveRaven/jqBootstrapValidation/96fef5d0800e29cbaf3fa9e2451cce0a1bd0b45d/dist/jqBootstrapValidation.zip -------------------------------------------------------------------------------- /libs/jquery-loader.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | // Get any jquery=___ param from the query string. 3 | var jqversion = location.search.match(/[?&]jquery=(.*?)(?=&|$)/); 4 | var path; 5 | if (jqversion) { 6 | // A version was specified, load that version from code.jquery.com. 7 | path = 'http://code.jquery.com/jquery-' + jqversion[1] + '.js'; 8 | } else { 9 | // No version was specified, load the local version. 10 | path = '../libs/jquery/jquery.js'; 11 | } 12 | // This is the only time I'll ever use document.write, I promise! 13 | document.write(''); 14 | }()); 15 | -------------------------------------------------------------------------------- /libs/jquery/jquery-migrate.1.2.0.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery Migrate - v1.2.0 - 2013-05-01 3 | * https://github.com/jquery/jquery-migrate 4 | * Copyright 2005, 2013 jQuery Foundation, Inc. and other contributors; Licensed MIT 5 | */ 6 | (function( jQuery, window, undefined ) { 7 | // See http://bugs.jquery.com/ticket/13335 8 | // "use strict"; 9 | 10 | 11 | var warnedAbout = {}; 12 | 13 | // List of warnings already given; public read only 14 | jQuery.migrateWarnings = []; 15 | 16 | // Set to true to prevent console output; migrateWarnings still maintained 17 | // jQuery.migrateMute = false; 18 | 19 | // Show a message on the console so devs know we're active 20 | if ( !jQuery.migrateMute && window.console && window.console.log ) { 21 | window.console.log("JQMIGRATE: Logging is active"); 22 | } 23 | 24 | // Set to false to disable traces that appear with warnings 25 | if ( jQuery.migrateTrace === undefined ) { 26 | jQuery.migrateTrace = true; 27 | } 28 | 29 | // Forget any warnings we've already given; public 30 | jQuery.migrateReset = function() { 31 | warnedAbout = {}; 32 | jQuery.migrateWarnings.length = 0; 33 | }; 34 | 35 | function migrateWarn( msg) { 36 | var console = window.console; 37 | if ( !warnedAbout[ msg ] ) { 38 | warnedAbout[ msg ] = true; 39 | jQuery.migrateWarnings.push( msg ); 40 | if ( console && console.warn && !jQuery.migrateMute ) { 41 | console.warn( "JQMIGRATE: " + msg ); 42 | if ( jQuery.migrateTrace && console.trace ) { 43 | console.trace(); 44 | } 45 | } 46 | } 47 | } 48 | 49 | function migrateWarnProp( obj, prop, value, msg ) { 50 | if ( Object.defineProperty ) { 51 | // On ES5 browsers (non-oldIE), warn if the code tries to get prop; 52 | // allow property to be overwritten in case some other plugin wants it 53 | try { 54 | Object.defineProperty( obj, prop, { 55 | configurable: true, 56 | enumerable: true, 57 | get: function() { 58 | migrateWarn( msg ); 59 | return value; 60 | }, 61 | set: function( newValue ) { 62 | migrateWarn( msg ); 63 | value = newValue; 64 | } 65 | }); 66 | return; 67 | } catch( err ) { 68 | // IE8 is a dope about Object.defineProperty, can't warn there 69 | } 70 | } 71 | 72 | // Non-ES5 (or broken) browser; just set the property 73 | jQuery._definePropertyBroken = true; 74 | obj[ prop ] = value; 75 | } 76 | 77 | if ( document.compatMode === "BackCompat" ) { 78 | // jQuery has never supported or tested Quirks Mode 79 | migrateWarn( "jQuery is not compatible with Quirks Mode" ); 80 | } 81 | 82 | 83 | var attrFn = jQuery( "", { size: 1 } ).attr("size") && jQuery.attrFn, 84 | oldAttr = jQuery.attr, 85 | valueAttrGet = jQuery.attrHooks.value && jQuery.attrHooks.value.get || 86 | function() { return null; }, 87 | valueAttrSet = jQuery.attrHooks.value && jQuery.attrHooks.value.set || 88 | function() { return undefined; }, 89 | rnoType = /^(?:input|button)$/i, 90 | rnoAttrNodeType = /^[238]$/, 91 | rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i, 92 | ruseDefault = /^(?:checked|selected)$/i; 93 | 94 | // jQuery.attrFn 95 | migrateWarnProp( jQuery, "attrFn", attrFn || {}, "jQuery.attrFn is deprecated" ); 96 | 97 | jQuery.attr = function( elem, name, value, pass ) { 98 | var lowerName = name.toLowerCase(), 99 | nType = elem && elem.nodeType; 100 | 101 | if ( pass ) { 102 | // Since pass is used internally, we only warn for new jQuery 103 | // versions where there isn't a pass arg in the formal params 104 | if ( oldAttr.length < 4 ) { 105 | migrateWarn("jQuery.fn.attr( props, pass ) is deprecated"); 106 | } 107 | if ( elem && !rnoAttrNodeType.test( nType ) && 108 | (attrFn ? name in attrFn : jQuery.isFunction(jQuery.fn[name])) ) { 109 | return jQuery( elem )[ name ]( value ); 110 | } 111 | } 112 | 113 | // Warn if user tries to set `type`, since it breaks on IE 6/7/8; by checking 114 | // for disconnected elements we don't warn on $( "\ 46 |
\ 47 | \ 48 | "); 49 | attachJqbv(); 50 | this.elems = $("#qunit-fixture").children(); 51 | }, 52 | teardown: function() { 53 | $("#qunit-fixture").empty(); 54 | } 55 | }); 56 | 57 | module('required field', { 58 | setup: function() { 59 | $("#qunit-fixture").append("\ 60 |
\ 61 |
\ 62 | \ 63 |
\ 64 | \ 69 |
\ 70 |
\ 71 |
\ 72 | \ 75 |
\ 76 |
\ 77 | "); 78 | attachJqbv(); 79 | }, 80 | teardown: function() { 81 | $("#qunit-fixture").empty(); 82 | } 83 | }); 84 | test('is required', 1 * numInJQBVTest, function() { 85 | runJQBVTest("", [], ["error"], [], ["This is required"]); 86 | }); 87 | 88 | }(jQuery)); 89 | -------------------------------------------------------------------------------- /test/issues/47/47.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | jqBootstrapValidation Test Suite 6 | 7 | 8 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 57 | 58 | 59 |

jqBootstrapValidation Test Suite

60 |

61 |
62 |

63 |
    64 |
    65 | lame test markup 66 | normal test markup 67 | awesome test markup 68 |
    69 | 70 | -------------------------------------------------------------------------------- /test/issues/47/test.js: -------------------------------------------------------------------------------- 1 | /*global QUnit:false, module:false, test:false, asyncTest:false, expect:false, console:false*/ 2 | /*global start:false, stop:false, ok:false, equal:false, notEqual:false, deepEqual:false*/ 3 | /*global notDeepEqual:false, strictEqual:false, notStrictEqual:false, raises:false*/ 4 | /*global JSON:false */ 5 | /*global runJQBVTest:false, attachJqbv:false, numInJQBVTest:false, startJQBVTestQueue:false, pushJQBVTest:false, extractEvents:false*/ 6 | /*jshint multistr: true */ 7 | (function($) { 8 | 9 | /* 10 | ======== A Handy Little QUnit Reference ======== 11 | http://docs.jquery.com/QUnit 12 | 13 | Test methods: 14 | - expect(numAssertions) 15 | - stop(increment) 16 | - start(decrement) 17 | Test assertions: 18 | - ok(value, [message]) 19 | - equal(actual, expected, [message]) 20 | - notEqual(actual, expected, [message]) 21 | - deepEqual(actual, expected, [message]) 22 | - notDeepEqual(actual, expected, [message]) 23 | - strictEqual(actual, expected, [message]) 24 | - notStrictEqual(actual, expected, [message]) 25 | - raises(block, [expected], [message]) 26 | */ 27 | 28 | module('required field', { 29 | setup: function() { 30 | $("#qunit-fixture").append("\ 31 |
    \ 32 |
    \ 33 | \ 34 |
    \ 35 | \ 41 |
    \ 42 |
    \ 43 |
    \ 44 | \ 47 |
    \ 48 |
    \ 49 | "); 50 | attachJqbv(); 51 | }, 52 | teardown: function() { 53 | $("#qunit-fixture").empty(); 54 | } 55 | }); 56 | 57 | test('select multiple value', 2 * numInJQBVTest, function () { 58 | runJQBVTest(null, [], ["error"], [], ["This is required"]); 59 | runJQBVTest(["t"], ["success"], [], [], []); 60 | }); 61 | 62 | }(jQuery)); 63 | -------------------------------------------------------------------------------- /test/issues/50/50.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | jqBootstrapValidation Test Suite 6 | 7 | 8 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 57 | 58 | 59 |

    jqBootstrapValidation Test Suite

    60 |

    61 |
    62 |

    63 |
      64 |
      65 | lame test markup 66 | normal test markup 67 | awesome test markup 68 |
      69 | 70 | -------------------------------------------------------------------------------- /test/issues/50/test.js: -------------------------------------------------------------------------------- 1 | /*global QUnit:false, module:false, test:false, asyncTest:false, expect:false, console:false*/ 2 | /*global start:false, stop:false, ok:false, equal:false, notEqual:false, deepEqual:false*/ 3 | /*global notDeepEqual:false, strictEqual:false, notStrictEqual:false, raises:false*/ 4 | /*global JSON:false */ 5 | /*global runJQBVTest:false, attachJqbv:false, numInJQBVTest:false, startJQBVTestQueue:false, pushJQBVTest:false, extractEvents:false*/ 6 | /*jshint multistr: true */ 7 | (function($) { 8 | 9 | /* 10 | ======== A Handy Little QUnit Reference ======== 11 | http://docs.jquery.com/QUnit 12 | 13 | Test methods: 14 | - expect(numAssertions) 15 | - stop(increment) 16 | - start(decrement) 17 | Test assertions: 18 | - ok(value, [message]) 19 | - equal(actual, expected, [message]) 20 | - notEqual(actual, expected, [message]) 21 | - deepEqual(actual, expected, [message]) 22 | - notDeepEqual(actual, expected, [message]) 23 | - strictEqual(actual, expected, [message]) 24 | - notStrictEqual(actual, expected, [message]) 25 | - raises(block, [expected], [message]) 26 | */ 27 | 28 | module('jqBootstrapValidation', { 29 | setup: function() { 30 | $("#qunit-fixture").append("\ 31 |
      \ 32 |
      \ 33 | \ 34 |
      \ 35 | \ 40 |
      \ 41 |
      \ 42 |
      \ 43 | \ 46 |
      \ 47 |
      \ 48 | "); 49 | attachJqbv(); 50 | this.elems = $("#qunit-fixture").children(); 51 | }, 52 | teardown: function() { 53 | $("#qunit-fixture").empty(); 54 | } 55 | }); 56 | 57 | module('required field', { 58 | setup: function() { 59 | $("#qunit-fixture").append("\ 60 |
      \ 61 |
      \ 62 | \ 63 |
      \ 64 | \ 69 |
      \ 70 |
      \ 71 |
      \ 72 | \ 75 |
      \ 76 |
      \ 77 | "); 78 | attachJqbv(); 79 | }, 80 | teardown: function() { 81 | $("#qunit-fixture").empty(); 82 | } 83 | }); 84 | 85 | test('tidies up events on destroy', 2, function () { 86 | var $input = $("#qunit-fixture input"); 87 | var formEventsBeforeDestroy = extractEvents($input.parents("form").first()); 88 | $input.jqBootstrapValidation("destroy"); 89 | var inputEvents = extractEvents($input); 90 | var $form = $input.parents("form").first(); 91 | var formEvents = extractEvents($form); 92 | 93 | ok(!formEvents, "Form still has lingering events"); 94 | ok(!inputEvents, "Input still has lingering events"); 95 | }); 96 | 97 | }(jQuery)); 98 | -------------------------------------------------------------------------------- /test/jqBootstrapValidation.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | jqBootstrapValidation Test Suite 6 | 7 | 8 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 57 | 58 | 59 |

      jqBootstrapValidation Test Suite

      60 |

      61 |
      62 |

      63 |
        64 |
        65 | lame test markup 66 | normal test markup 67 | awesome test markup 68 |
        69 | 70 | 71 | -------------------------------------------------------------------------------- /test/jqBootstrapValidation_helpers.js: -------------------------------------------------------------------------------- 1 | /*global QUnit:false, module:false, test:false, asyncTest:false, expect:false, console:false*/ 2 | /*global start:false, stop:false ok:false, equal:false, notEqual:false, deepEqual:false*/ 3 | /*global notDeepEqual:false, strictEqual:false, notStrictEqual:false, raises:false*/ 4 | /*global importFromTd:false */ 5 | (function ($) { 6 | window.number_of_submit_successes = 0; 7 | window.number_of_submit_errors = 0; 8 | 9 | window.attachJqbv = function() { 10 | $("#qunit-fixture").find("input,select,textarea").not("[type=submit]").jqBootstrapValidation( 11 | { 12 | preventSubmit: true, 13 | submitError: function($form, event, errors) { 14 | // Here I do nothing, but you could do something like display 15 | // the error messages to the user, log, etc. 16 | window.number_of_submit_errors++; 17 | }, 18 | submitSuccess: function($form, event) { 19 | window.number_of_submit_successes++; 20 | event.preventDefault(); 21 | } 22 | } 23 | ); 24 | }; 25 | 26 | window.importFromTd = function($td) { 27 | 28 | // Handle single items simply 29 | var result = $td.text(); 30 | if (result.length > 0) { 31 | result = [result]; 32 | } else { 33 | // literally nothing there? Guess it should be empty. 34 | result = []; 35 | } 36 | 37 | // if multiple items, expect them in a list 38 | if ($td.find("ul,ol").length) { 39 | result = $td.find("ol,ul").first().find("li").map(function(i, el) { 40 | return $(el).text(); 41 | }).toArray(); 42 | } 43 | 44 | return result; 45 | }; 46 | 47 | window.arraysMatch = function(first, second) { 48 | return ( 49 | $(first).not(second).length === 0 && 50 | $(second).not(first).length === 0 51 | ); 52 | }; 53 | 54 | window.numInJQBVTest = 9; 55 | 56 | window.jqbvTestQueue = []; 57 | 58 | window.pushJQBVTest = function(value, classChange, classSubmit, messageChange, messageSubmit) { 59 | window.jqbvTestQueue.push([value, classChange, classSubmit, messageChange, messageSubmit]); 60 | }; 61 | 62 | window.startJQBVTestQueue = function() { 63 | window.tickJQBVTestQueue(); 64 | }; 65 | 66 | window.stopJQBVTestQueue = function() { 67 | start(); 68 | }; 69 | 70 | window.tickJQBVTestQueue = function() { 71 | if (window.jqbvTestQueue.length > 0) { 72 | var v = window.jqbvTestQueue.pop(); 73 | window.runJQBVTestAsync(v[0], v[1], v[2], v[3], v[4], function() { 74 | window.tickJQBVTestQueue(); 75 | }); 76 | } else { 77 | window.stopJQBVTestQueue(); 78 | } 79 | }; 80 | 81 | window.extractEvents = function($input) { 82 | var eventsArray = null; 83 | if ($input.data("events")) { 84 | eventsArray = $input.data("events"); 85 | } else if ($input._data && $input._data("events")) { 86 | eventsArray = $input._data("events"); 87 | } 88 | return eventsArray; 89 | }; 90 | 91 | 92 | 93 | window.runJQBVTest = function(value, classChange, classSubmit, messageChange, messageSubmit) { 94 | 95 | var $input = $("#qunit-fixture").find("[name=input]"); 96 | var $controlGroup = $($input.parents(".control-group")[0]); 97 | var $form = $input.parents("form").first(); 98 | var isMulti = ($input.length > 1); 99 | 100 | var values; 101 | if (isMulti) { 102 | if (value.length) { 103 | if (typeof value === "string") { 104 | values = value.split(","); 105 | } else { 106 | // is an array already, so just use it 107 | values = value; 108 | } 109 | } else { 110 | values = []; 111 | } 112 | } else { 113 | values = [value]; 114 | } 115 | 116 | var valueJson = JSON.stringify(values); 117 | 118 | 119 | var valueAccepted = true; 120 | 121 | if (isMulti) { 122 | // dealing with checkboxes, radioboxes, etc 123 | var $inputs = $input; 124 | $inputs.removeAttr("checked"); 125 | $(values).each(function(i, el) { 126 | var $curInput = $inputs.filter("[value=\"" + el + "\"]"); 127 | 128 | if ($curInput.length === 0) { 129 | valueAccepted = false; 130 | } else { 131 | $curInput.attr("checked", "checked"); 132 | } 133 | }); 134 | 135 | deepEqual(valueAccepted, true, "value is accepted by browser - " + valueJson); 136 | 137 | } else { 138 | 139 | // dealing with text, selects, etc 140 | $input.val(values[0]); 141 | 142 | deepEqual($input.val(), values[0], "value is accepted by browser - " + valueJson); 143 | } 144 | 145 | $input.trigger("change.validation"); 146 | var changeClassExpected = ["control-group"].concat(classChange); 147 | var changeClassActual = $controlGroup.attr("class").split(" "); 148 | deepEqual(changeClassActual, changeClassExpected, "classes as expected on change - " + valueJson); 149 | 150 | var changeMessageActual = importFromTd($controlGroup.find(".help-block")); 151 | deepEqual(changeMessageActual, messageChange, "message as expected on change - " + valueJson); 152 | 153 | var prevErrors = window.number_of_submit_errors; 154 | var prevSuccess = window.number_of_submit_successes; 155 | $form.trigger("submit"); 156 | var nowErrors = window.number_of_submit_errors; 157 | var nowSuccess = window.number_of_submit_successes; 158 | 159 | var submitClassExpected = ["control-group"].concat(classSubmit); 160 | var submitClassActual = $controlGroup.attr("class").split(" "); 161 | deepEqual(submitClassActual, submitClassExpected, "classes as expected on submit - " + valueJson); 162 | 163 | var submitMessageExpected = messageSubmit; 164 | var submitMessageActual = importFromTd($controlGroup.find(".help-block")); 165 | deepEqual(submitMessageActual, submitMessageExpected, "message as expected on submit - " + valueJson); 166 | 167 | if (classSubmit.indexOf("error") > -1) { 168 | deepEqual(prevErrors + 1, nowErrors, "expect an error to be fired - " + valueJson); 169 | deepEqual(prevSuccess, nowSuccess, "DID NOT expect success to be fired - " + valueJson); 170 | } else { 171 | deepEqual(prevErrors, nowErrors, "DID NOT expect an error to be fired - " + valueJson); 172 | deepEqual(prevSuccess + 1, nowSuccess, "expect success to be fired - " + valueJson); 173 | } 174 | 175 | $input.trigger("change.validation"); 176 | changeClassActual = $controlGroup.attr("class").split(" "); 177 | deepEqual(changeClassActual, changeClassExpected, "classes revert again on change - " + valueJson); 178 | 179 | changeMessageActual = importFromTd($controlGroup.find(".help-block")); 180 | deepEqual(changeMessageActual, messageChange, "message reverts again on change - " + valueJson); 181 | }; 182 | 183 | window.runJQBVTestAsync = function(value, classChange, classSubmit, messageChange, messageSubmit, callback) { 184 | 185 | var $input = $("#qunit-fixture").find("[name=input]"); 186 | var $controlGroup = $($input.parents(".control-group")[0]); 187 | var $form = $input.parents("form").first(); 188 | var isMulti = ($input.length > 1); 189 | 190 | var values; 191 | if (isMulti) { 192 | if (value.length) { 193 | if (typeof value === "string") { 194 | values = value.split(","); 195 | } else { 196 | // is an array already, so just use it 197 | values = value; 198 | } 199 | } else { 200 | values = []; 201 | } 202 | } else { 203 | values = [value]; 204 | } 205 | 206 | var valueJson = JSON.stringify(values); 207 | 208 | 209 | var valueAccepted = true; 210 | 211 | if (isMulti) { 212 | // dealing with checkboxes, radioboxes, etc 213 | var $inputs = $input; 214 | $inputs.removeAttr("checked"); 215 | $(values).each(function(i, el) { 216 | var $curInput = $inputs.filter("[value=\"" + el + "\"]"); 217 | 218 | if ($curInput.length === 0) { 219 | valueAccepted = false; 220 | } else { 221 | $curInput.attr("checked", "checked"); 222 | } 223 | }); 224 | 225 | deepEqual(valueAccepted, true, "value is accepted by browser - " + valueJson); 226 | 227 | } else { 228 | 229 | // dealing with text, selects, etc 230 | $input.val(values[0]); 231 | 232 | deepEqual($input.val(), values[0], "value is accepted by browser - " + valueJson); 233 | } 234 | 235 | $input.trigger("change.validation"); 236 | setTimeout( 237 | function() { 238 | var changeClassExpected = ["control-group"].concat(classChange); 239 | var changeClassActual = $controlGroup.attr("class").split(" "); 240 | deepEqual(changeClassActual, changeClassExpected, "classes as expected on change - " + valueJson); 241 | 242 | var changeMessageActual = importFromTd($controlGroup.find(".help-block")); 243 | deepEqual(changeMessageActual, messageChange, "message as expected on change - " + valueJson); 244 | 245 | 246 | 247 | /* 248 | var prevErrors = window.number_of_submit_errors; 249 | var prevSuccess = window.number_of_submit_successes; 250 | $form.trigger("submit"); 251 | var nowErrors = window.number_of_submit_errors; 252 | var nowSuccess = window.number_of_submit_successes; 253 | 254 | var submitClassExpected = ["control-group"].concat(classSubmit); 255 | var submitClassActual = $controlGroup.attr("class").split(" "); 256 | deepEqual(submitClassActual, submitClassExpected, "classes as expected on submit - " + valueJson); 257 | 258 | var submitMessageExpected = messageSubmit; 259 | var submitMessageActual = importFromTd($controlGroup.find(".help-block")); 260 | deepEqual(submitMessageActual, submitMessageExpected, "message as expected on submit - " + valueJson); 261 | 262 | if (classSubmit.indexOf("error") > -1) { 263 | deepEqual(prevErrors + 1, nowErrors, "expect an error to be fired - " + valueJson); 264 | deepEqual(prevSuccess, nowSuccess, "DID NOT expect success to be fired - " + valueJson); 265 | } else { 266 | deepEqual(prevErrors, nowErrors, "DID NOT expect an error to be fired - " + valueJson); 267 | deepEqual(prevSuccess + 1, nowSuccess, "expect success to be fired - " + valueJson); 268 | } 269 | **/ 270 | 271 | var prevErrors = window.number_of_submit_errors; 272 | var prevSuccess = window.number_of_submit_successes; 273 | $form.trigger("submit"); 274 | setTimeout( 275 | function() { 276 | var submitClassExpected = ["control-group"].concat(classSubmit); 277 | var submitClassActual = $controlGroup.attr("class").split(" "); 278 | 279 | deepEqual(submitClassActual, submitClassExpected, "classes as expected on submit - " + valueJson); 280 | 281 | var submitMessageExpected = messageSubmit; 282 | var submitMessageActual = importFromTd($controlGroup.find(".help-block")); 283 | deepEqual(submitMessageActual, submitMessageExpected, "message as expected on submit - " + valueJson); 284 | 285 | var nowErrors = window.number_of_submit_errors; 286 | var nowSuccess = window.number_of_submit_successes; 287 | 288 | if (classSubmit.indexOf("error") > -1) { 289 | deepEqual(prevErrors + 1, nowErrors, "expect an error to be fired - " + valueJson); 290 | deepEqual(prevSuccess, nowSuccess, "DID NOT expect success to be fired - " + valueJson); 291 | } else { 292 | deepEqual(prevErrors, nowErrors, "DID NOT expect an error to be fired - " + valueJson); 293 | deepEqual(prevSuccess + 1, nowSuccess, "expect success to be fired - " + valueJson); 294 | } 295 | 296 | $input.trigger("change.validation"); 297 | setTimeout( 298 | function() { 299 | changeClassActual = $controlGroup.attr("class").split(" "); 300 | deepEqual(changeClassActual, changeClassExpected, "classes revert again on change - " + valueJson); 301 | 302 | changeMessageActual = importFromTd($controlGroup.find(".help-block")); 303 | deepEqual(changeMessageActual, messageChange, "message reverts again on change - " + valueJson); 304 | 305 | callback(); 306 | }, 307 | 50 308 | ); 309 | }, 310 | 50 311 | ); 312 | }, 313 | 50 314 | ); 315 | }; 316 | })(jQuery); -------------------------------------------------------------------------------- /test/notravis/67/67.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | jqBootstrapValidation Test Suite 6 | 7 | 8 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 57 | 58 | 59 |

        jqBootstrapValidation Test Suite

        60 |

        61 |
        62 |

        63 |
          64 |
          65 | lame test markup 66 | normal test markup 67 | awesome test markup 68 |
          69 | 585 | 586 | -------------------------------------------------------------------------------- /test/notravis/67/test.js: -------------------------------------------------------------------------------- 1 | /*global QUnit:false, module:false, test:false, asyncTest:false, expect:false, console:false*/ 2 | /*global start:false, stop:false, ok:false, equal:false, notEqual:false, deepEqual:false*/ 3 | /*global notDeepEqual:false, strictEqual:false, notStrictEqual:false, raises:false*/ 4 | /*global JSON:false */ 5 | /*global runJQBVTest:false, attachJqbv:false, numInJQBVTest:false, startJQBVTestQueue:false, pushJQBVTest:false, extractEvents:false*/ 6 | /*jshint multistr: true */ 7 | (function($) { 8 | 9 | /* 10 | ======== A Handy Little QUnit Reference ======== 11 | http://docs.jquery.com/QUnit 12 | 13 | Test methods: 14 | - expect(numAssertions) 15 | - stop(increment) 16 | - start(decrement) 17 | Test assertions: 18 | - ok(value, [message]) 19 | - equal(actual, expected, [message]) 20 | - notEqual(actual, expected, [message]) 21 | - deepEqual(actual, expected, [message]) 22 | - notDeepEqual(actual, expected, [message]) 23 | - strictEqual(actual, expected, [message]) 24 | - notStrictEqual(actual, expected, [message]) 25 | - raises(block, [expected], [message]) 26 | */ 27 | 28 | module('jqBootstrapValidation', { 29 | setup: function() { 30 | $("#qunit-fixture").append("\ 31 |
          \ 32 |
          \ 33 | \ 34 |
          \ 35 | \ 40 |
          \ 41 |
          \ 42 |
          \ 43 | \ 46 |
          \ 47 |
          \ 48 | "); 49 | attachJqbv(); 50 | this.elems = $("#qunit-fixture").children(); 51 | }, 52 | teardown: function() { 53 | $("#qunit-fixture").empty(); 54 | } 55 | }); 56 | 57 | module('load test', { 58 | setup: function() { 59 | $("#qunit-fixture").append($("#large-form").html()); 60 | attachJqbv(); 61 | }, 62 | teardown: function() { 63 | $("#qunit-fixture").empty(); 64 | } 65 | }); 66 | test('runs quickly', 1, function() { 67 | var $form = $("#qunit-fixture form").first(); 68 | var start = new Date(); 69 | $form.trigger("submit"); 70 | var elapsed = new Date() - start; 71 | var inputCount = $("#qunit-fixture form").find("textarea,input,select").not("[type=submit]").length; 72 | var timeAllowed = 25 * inputCount; // runs much slower in Grunt than a real browser, should aim for 30% of budget in browser. 73 | var percent = Math.floor((elapsed / timeAllowed) * 100) + "%"; 74 | ok( 75 | elapsed < timeAllowed, 76 | "Should run submit checks in less than " + timeAllowed + "ms - took " + elapsed + "ms; " + percent + " of budget." 77 | ); 78 | console.log("Should run submit checks in less than " + timeAllowed + "ms - took " + elapsed + "ms; " + percent + " of budget."); 79 | }); 80 | 81 | }(jQuery)); 82 | --------------------------------------------------------------------------------