github.com/fanux/shipyard@v0.0.0-20161009071005-6515ce223235/controller/static/semantic/dist/components/form.js (about) 1 /*! 2 * # Semantic UI x.x - Form Validation 3 * http://github.com/semantic-org/semantic-ui/ 4 * 5 * 6 * Copyright 2014 Contributors 7 * Released under the MIT license 8 * http://opensource.org/licenses/MIT 9 * 10 */ 11 12 ;(function ( $, window, document, undefined ) { 13 14 "use strict"; 15 16 $.fn.form = function(fields, parameters) { 17 var 18 $allModules = $(this), 19 20 settings = $.extend(true, {}, $.fn.form.settings, parameters), 21 validation = $.extend({}, $.fn.form.settings.defaults, fields), 22 23 namespace = settings.namespace, 24 metadata = settings.metadata, 25 selector = settings.selector, 26 className = settings.className, 27 error = settings.error, 28 29 eventNamespace = '.' + namespace, 30 moduleNamespace = 'module-' + namespace, 31 32 moduleSelector = $allModules.selector || '', 33 34 time = new Date().getTime(), 35 performance = [], 36 37 query = arguments[0], 38 methodInvoked = (typeof query == 'string'), 39 queryArguments = [].slice.call(arguments, 1), 40 returnedValue 41 ; 42 $allModules 43 .each(function() { 44 var 45 $module = $(this), 46 $field = $(this).find(selector.field), 47 $group = $(this).find(selector.group), 48 $message = $(this).find(selector.message), 49 $prompt = $(this).find(selector.prompt), 50 51 $submit = $(this).find(selector.submit), 52 $clear = $(this).find(selector.clear), 53 $reset = $(this).find(selector.reset), 54 55 formErrors = [], 56 keyHeldDown = false, 57 58 element = this, 59 instance = $module.data(moduleNamespace), 60 module 61 ; 62 63 module = { 64 65 initialize: function() { 66 module.verbose('Initializing form validation', $module, validation, settings); 67 module.bindEvents(); 68 module.set.defaults(); 69 module.instantiate(); 70 }, 71 72 instantiate: function() { 73 module.verbose('Storing instance of module', module); 74 instance = module; 75 $module 76 .data(moduleNamespace, module) 77 ; 78 }, 79 80 destroy: function() { 81 module.verbose('Destroying previous module', instance); 82 module.removeEvents(); 83 $module 84 .removeData(moduleNamespace) 85 ; 86 }, 87 88 refresh: function() { 89 module.verbose('Refreshing selector cache'); 90 $field = $module.find(selector.field); 91 }, 92 93 submit: function() { 94 module.verbose('Submitting form', $module); 95 $module 96 .submit() 97 ; 98 }, 99 100 attachEvents: function(selector, action) { 101 action = action || 'submit'; 102 $(selector) 103 .on('click', function(event) { 104 module[action](); 105 event.preventDefault(); 106 }) 107 ; 108 }, 109 110 bindEvents: function() { 111 if(settings.keyboardShortcuts) { 112 $field 113 .on('keydown' + eventNamespace, module.event.field.keydown) 114 ; 115 } 116 $module 117 .on('submit' + eventNamespace, module.validate.form) 118 ; 119 $field 120 .on('blur' + eventNamespace, module.event.field.blur) 121 ; 122 123 // attach events to common elements 124 module.attachEvents($submit, 'submit'); 125 module.attachEvents($reset, 'reset'); 126 module.attachEvents($clear, 'clear'); 127 128 $field 129 .each(function() { 130 var 131 type = $(this).prop('type'), 132 inputEvent = module.get.changeEvent(type) 133 ; 134 $(this) 135 .on(inputEvent + eventNamespace, module.event.field.change) 136 ; 137 }) 138 ; 139 }, 140 141 clear: function() { 142 $field 143 .each(function () { 144 var 145 $field = $(this), 146 $element = $field.parent(), 147 $fieldGroup = $field.closest($group), 148 $prompt = $fieldGroup.find(selector.prompt), 149 defaultValue = $field.data(metadata.defaultValue) || '', 150 isCheckbox = $element.is(selector.uiCheckbox), 151 isDropdown = $element.is(selector.uiDropdown), 152 isErrored = $fieldGroup.hasClass(className.error) 153 ; 154 if(isErrored) { 155 module.verbose('Resetting error on field', $fieldGroup); 156 $fieldGroup.removeClass(className.error); 157 $prompt.remove(); 158 } 159 if(isDropdown) { 160 module.verbose('Resetting dropdown value', $element, defaultValue); 161 $element.dropdown('clear'); 162 } 163 else if(isCheckbox) { 164 $element.checkbox('uncheck'); 165 } 166 else { 167 module.verbose('Resetting field value', $field, defaultValue); 168 $field.val(''); 169 } 170 }) 171 ; 172 }, 173 174 reset: function() { 175 $field 176 .each(function () { 177 var 178 $field = $(this), 179 $element = $field.parent(), 180 $fieldGroup = $field.closest($group), 181 $prompt = $fieldGroup.find(selector.prompt), 182 defaultValue = $field.data(metadata.defaultValue) || '', 183 isCheckbox = $element.is(selector.uiCheckbox), 184 isDropdown = $element.is(selector.uiDropdown), 185 isErrored = $fieldGroup.hasClass(className.error) 186 ; 187 if(isErrored) { 188 module.verbose('Resetting error on field', $fieldGroup); 189 $fieldGroup.removeClass(className.error); 190 $prompt.remove(); 191 } 192 if(isDropdown) { 193 module.verbose('Resetting dropdown value', $element, defaultValue); 194 $element.dropdown('restore defaults'); 195 } 196 else if(isCheckbox) { 197 module.verbose('Resetting checkbox value', $element, defaultValue); 198 if(defaultValue === true) { 199 $element.checkbox('check'); 200 } 201 else { 202 $element.checkbox('uncheck'); 203 } 204 } 205 else { 206 module.verbose('Resetting field value', $field, defaultValue); 207 $field.val(defaultValue); 208 } 209 }) 210 ; 211 }, 212 213 removeEvents: function() { 214 $module 215 .off(eventNamespace) 216 ; 217 $field 218 .off(eventNamespace) 219 ; 220 $submit 221 .off(eventNamespace) 222 ; 223 $field 224 .off(eventNamespace) 225 ; 226 }, 227 228 event: { 229 field: { 230 keydown: function(event) { 231 var 232 $field = $(this), 233 key = event.which, 234 keyCode = { 235 enter : 13, 236 escape : 27 237 } 238 ; 239 if( key == keyCode.escape) { 240 module.verbose('Escape key pressed blurring field'); 241 $field 242 .blur() 243 ; 244 } 245 if(!event.ctrlKey && key == keyCode.enter && $field.is(selector.input) && $field.not(selector.checkbox).length > 0 ) { 246 $submit 247 .addClass(className.pressed) 248 ; 249 if(!keyHeldDown) { 250 $field 251 .one('keyup' + eventNamespace, module.event.field.keyup) 252 ; 253 module.submit(); 254 module.debug('Enter pressed on input submitting form'); 255 } 256 keyHeldDown = true; 257 } 258 }, 259 keyup: function() { 260 keyHeldDown = false; 261 $submit.removeClass(className.pressed); 262 }, 263 blur: function() { 264 var 265 $field = $(this), 266 $fieldGroup = $field.closest($group) 267 ; 268 if( $fieldGroup.hasClass(className.error) ) { 269 module.debug('Revalidating field', $field, module.get.validation($field)); 270 module.validate.field( module.get.validation($field) ); 271 } 272 else if(settings.on == 'blur' || settings.on == 'change') { 273 module.validate.field( module.get.validation($field) ); 274 } 275 }, 276 change: function() { 277 var 278 $field = $(this), 279 $fieldGroup = $field.closest($group) 280 ; 281 if(settings.on == 'change' || ( $fieldGroup.hasClass(className.error) && settings.revalidate) ) { 282 clearTimeout(module.timer); 283 module.timer = setTimeout(function() { 284 module.debug('Revalidating field', $field, module.get.validation($field)); 285 module.validate.field( module.get.validation($field) ); 286 }, settings.delay); 287 } 288 } 289 } 290 291 }, 292 293 get: { 294 changeEvent: function(type) { 295 if(type == 'checkbox' || type == 'radio' || type == 'hidden') { 296 return 'change'; 297 } 298 else { 299 return module.get.inputEvent(); 300 } 301 }, 302 inputEvent: function() { 303 return (document.createElement('input').oninput !== undefined) 304 ? 'input' 305 : (document.createElement('input').onpropertychange !== undefined) 306 ? 'propertychange' 307 : 'keyup' 308 ; 309 }, 310 field: function(identifier) { 311 module.verbose('Finding field with identifier', identifier); 312 if( $field.filter('#' + identifier).length > 0 ) { 313 return $field.filter('#' + identifier); 314 } 315 else if( $field.filter('[name="' + identifier +'"]').length > 0 ) { 316 return $field.filter('[name="' + identifier +'"]'); 317 } 318 else if( $field.filter('[name="' + identifier +'[]"]').length > 0 ) { 319 return $field.filter('[name="' + identifier +'[]"]'); 320 } 321 else if( $field.filter('[data-' + metadata.validate + '="'+ identifier +'"]').length > 0 ) { 322 return $field.filter('[data-' + metadata.validate + '="'+ identifier +'"]'); 323 } 324 return $('<input/>'); 325 }, 326 fields: function(fields) { 327 var 328 $fields = $() 329 ; 330 $.each(fields, function(index, name) { 331 $fields = $fields.add( module.get.field(name) ); 332 }); 333 return $fields; 334 }, 335 validation: function($field) { 336 var 337 rules 338 ; 339 $.each(validation, function(fieldName, field) { 340 if( module.get.field(field.identifier).get(0) == $field.get(0) ) { 341 rules = field; 342 } 343 }); 344 return rules || false; 345 }, 346 value: function (field) { 347 var 348 fields = [], 349 results 350 ; 351 fields.push(field); 352 results = module.get.values.call(element, fields); 353 return results[field]; 354 }, 355 values: function (fields) { 356 var 357 $fields = $.isArray(fields) 358 ? module.get.fields(fields) 359 : $field, 360 values = {} 361 ; 362 $fields.each(function(index, field) { 363 var 364 $field = $(field), 365 type = $field.prop('type'), 366 name = $field.prop('name'), 367 value = $field.val(), 368 isCheckbox = $field.is(selector.checkbox), 369 isRadio = $field.is(selector.radio), 370 isMultiple = (name.indexOf('[]') !== -1), 371 isChecked = (isCheckbox) 372 ? $field.is(':checked') 373 : false 374 ; 375 if(name) { 376 if(isMultiple) { 377 name = name.replace('[]', ''); 378 if(!values[name]) { 379 values[name] = []; 380 } 381 if(isCheckbox) { 382 if(isChecked) { 383 values[name].push(value) 384 } 385 else { 386 module.debug('Omitted unchecked checkbox', $field); 387 return true; 388 } 389 } 390 else { 391 values[name].push(value); 392 } 393 } 394 else { 395 if(isRadio) { 396 if(isChecked) { 397 values[name] = value; 398 } 399 } 400 else if(isCheckbox) { 401 if(isChecked) { 402 values[name] = true; 403 } 404 else { 405 module.debug('Omitted unchecked checkbox', $field); 406 return true; 407 } 408 } 409 else { 410 values[name] = value; 411 } 412 } 413 } 414 }); 415 return values; 416 } 417 }, 418 419 has: { 420 421 field: function(identifier) { 422 module.verbose('Checking for existence of a field with identifier', identifier); 423 if( $field.filter('#' + identifier).length > 0 ) { 424 return true; 425 } 426 else if( $field.filter('[name="' + identifier +'"]').length > 0 ) { 427 return true; 428 } 429 else if( $field.filter('[data-' + metadata.validate + '="'+ identifier +'"]').length > 0 ) { 430 return true; 431 } 432 return false; 433 } 434 435 }, 436 437 add: { 438 prompt: function(identifier, errors) { 439 var 440 $field = module.get.field(identifier), 441 $fieldGroup = $field.closest($group), 442 $prompt = $fieldGroup.children(selector.prompt), 443 promptExists = ($prompt.length !== 0) 444 ; 445 errors = (typeof errors == 'string') 446 ? [errors] 447 : errors 448 ; 449 module.verbose('Adding field error state', identifier); 450 $fieldGroup 451 .addClass(className.error) 452 ; 453 if(settings.inline) { 454 if(!promptExists) { 455 $prompt = settings.templates.prompt(errors); 456 $prompt 457 .appendTo($fieldGroup) 458 ; 459 } 460 $prompt 461 .html(errors[0]) 462 ; 463 if(!promptExists) { 464 if(settings.transition && $.fn.transition !== undefined && $module.transition('is supported')) { 465 module.verbose('Displaying error with css transition', settings.transition); 466 $prompt.transition(settings.transition + ' in', settings.duration); 467 } 468 else { 469 module.verbose('Displaying error with fallback javascript animation'); 470 $prompt 471 .fadeIn(settings.duration) 472 ; 473 } 474 } 475 else { 476 module.verbose('Inline errors are disabled, no inline error added', identifier); 477 } 478 } 479 }, 480 errors: function(errors) { 481 module.debug('Adding form error messages', errors); 482 $message 483 .html( settings.templates.error(errors) ) 484 ; 485 } 486 }, 487 488 remove: { 489 prompt: function(field) { 490 var 491 $field = module.get.field(field.identifier), 492 $fieldGroup = $field.closest($group), 493 $prompt = $fieldGroup.children(selector.prompt) 494 ; 495 $fieldGroup 496 .removeClass(className.error) 497 ; 498 if(settings.inline && $prompt.is(':visible')) { 499 module.verbose('Removing prompt for field', field); 500 if(settings.transition && $.fn.transition !== undefined && $module.transition('is supported')) { 501 $prompt.transition(settings.transition + ' out', settings.duration, function() { 502 $prompt.remove(); 503 }); 504 } 505 else { 506 $prompt 507 .fadeOut(settings.duration, function(){ 508 $prompt.remove(); 509 }) 510 ; 511 } 512 } 513 } 514 }, 515 516 set: { 517 success: function() { 518 $module 519 .removeClass(className.error) 520 .addClass(className.success) 521 ; 522 }, 523 defaults: function () { 524 $field 525 .each(function () { 526 var 527 $field = $(this), 528 isCheckbox = ($field.filter(selector.checkbox).length > 0), 529 value = (isCheckbox) 530 ? $field.is(':checked') 531 : $field.val() 532 ; 533 $field.data(metadata.defaultValue, value); 534 }) 535 ; 536 }, 537 error: function() { 538 $module 539 .removeClass(className.success) 540 .addClass(className.error) 541 ; 542 }, 543 value: function (field, value) { 544 var 545 fields = {} 546 ; 547 fields[field] = value; 548 return module.set.values.call(element, fields); 549 }, 550 values: function (fields) { 551 if($.isEmptyObject(fields)) { 552 return; 553 } 554 $.each(fields, function(key, value) { 555 var 556 $field = module.get.field(key), 557 $element = $field.parent(), 558 isMultiple = $.isArray(value), 559 isCheckbox = $element.is(selector.uiCheckbox), 560 isDropdown = $element.is(selector.uiDropdown), 561 isRadio = ($field.is(selector.radio) && isCheckbox), 562 fieldExists = ($field.length > 0), 563 $multipleField 564 ; 565 if(fieldExists) { 566 if(isMultiple && isCheckbox) { 567 module.verbose('Selecting multiple', value, $field); 568 $element.checkbox('uncheck'); 569 $.each(value, function(index, value) { 570 $multipleField = $field.filter('[value="' + value + '"]'); 571 $element = $multipleField.parent(); 572 if($multipleField.length > 0) { 573 $element.checkbox('check'); 574 } 575 }); 576 } 577 else if(isRadio) { 578 module.verbose('Selecting radio value', value, $field); 579 $field.filter('[value="' + value + '"]') 580 .parent(selector.uiCheckbox) 581 .checkbox('check') 582 ; 583 } 584 else if(isCheckbox) { 585 module.verbose('Setting checkbox value', value, $element); 586 if(value === true) { 587 $element.checkbox('check'); 588 } 589 else { 590 $element.checkbox('uncheck'); 591 } 592 } 593 else if(isDropdown) { 594 module.verbose('Setting dropdown value', value, $element); 595 $element.dropdown('set selected', value); 596 } 597 else { 598 module.verbose('Setting field value', value, $field); 599 $field.val(value); 600 } 601 } 602 }); 603 module.validate.form(); 604 } 605 }, 606 607 validate: { 608 609 form: function(event) { 610 var 611 allValid = true, 612 apiRequest 613 ; 614 615 // input keydown event will fire submit repeatedly by browser default 616 if(keyHeldDown) { 617 return false; 618 } 619 620 // reset errors 621 formErrors = []; 622 $.each(validation, function(fieldName, field) { 623 if( !( module.validate.field(field) ) ) { 624 allValid = false; 625 } 626 }); 627 if(allValid) { 628 module.debug('Form has no validation errors, submitting'); 629 module.set.success(); 630 return settings.onSuccess.call(element, event); 631 } 632 else { 633 module.debug('Form has errors'); 634 module.set.error(); 635 if(!settings.inline) { 636 module.add.errors(formErrors); 637 } 638 // prevent ajax submit 639 if($module.data('moduleApi') !== undefined) { 640 event.stopImmediatePropagation(); 641 } 642 return settings.onFailure.call(element, formErrors); 643 } 644 }, 645 646 // takes a validation object and returns whether field passes validation 647 field: function(field) { 648 var 649 $field = module.get.field(field.identifier), 650 fieldValid = true, 651 fieldErrors = [] 652 ; 653 if($field.prop('disabled')) { 654 module.debug('Field is disabled. Skipping', field.identifier); 655 fieldValid = true; 656 } 657 else if(field.optional && $.trim($field.val()) === ''){ 658 module.debug('Field is optional and empty. Skipping', field.identifier); 659 fieldValid = true; 660 } 661 else if(field.rules !== undefined) { 662 $.each(field.rules, function(index, rule) { 663 if( module.has.field(field.identifier) && !( module.validate.rule(field, rule) ) ) { 664 module.debug('Field is invalid', field.identifier, rule.type); 665 fieldErrors.push(rule.prompt); 666 fieldValid = false; 667 } 668 }); 669 } 670 if(fieldValid) { 671 module.remove.prompt(field, fieldErrors); 672 settings.onValid.call($field); 673 } 674 else { 675 formErrors = formErrors.concat(fieldErrors); 676 module.add.prompt(field.identifier, fieldErrors); 677 settings.onInvalid.call($field, fieldErrors); 678 return false; 679 } 680 return true; 681 }, 682 683 // takes validation rule and returns whether field passes rule 684 rule: function(field, validation) { 685 var 686 $field = module.get.field(field.identifier), 687 type = validation.type, 688 value = $.trim($field.val() + ''), 689 690 bracketRegExp = /\[(.*)\]/i, 691 bracket = bracketRegExp.exec(type), 692 isValid = true, 693 ancillary, 694 functionType 695 ; 696 // if bracket notation is used, pass in extra parameters 697 if(bracket !== undefined && bracket !== null) { 698 ancillary = '' + bracket[1]; 699 functionType = type.replace(bracket[0], ''); 700 isValid = settings.rules[functionType].call(element, value, ancillary); 701 } 702 // normal notation 703 else { 704 isValid = settings.rules[type].call($field, value); 705 } 706 return isValid; 707 } 708 }, 709 710 setting: function(name, value) { 711 if( $.isPlainObject(name) ) { 712 $.extend(true, settings, name); 713 } 714 else if(value !== undefined) { 715 settings[name] = value; 716 } 717 else { 718 return settings[name]; 719 } 720 }, 721 internal: function(name, value) { 722 if( $.isPlainObject(name) ) { 723 $.extend(true, module, name); 724 } 725 else if(value !== undefined) { 726 module[name] = value; 727 } 728 else { 729 return module[name]; 730 } 731 }, 732 debug: function() { 733 if(settings.debug) { 734 if(settings.performance) { 735 module.performance.log(arguments); 736 } 737 else { 738 module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':'); 739 module.debug.apply(console, arguments); 740 } 741 } 742 }, 743 verbose: function() { 744 if(settings.verbose && settings.debug) { 745 if(settings.performance) { 746 module.performance.log(arguments); 747 } 748 else { 749 module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':'); 750 module.verbose.apply(console, arguments); 751 } 752 } 753 }, 754 error: function() { 755 module.error = Function.prototype.bind.call(console.error, console, settings.name + ':'); 756 module.error.apply(console, arguments); 757 }, 758 performance: { 759 log: function(message) { 760 var 761 currentTime, 762 executionTime, 763 previousTime 764 ; 765 if(settings.performance) { 766 currentTime = new Date().getTime(); 767 previousTime = time || currentTime; 768 executionTime = currentTime - previousTime; 769 time = currentTime; 770 performance.push({ 771 'Name' : message[0], 772 'Arguments' : [].slice.call(message, 1) || '', 773 'Element' : element, 774 'Execution Time' : executionTime 775 }); 776 } 777 clearTimeout(module.performance.timer); 778 module.performance.timer = setTimeout(module.performance.display, 100); 779 }, 780 display: function() { 781 var 782 title = settings.name + ':', 783 totalTime = 0 784 ; 785 time = false; 786 clearTimeout(module.performance.timer); 787 $.each(performance, function(index, data) { 788 totalTime += data['Execution Time']; 789 }); 790 title += ' ' + totalTime + 'ms'; 791 if(moduleSelector) { 792 title += ' \'' + moduleSelector + '\''; 793 } 794 if($allModules.length > 1) { 795 title += ' ' + '(' + $allModules.length + ')'; 796 } 797 if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) { 798 console.groupCollapsed(title); 799 if(console.table) { 800 console.table(performance); 801 } 802 else { 803 $.each(performance, function(index, data) { 804 console.log(data['Name'] + ': ' + data['Execution Time']+'ms'); 805 }); 806 } 807 console.groupEnd(); 808 } 809 performance = []; 810 } 811 }, 812 invoke: function(query, passedArguments, context) { 813 var 814 object = instance, 815 maxDepth, 816 found, 817 response 818 ; 819 passedArguments = passedArguments || queryArguments; 820 context = element || context; 821 if(typeof query == 'string' && object !== undefined) { 822 query = query.split(/[\. ]/); 823 maxDepth = query.length - 1; 824 $.each(query, function(depth, value) { 825 var camelCaseValue = (depth != maxDepth) 826 ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1) 827 : query 828 ; 829 if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) { 830 object = object[camelCaseValue]; 831 } 832 else if( object[camelCaseValue] !== undefined ) { 833 found = object[camelCaseValue]; 834 return false; 835 } 836 else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) { 837 object = object[value]; 838 } 839 else if( object[value] !== undefined ) { 840 found = object[value]; 841 return false; 842 } 843 else { 844 return false; 845 } 846 }); 847 } 848 if( $.isFunction( found ) ) { 849 response = found.apply(context, passedArguments); 850 } 851 else if(found !== undefined) { 852 response = found; 853 } 854 if($.isArray(returnedValue)) { 855 returnedValue.push(response); 856 } 857 else if(returnedValue !== undefined) { 858 returnedValue = [returnedValue, response]; 859 } 860 else if(response !== undefined) { 861 returnedValue = response; 862 } 863 return found; 864 } 865 }; 866 if(methodInvoked) { 867 if(instance === undefined) { 868 module.initialize(); 869 } 870 module.invoke(query); 871 } 872 else { 873 if(instance !== undefined) { 874 instance.invoke('destroy'); 875 } 876 module.initialize(); 877 } 878 879 }) 880 ; 881 882 return (returnedValue !== undefined) 883 ? returnedValue 884 : this 885 ; 886 }; 887 888 $.fn.form.settings = { 889 890 name : 'Form', 891 namespace : 'form', 892 893 debug : false, 894 verbose : true, 895 performance : true, 896 897 898 keyboardShortcuts : true, 899 on : 'submit', 900 inline : false, 901 902 delay : 200, 903 revalidate : true, 904 905 transition : 'scale', 906 duration : 200, 907 908 onValid : function() {}, 909 onInvalid : function() {}, 910 onSuccess : function() { return true; }, 911 onFailure : function() { return false; }, 912 913 metadata : { 914 defaultValue : 'default', 915 validate : 'validate' 916 }, 917 918 selector : { 919 checkbox : 'input[type="checkbox"], input[type="radio"]', 920 clear : '.clear', 921 field : 'input, textarea, select', 922 group : '.field', 923 input : 'input', 924 message : '.error.message', 925 prompt : '.prompt.label', 926 radio : 'input[type="radio"]', 927 reset : '.reset', 928 submit : '.submit', 929 uiCheckbox : '.ui.checkbox', 930 uiDropdown : '.ui.dropdown' 931 }, 932 933 className : { 934 error : 'error', 935 label : 'ui prompt label', 936 pressed : 'down', 937 success : 'success' 938 }, 939 940 error: { 941 method : 'The method you called is not defined.' 942 }, 943 944 templates: { 945 946 // template that produces error message 947 error: function(errors) { 948 var 949 html = '<ul class="list">' 950 ; 951 $.each(errors, function(index, value) { 952 html += '<li>' + value + '</li>'; 953 }); 954 html += '</ul>'; 955 return $(html); 956 }, 957 958 // template that produces label 959 prompt: function(errors) { 960 return $('<div/>') 961 .addClass('ui red pointing prompt label') 962 .html(errors[0]) 963 ; 964 } 965 }, 966 967 rules: { 968 969 // checkbox checked 970 checked: function() { 971 return ($(this).filter(':checked').length > 0); 972 }, 973 974 // value contains text (insensitive) 975 contains: function(value, text) { 976 // escape regex characters 977 text = text.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); 978 return (value.search( new RegExp(text, 'i') ) !== -1); 979 }, 980 981 // value contains text (case sensitive) 982 containsExactly: function(value, text) { 983 // escape regex characters 984 text = text.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); 985 return (value.search( new RegExp(text) ) !== -1); 986 }, 987 988 // is most likely an email 989 email: function(value){ 990 var 991 emailRegExp = new RegExp("[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?", "i") 992 ; 993 return emailRegExp.test(value); 994 }, 995 996 // is not empty or blank string 997 empty: function(value) { 998 return !(value === undefined || '' === value); 999 }, 1000 1001 // is valid integer 1002 integer: function(value, range) { 1003 var 1004 intRegExp = /^\-?\d+$/, 1005 min, 1006 max, 1007 parts 1008 ; 1009 if(range === undefined || range === '' || range === '..') { 1010 // do nothing 1011 } 1012 else if(range.indexOf('..') == -1) { 1013 if(intRegExp.test(range)) { 1014 min = max = range - 0; 1015 } 1016 } 1017 else { 1018 parts = range.split('..', 2); 1019 if(intRegExp.test(parts[0])) { 1020 min = parts[0] - 0; 1021 } 1022 if(intRegExp.test(parts[1])) { 1023 max = parts[1] - 0; 1024 } 1025 } 1026 return ( 1027 intRegExp.test(value) && 1028 (min === undefined || value >= min) && 1029 (max === undefined || value <= max) 1030 ); 1031 }, 1032 1033 // is value (case insensitive) 1034 is: function(value, text) { 1035 text = (typeof text == 'string') 1036 ? text.toLowerCase() 1037 : text 1038 ; 1039 value = (typeof value == 'string') 1040 ? value.toLowerCase() 1041 : value 1042 ; 1043 return (value == text); 1044 }, 1045 1046 // is value 1047 isExactly: function(value, text) { 1048 return (value == text); 1049 }, 1050 1051 // is at least string length 1052 length: function(value, requiredLength) { 1053 return (value !== undefined) 1054 ? (value.length >= requiredLength) 1055 : false 1056 ; 1057 }, 1058 1059 // matches another field 1060 match: function(value, fieldIdentifier) { 1061 // use either id or name of field 1062 var 1063 $form = $(this), 1064 matchingValue 1065 ; 1066 if($form.find('#' + fieldIdentifier).length > 0) { 1067 matchingValue = $form.find('#' + fieldIdentifier).val(); 1068 } 1069 else if($form.find('[name="' + fieldIdentifier +'"]').length > 0) { 1070 matchingValue = $form.find('[name="' + fieldIdentifier + '"]').val(); 1071 } 1072 else if( $form.find('[data-validate="'+ fieldIdentifier +'"]').length > 0 ) { 1073 matchingValue = $form.find('[data-validate="'+ fieldIdentifier +'"]').val(); 1074 } 1075 return (matchingValue !== undefined) 1076 ? ( value.toString() == matchingValue.toString() ) 1077 : false 1078 ; 1079 }, 1080 1081 // string length is less than max length 1082 maxLength: function(value, maxLength) { 1083 return (value !== undefined) 1084 ? (value.length <= maxLength) 1085 : false 1086 ; 1087 }, 1088 1089 // value is not value (case insensitive) 1090 not: function(value, notValue) { 1091 value = (typeof value == 'string') 1092 ? value.toLowerCase() 1093 : value 1094 ; 1095 notValue = (typeof notValue == 'string') 1096 ? notValue.toLowerCase() 1097 : notValue 1098 ; 1099 return (value != notValue); 1100 }, 1101 1102 // value is not value (case sensitive) 1103 notExactly: function(value, notValue) { 1104 return (value != notValue); 1105 }, 1106 1107 // value is most likely url 1108 url: function(value) { 1109 var 1110 urlRegExp = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/ 1111 ; 1112 return urlRegExp.test(value); 1113 } 1114 } 1115 1116 }; 1117 1118 })( jQuery, window , document );