1/** 2 * Uni-Form jQuery Plugin with Validation 3 * 4 * Provides form actions for use with the Uni-Form markup style 5 * This version adds additional support for client side validation 6 * 7 * Author: Ilija Studen for the purposes of Uni-Form 8 * 9 * Modified by Aris Karageorgos to use the parents function 10 * 11 * Modified by Toni Karlheinz to support input fields' text 12 * coloring and removal of their initial values on focus 13 * 14 * Modified by Jason Brumwell for optimization, addition 15 * of valid and invalid states and default data attribues 16 * 17 * Modified by LearningStation to add support for client 18 * side validation routines. The validation routines based on an 19 * open source library of unknown authorship. 20 * 21 * @see http://sprawsm.com/uni-form/ 22 * @license MIT http://www.opensource.org/licenses/mit-license.php 23 */ 24jQuery.fn.uniform = function(extended_settings) { 25 26 /** 27 * Self reference for closures 28 * 29 * @var object 30 */ 31 var self = this; 32 33 /** 34 * Object extending the defaults object 35 * 36 * @var object 37 */ 38 var settings = jQuery.extend( 39 jQuery.fn.uniform.defaults, 40 extended_settings 41 ); 42 43 /** 44 * Language abstration string 45 * 46 * to extend, use a script tag to include a file from the localization folder 47 * 48 * @var object 49 */ 50 var i18n_strings = jQuery.fn.uniform.language; 51 52 /** 53 * Supported validators 54 * 55 * @var Object 56 */ 57 this.validators = { 58 59 /** 60 * Get the value for a validator that takes parameters 61 * 62 * @param string name 63 * @param string all classes on element 64 * 65 * @return mixed || null 66 */ 67 get_val : function(name, classes, default_value) { 68 var value = default_value; 69 classes = classes.split(' '); 70 for(var i = 0; i < classes.length; i++) { 71 if(classes[i] == name) { 72 if((classes[i + 1] != 'undefined') && ('val-' === classes[i + 1].substr(0,4))) { 73 value = parseInt(classes[i + 1].substr(4),10); 74 return value; 75 } 76 } 77 } 78 return value; 79 }, 80 81 /** 82 * Value of field is not empty, whitespace will be counted as empty 83 * 84 * @param jQuery field 85 * @param string caption 86 */ 87 required : function(field, caption) { 88 if(field.is(':radio')) { 89 var name = field.attr('name'); 90 if($("input[name=" + name + "]:checked").length) { 91 return true; 92 } 93 return i18n('req_radio', caption); 94 } 95 if(field.is(':checkbox')) { 96 var name = field.attr('name'); 97 if(field.is(":checked")) { 98 return true; 99 } 100 return i18n('req_checkbox', caption); 101 } 102 if(jQuery.trim(field.val()) == '') { 103 return i18n('required', caption); 104 } 105 return true; 106 }, 107 108 /** 109 * Value is shorter than allowed 110 * 111 * @param jQuery field 112 * @param sting caption 113 */ 114 validateMinLength : function(field, caption) { 115 var min_length = this.get_val('validateMinLength', field.attr('class'), 0); 116 117 if((min_length > 0) && (field.val().length < min_length)) { 118 return i18n('minlength', caption, min_length); 119 } 120 return true; 121 }, 122 123 /** 124 * Value is less than min 125 * 126 * @param jQuery field 127 * @param sting caption 128 */ 129 validateMin : function(field, caption) { 130 var min_val = this.get_val('validateMin', field.attr('class'), 0); 131 132 if((parseInt(field.val(),10) < min_val)) { 133 return i18n('min', caption, min_val); 134 } 135 return true; 136 }, 137 138 /** 139 * Value is longer than allowed 140 * 141 * @param jQuery field 142 * @param string caption 143 */ 144 validateMaxLength : function(field, caption) { 145 var max_length = this.get_val('validateMaxLength', field.attr('class'), 0); 146 147 if((max_length > 0) && (field.val().length > max_length)) { 148 return i18n('maxlength', caption, max_length); 149 } 150 return true; 151 }, 152 153 /** 154 * Value is greater than max 155 * 156 * @param jQuery field 157 * @param sting caption 158 */ 159 validateMax : function(field, caption) { 160 var max_val = this.get_val('validateMax', field.attr('class'), 0); 161 162 if((parseInt(field.val(),10) > max_val)) { 163 return i18n('max', caption, max_val); 164 } 165 return true; 166 }, 167 168 /** 169 * Element has same value as that of the target Element 170 * 171 * This does not use the val-{name} format, and instead 172 * is only the name of the element 173 * 174 * class="validateSameAs field_id" 175 * 176 * @param jQuery field 177 * @param string caption 178 */ 179 validateSameAs : function(field, caption) { 180 var classes = field.attr('class').split(' '); 181 var target_field_name = ''; 182 183 for(var i = 0; i < classes.length; i++) { 184 if(classes[i] == 'validateSameAs') { 185 if(classes[i + 1] != 'undefined') { 186 target_field_name = classes[i + 1]; 187 break; 188 } 189 } 190 } 191 192 if(target_field_name) { 193 var target_field = field.closest('form').find('input[name="' + target_field_name + '"]'); 194 if(target_field.length > 0) { 195 if(target_field.val() != field.val()) { 196 var target_field_caption = target_field.closest('div.'+settings.holder_class).find('label').text().replace('*',''); 197 return i18n('same_as', caption, target_field_caption); 198 } 199 } 200 } 201 202 return true; 203 }, 204 205 /** 206 * Valid email address 207 * 208 * @param jQuery field 209 * @param string caption 210 */ 211 validateEmail : function(field, caption) { 212 if(field.val().match(/^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/)) { 213 return true; 214 } else { 215 return i18n('email', caption); 216 } 217 }, 218 219 /** 220 * Valid URL (http://,https://,ftp://) 221 * 222 * @param jQuery field 223 * @param string caption 224 */ 225 validateUrl : function(field, caption) { 226 if(field.val().match(/^(http|https|ftp):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(:(\d+))?\/?/i)) { 227 return true; 228 } 229 return i18n('url', caption); 230 }, 231 232 /** 233 * Number is only valid value (integers and floats) 234 * 235 * @param jQuery field 236 * @param string caption 237 */ 238 validateNumber : function(field, caption) { 239 if(field.val().match(/(^-?\d\d*\.\d*$)|(^-?\d\d*$)|(^-?\.\d\d*$)/) || field.val() == '') { 240 return true; 241 } 242 return i18n('number', caption); 243 }, 244 245 /** 246 * Whole numbers are allowed 247 * 248 * @param jQuery field 249 * @param string caption 250 */ 251 validateInteger : function(field, caption) { 252 if(field.val().match(/(^-?\d\d*$)/) || field.val() == '') { 253 return true; 254 } 255 return i18n('integer', caption); 256 }, 257 258 /** 259 * Letters only 260 * 261 * @param jQuery field 262 * @param string caption 263 */ 264 validateAlpha : function(field, caption) { 265 if(field.val().match(/^[a-zA-Z]+$/)) { 266 return true; 267 } 268 return i18n('alpha', caption); 269 }, 270 271 /** 272 * Letters and numbers 273 * 274 * @param jQuery field 275 * @param string caption 276 */ 277 validateAlphaNum : function(field, caption) { 278 if(field.val().match(/\W/)) { 279 return i18n('alphanum', caption); 280 } 281 return true; 282 }, 283 284 /** 285 * Simple phrases 286 * 287 * @param jQuery field 288 * @param string caption 289 */ 290 validatePhrase : function(field, caption) { 291 if((field.val() == '') || field.val().match(/^[\w\d\.\-_\(\)\*'# :,]+$/i)) { 292 return true; 293 } 294 return i18n('phrase', caption); 295 }, 296 297 /** 298 * Phone number 299 * 300 * @param jQuery field 301 * @param string caption 302 */ 303 validatePhone : function(field, caption) { 304 phoneNumber = /^\(?(\d{3})\)?[- ]?(\d{3})[- ]?(\d{4})$/; 305 if(phoneNumber.test(field.val())) { 306 return true; 307 } 308 return i18n('phone', caption); 309 }, 310 311 /** 312 * Date in MM/DD/YYYY format 313 * 314 * @param jQuery field 315 * @param string caption 316 */ 317 validateDate : function(field, caption) { 318 if(field.val().match('(1[0-9]|[1-9])/([1-3][0-9]|[1-9])/((19|20)[0-9][0-9]|[0-9][0-9])')) { 319 return true; 320 } 321 return i18n('date', caption); 322 }, 323 324 /** 325 * Callback validator 326 * 327 * Lets you define your own validators. Usage: 328 * 329 * <input name="myinput" class="validateCallback my_callback" /> 330 * 331 * This will result in UniForm searching for window.my_callback funciton and 332 * executing it with field and caption arguments. Sample implementation: 333 * 334 * window.my_callback = function(field, caption) { 335 * if(field.val() == '34') { 336 * return true; 337 * } else { 338 * return caption + ' value should be "34"'; 339 * } 340 * } 341 * 342 * @param jQuery field 343 * @param caption 344 */ 345 validateCallback : function(field, caption) { 346 var classes = field.attr('class').split(' '); 347 var callback_function = ''; 348 349 for(var i = 0; i < classes.length; i++) { 350 if(classes[i] == 'validateCallback') { 351 if(classes[i + 1] != 'undefined') { 352 callback_function = classes[i + 1]; 353 break; 354 } 355 } 356 } 357 358 if(window[callback_function] != 'undefined' && (typeof window[callback_function] == 'function')) { 359 return window[callback_function](field, caption); 360 } 361 362 return i18n('callback', caption, callback_function); 363 } 364 365 }; 366 367 /** 368 * Simple replacement for i18n + sprintf 369 * 370 * @param string language for for the local i18n object 371 * @param mixed substitution vars 372 * 373 * @return internationalized string 374 */ 375 var i18n = function(lang_key) {
376 var lang_string = i18n_strings[lang_key]; 377 var bits = lang_string.split('%'); 378 var out = bits[0]; 379 var re = /^([ds])(.*)$/; 380 for (var i=1; i<bits.length; i++) { 381 p = re.exec(bits[i]); 382 if (!p || arguments[i] == null) continue; 383 if (p[1] == 'd') { 384 out += parseInt(arguments[i], 10); 385 } else if (p[1] == 's') { 386 out += arguments[i]; 387 } 388 out += p[2]; 389 } 390 return out; 391 }; 392 393 /** 394 * Uni-Form form validation error 395 * 396 * @param string title of the form error 397 * @param array list of error messages to show 398 * 399 * @return false 400 */ 401 var showFormError = function(form, title, messages) { 402 if($('#errorMsg').length) { 403 $('#errorMsg').remove(); 404 } 405 $message = 406 $('<div />') 407 .attr('id','errorMsg') 408 .html("<h3>" + title + "</h3>"); 409 if(messages.length) { 410 $message.append($('<ol />')); 411 for(m in messages) { 412 $('ol', $message).append( 413 $('<li />').text(messages[m]) 414 ); 415 } 416 } 417 form.prepend($message); 418 $('html, body').animate({ 419 scrollTop: form.offset().top 420 }, 500); 421 $('#errorMsg').slideDown(); 422 return false; 423 }; 424 425 var showFormSuccess = function(form, title) { 426 if($('#okMsg').length) { 427 $('#okMsg').remove(); 428 } 429 $message = 430 $('<div />') 431 .attr('id','okMsg') 432 .html("<h3>" + title + "</h3>"); 433 form.prepend($message); 434 $('html, body').animate({ 435 scrollTop: form.offset().top 436 }, 500); 437 $('#okMsg').slideDown(); 438 return false; 439 }; 440 441 return this.each(function() { 442 var form = jQuery(this); 443 444 /** 445 * Set the results of form validation on the form element 446 * 447 * @param object $input jQuery form element 448 * @param bool valid true if the form value passed all validation 449 * @param string text validation error message to display 450 * 451 * @return null 452 */ 453 var validate = function($input,valid,text) { 454 var $p = $input.closest('div.' + settings.holder_class) 455 .andSelf() 456 .toggleClass(settings.invalid_class, !valid) 457 .toggleClass(settings.error_class, !valid) 458 .toggleClass(settings.valid_class, valid) 459 .find('p.formHint'); 460 461 if (! valid && ! $p.data('info-text')) { 462 $p.data('info-text', $p.html()); 463 } 464 else if (valid) { 465 text = $p.data('info-text'); 466 } 467 468 if (text) { 469 $p.html(text); 470 } 471 }; 472 473 /** 474 * Select form fields and attach the higlighter functionality 475 * 476 */ 477 form.find(settings.field_selector).each(function(){ 478 var $input = $(this), 479 value = $input.val(); 480 481 $input.data('default-color',$input.css('color')); 482 483 if (value === $input.data('default-value') || ! value) { 484 $input.not('select').css("color", settings.default_value_color); 485 $input.val($input.data('default-value')); 486 } 487 }); 488 489 /** 490 * If we've set ask_on_leave we'll register a handler here 491 * 492 * We need to seriaze the form data, wait for a beforeunload, 493 * then serialize and compare for changes 494 * 495 * If they changed things, and haven't submitted, we'll let them 496 * know about it 497 * 498 */ 499 if(settings.ask_on_leave || form.hasClass('askOnLeave')) { 500 var initial_values = form.serialize(); 501 $(window).bind("beforeunload", function(e) { 502 if((initial_values != form.serialize()) 503 && (settings.ask_on_leave || form.hasClass('askOnLeave')) 504 ) { 505 return ($.isFunction(settings.on_leave_callback)) 506 ? settings.on_leave_callback(form) 507 : confirm(i18n('on_leave')); 508 } 509 }); 510 } 511 512 /** 513 * Handle the submission of the form 514 * 515 * Tasks 516 * * Remove any default values from the form 517 * * If prevent_submit is set true, return false if 518 * there are outstanding errors in the form 519 * 520 * Todo 521 * * it would be novel to use prevent_submit to disable 522 * the submit button in the blur handler 523 * 524 * @return bool 525 */ 526 form.submit(function(){ 527 // in the case of a previously failed submit, we'll remove our marker 528 form.removeClass('failedSubmit'); 529 530 // remove the default values from the val() where they were being displayed 531 form.find(settings.field_selector).each(function(){ 532 if($(this).val() === $(this).data('default-value')) { $(this).val(""); } 533 }); 534 535 // traverse and revalidate making sure that we haven't missed any fields 536 // perhaps if a field was filled in before uniform was initialized 537 // or if blur failed to fire correctly 538 if(settings.prevent_submit || form.hasClass('preventSubmit')) { 539 // use blur to run the validators on each field 540 form.find(settings.field_selector).each(function(){ 541 $(this).blur(); 542 }); 543 544 if (form 545 .find('.' + settings.invalid_class) 546 .add('.' + settings.error_class).length 547 ) { 548 form.addClass('failedSubmit'); 549 return ($.isFunction(settings.prevent_submit_callback)) 550 ? settings.prevent_submit_callback(form) 551 : showFormError(form, i18n('submit_msg'), [i18n('submit_help')]); 552 } 553 554 settings.ask_on_leave = false;
555 form.removeClass('askOnLeave'); 556 return true; 557 } 558 559 // qUnit needs to run this function, and still prevent the submit 560 if(form.parents('#qunit-fixture').length) { 561 return false; 562 } 563 564 settings.ask_on_leave = false; 565 form.removeClass('askOnLeave'); 566 return true; 567 }); 568 569 /** 570 * Set the form focus class 571 * 572 * Remove any classes other than the focus class and hide the default label text 573 * 574 */ 575 form.delegate(settings.field_selector, 'focus', function() { 576 form.find('.' + settings.focused_class).removeClass(settings.focused_class); 577 578 var $input = $(this); 579 580 $input.parents().filter('.'+settings.holder_class+':first').addClass(settings.focused_class); 581 582 if($input.val() === $input.data('default-value')){ 583 $input.val(""); 584 } 585 586 $input.not('select').css('color',$input.data('default-color')); 587 }); 588 589 /** 590 * Validate a form field on the blur event 591 * 592 * Search the classnames on the element for the names of 593 * validators, and run them as we find them 594 * 595 * If the validators fail, we trigger either 'success' or 'error' events 596 * 597 */ 598 form.delegate(settings.field_selector, 'blur', function() { 599 var $input = $(this); 600 var label = $(this) 601 .closest('div.' + settings.holder_class) 602 .find('label').text().replace('*',''); 603 604 // remove focus from form element 605 form.find('.' + settings.focused_class).removeClass(settings.focused_class); 606 607 // (if empty or equal to default value) AND not required 608 if(($input.val() === "" || $input.val() === $input.data('default-value')) 609 && !$input.hasClass('required') 610 ){ 611 $input.not('select').css("color",settings.default_value_color); 612 $input.val($input.data('default-value')); 613 return; 614 } 615 616 // run the validation and if they all pass, we mark the color and move on 617 var has_validation = false; 618 for(validator in self.validators) { 619 if($input.hasClass(validator)){ 620 has_validation = true; 621 var validation_result = self.validators[validator]($input, label); 622 if(typeof(validation_result) == 'string') { 623 $input.trigger('error', validation_result); 624 return; 625 } 626 } 627 } 628 629 // if it had validation and we didn't return above, 630 // then all validation passed 631 if (has_validation) { 632 $input.trigger('success'); 633 } 634 635 // return the color to the default 636 $input.css('color', $input.data('default-color')); 637 return; 638 }); 639 640 /** 641 * Handle a validation error in the form element 642 * 643 * This will set the field to have the error marker 644 * and update the warning text 645 * 646 * @param event e 647 * @param string validation message 648 */ 649 form.delegate(settings.field_selector,'error',function(e,text) { 650 validate($(this), false, text); 651 }); 652 653 /** 654 * Handle a succesful validation in the form element 655 * 656 * Remove any error messages and set the validation 657 * marker to be success 658 * 659 * @param event e 660 * @param string unused 661 */ 662 form.delegate(settings.field_selector,'success',function(e,text) { 663 validate($(this), true); 664 }); 665 }); 666}; 667 668/** 669 * Internationalized language strings for validation messages 670 */ 671jQuery.fn.uniform.language = { 672 required : '%s is required', 673 req_radio : 'Please make a selection', 674 req_checkbox : 'You must select this checkbox to continue', 675 minlength : '%s should be at least %d characters long', 676 min : '%s should be greater than or equal to %d', 677 maxlength : '%s should not be longer than %d characters', 678 max : '%s should be less than or equal to %d', 679 same_as : '%s is expected to be same as %s', 680 email : '%s is not a valid email address', 681 url : '%s is not a valid URL', 682 number : '%s needs to be a number', 683 integer : '%s needs to be a whole number',
684 alpha : '%s should contain only letters (without special characters or numbers)', 685 alphanum : '%s should contain only numbers and letters (without special characters)', 686 phrase : '%s should contain only alphabetic characters, numbers, spaces, and the following: . , - _ () * # :', 687 phone : '%s should be a phone number', 688 date : '%s should be a date (mm/dd/yyyy)', 689 callback : 'Failed to validate %s field. Validator function (%s) is not defined!', 690 on_leave : 'Are you sure you want to leave this page without saving this form?', 691 submit_msg : 'Sorry, this form needs corrections.', 692 submit_help : 'Please see the items marked below.', 693 submit_success: 'Thank you, this form has been sent.' 694}; 695 696/** 697 * See the validation.md file for more information about these options 698 */ 699jQuery.fn.uniform.defaults = { 700 prevent_submit : false, 701 prevent_submit_callback : false, 702 ask_on_leave : false, 703 on_leave_callback : false, 704 valid_class : 'valid', 705 invalid_class : 'invalid', 706 error_class : 'error', 707 focused_class : 'focused', 708 holder_class : 'ctrlHolder', 709 field_selector : 'input, textarea, select', 710 default_value_color : "#AFAFAF" 711};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.