1/*jshint strict:true, browser:true, curly:true, eqeqeq:true, expr:true, forin:true, latedef:true, newcap:true, noarg:true, trailing: true, undef:true, unused:true */ 2/*global Drupal: true, jQuery: true, XRegExp:true*/ 3/** 4 * File: clientside_validation.js 5 * Version: 7.x-1.x 6 * Description: Add clientside validation rules 7 * Author: Attiks 8 * Language: Javascript 9 * Project: clientside_validation 10 * @module clientside_validation 11 */ 12 13 14(/** @lends Drupal */function ($) { 15 /** 16 * Drupal.behaviors.clientsideValidation. 17 * 18 * Attach clientside validation to the page or rebind the rules in case of AJAX calls. 19 * @extends Drupal.behaviors 20 * @fires clientsideValidationInitialized 21 */ 22 "use strict"; 23 Drupal.behaviors.clientsideValidation = { 24 attach: function (context) { 25 if (!Drupal.myClientsideValidation) { 26 if (Drupal.settings.clientsideValidation) { 27 Drupal.myClientsideValidation = new Drupal.clientsideValidation(); 28 } 29 } 30 else { 31 if (typeof(Drupal.settings.clientsideValidation.forms) === 'undefined') { 32 return; 33 } 34 var update = false; 35 $.each(Drupal.settings.clientsideValidation.forms, function (f) { 36 if ($(context).find('#' + f).length || $(context).is('#' + f)) { 37 update = true; 38 } 39 }); 40 //update settings 41 if (update) { 42 Drupal.myClientsideValidation.data = Drupal.settings.clientsideValidation; 43 Drupal.myClientsideValidation.forms = Drupal.myClientsideValidation.data.forms; 44 Drupal.myClientsideValidation.groups = Drupal.myClientsideValidation.data.groups; 45 Drupal.myClientsideValidation.bindForms(); 46 } 47 } 48 49 /** 50 * Let other modules know we are ready. 51 * @event clientsideValidationInitialized 52 * @name clientsideValidationInitialized 53 * @memberof Drupal.clientsideValidation 54 */ 55 $.event.trigger('clientsideValidationInitialized'); 56 } 57 }; 58 59 /** 60 * Drupal.clientsideValidation. 61 * This module adds clientside validation for all forms and webforms using jquery.validate 62 * Don't forget to read the README 63 * 64 * @class Drupal.clientsideValidation 65 * @see https://github.com/jzaefferer/jquery-validation 66 * @fires clientsideValidationAddCustomRules 67 */ 68 Drupal.clientsideValidation = function() { 69 var self = this; 70 if (typeof window.time !== 'undefined') { 71 // activate by setting clientside_validation_add_js_timing 72 self.time = window.time; 73 } 74 else { 75 self.time = { 76 start: function() {}, 77 stop: function() {}, 78 report: function() {} 79 }; 80 } 81 self.time.start('1. clientsideValidation'); 82 83 /** 84 * prefix to use 85 * @memberof Drupal.clientsideValidation 86 * @type string 87 * @readonly 88 * @private 89 */ 90 this.prefix = 'clientsidevalidation-'; 91 92 /** 93 * local copy of settings 94 * @memberof Drupal.clientsideValidation 95 * @type array 96 * @readonly 97 * @private 98 */ 99 this.data = $.extend(true, {}, Drupal.settings.clientsideValidation); 100 101 /** 102 * local reference of all defined forms 103 * @memberof Drupal.clientsideValidation 104 * @type array 105 * @readonly 106 */ 107 this.forms = this.data.forms; 108 109 /** 110 * list of all defined validators 111 * @memberof Drupal.clientsideValidation 112 * @type array 113 * @readonly 114 */ 115 this.validators = {}; 116 117 /** 118 * groups used for radios/checkboxes 119 * @memberof Drupal.clientsideValidation 120 * @type array 121 * @readonly 122 * @private 123 */ 124 this.groups = this.data.groups; 125 126 // disable class and attribute rules defined by jquery.validate 127 $.validator.classRules = function() { 128 return {}; 129 }; 130 $.validator.attributeRules = function() { 131 return {}; 132 }; 133 134 /** 135 * add extra rules not defined in jquery.validate 136 * @see jquery.validate 137 */ 138 this.addExtraRules(); 139 140 /** 141 * bind all rules to all forms 142 * @see Drupal.clientsideValidation.prototype.bindForms 143 */ 144 this.bindForms(); 145 self.time.stop('1. clientsideValidation'); 146 self.time.report(); 147 }; 148 149 /** 150 * findVerticalTab helper. 151 * @memberof Drupal.clientsideValidation 152 * @private 153 */ 154 Drupal.clientsideValidation.prototype.findVerticalTab = function(element) { 155 element = $(element); 156 157 // Check for the vertical tabs fieldset and the verticalTab data 158 var fieldset = element.parents('fieldset.vertical-tabs-pane'); 159 if ((fieldset.size() > 0) && (typeof(fieldset.data('verticalTab')) !== 'undefined')) { 160 var tab = $(fieldset.data('verticalTab').item[0]).find('a'); 161 if (tab.size()) { 162 return tab; 163 } 164 } 165 166 // Return null by default 167 return null; 168 }; 169 170 /** 171 * findHorizontalTab helper. 172 * @memberof Drupal.clientsideValidation 173 * @private 174 */ 175 Drupal.clientsideValidation.prototype.findHorizontalTab = function(element) { 176 element = $(element); 177 178 // Check for the vertical tabs fieldset and the verticalTab data 179 var fieldset = element.parents('fieldset.horizontal-tabs-pane'); 180 if ((fieldset.size() > 0) && (typeof(fieldset.data('horizontalTab')) !== 'undefined')) { 181 var tab = $(fieldset.data('horizontalTab').item[0]).find('a'); 182 if (tab.size()) { 183 return tab; 184 } 185 } 186 187 // Return null by default 188 return null; 189 }; 190 191 /** 192 * Bind all forms. 193 * @memberof Drupal.clientsideValidation 194 * @public 195 */ 196 Drupal.clientsideValidation.prototype.bindForms = function(){ 197 var self = this; 198 var groupkey; 199 if (typeof(self.forms) === 'undefined') { 200 return; 201 } 202 self.time.start('2. bindForms'); 203 // unset invalid forms 204 $.each (self.forms, function (f) { 205 if ($('#' + f).length < 1) { 206 delete self.forms[f]; 207 } 208 }); 209 $.each (self.forms, function(f) { 210 var errorel = self.prefix + f + '-errors'; 211 // Remove any existing validation stuff 212 if (self.validators[f]) { 213 // Doesn't work :: $('#' + f).rules('remove'); 214 var form = $('#' + f).get(0); 215 if (typeof(form) !== 'undefined') { 216 $.removeData(form, 'validator'); 217 } 218 } 219 220 if('checkboxrules' in self.forms[f]){ 221 self.time.start('checkboxrules_groups'); 222 groupkey = ""; 223 $.each (self.forms[f].checkboxrules, function(r) { 224 groupkey = r + '_group'; 225 self.groups[f][groupkey] = []; 226 $.each(this, function(){ 227 $(this[2]).find('input[type=checkbox]').each(function(){ 228 self.groups[f][groupkey].push($(this).attr('name')); 229 }); 230 }); 231 }); 232 self.time.stop('checkboxrules_groups'); 233 } 234 235 if('daterangerules' in self.forms[f]){ 236 self.time.start('daterangerules'); 237 groupkey = ""; 238 $.each (self.forms[f].daterangerules, function(r) { 239 groupkey = r + '_group'; 240 self.groups[f][groupkey] = []; 241 $.each(this, function(){ 242 $('#' + f + ' #' + r + ' :input').not('input[type=image]').each(function(){ 243 self.groups[f][groupkey].push($(this).attr('name')); 244 }); 245 }); 246 }); 247 self.time.stop('daterangerules'); 248 } 249 250 if('dateminrules' in self.forms[f]){ 251 self.time.start('dateminrules'); 252 groupkey = ""; 253 $.each (self.forms[f].dateminrules, function(r) { 254 groupkey = r + '_group'; 255 self.groups[f][groupkey] = []; 256 $.each(this, function(){ 257 $('#' + f + ' #' + r + ' :input').not('input[type=image]').each(function(){ 258 self.groups[f][groupkey].push($(this).attr('name')); 259 }); 260 }); 261 }); 262 self.time.stop('dateminrules'); 263 } 264 265 if('datemaxrules' in self.forms[f]){ 266 self.time.start('datemaxrules'); 267 groupkey = ""; 268 $.each (self.forms[f].datemaxrules, function(r) { 269 groupkey = r + '_group'; 270 self.groups[f][groupkey] = []; 271 $.each(this, function(){ 272 $('#' + f + ' #' + r + ' :input').not('input[type=image]').each(function(){ 273 self.groups[f][groupkey].push($(this).attr('name')); 274 }); 275 }); 276 }); 277 self.time.stop('datemaxrules'); 278 } 279 280 281 // Add basic settings 282 // todo: find cleaner fix 283 // ugly fix for nodes in colorbox 284 if(typeof $('#' + f).validate === 'function') {
285 var validate_options = { 286 errorClass: 'error', 287 groups: self.groups[f], 288 errorElement: self.forms[f].general.errorElement, 289 unhighlight: function(element, errorClass, validClass) { 290 var tab; 291 292 // If this is a pending element skip handling. 293 if (typeof this.pending[element.name] != 'undefined') { 294 return; 295 } 296 297 // Default behavior 298 $(element).removeClass(errorClass).addClass(validClass); 299 300 // Sort the classes out for the tabs - we only want to remove the 301 // highlight if there are no inputs with errors... 302 var fieldset = $(element).parents('fieldset.vertical-tabs-pane'); 303 if (fieldset.size() && fieldset.find('.' + errorClass).not('label').size() === 0) { 304 tab = self.findVerticalTab(element); 305 if (tab) { 306 tab.removeClass(errorClass).addClass(validClass); 307 } 308 } 309 310 // Same for horizontal tabs 311 fieldset = $(element).parents('fieldset.horizontal-tabs-pane'); 312 if (fieldset.size() && fieldset.find('.' + errorClass).not('label').size() === 0) { 313 tab = self.findHorizontalTab(element); 314 if (tab) { 315 tab.removeClass(errorClass).addClass(validClass); 316 } 317 } 318 319 /** 320 * Let other modules know this / which / an element is valid. 321 * @event clientsideValidationValid 322 * @name clientsideValidationValid 323 * @memberof Drupal.clientsideValidation 324 */ 325 $(element).trigger('clientsideValidationValid'); 326 $.event.trigger('clientsideValidationValid', [element]); 327 }, 328 highlight: function(element, errorClass, validClass) { 329 // Default behavior 330 $(element).addClass(errorClass).removeClass(validClass); 331 332 // Sort the classes out for the tabs 333 var tab = self.findVerticalTab(element); 334 if (tab) { 335 tab.addClass(errorClass).removeClass(validClass); 336 } 337 tab = self.findHorizontalTab(element); 338 if (tab) { 339 tab.addClass(errorClass).removeClass(validClass); 340 } 341 342 /** 343 * Let other modules know this / which / an element is invalid. 344 * @event clientsideValidationInvalid 345 * @name clientsideValidationInvalid 346 * @memberof Drupal.clientsideValidation 347 */ 348 $(element).trigger('clientsideValidationInvalid'); 349 $.event.trigger('clientsideValidationInvalid', [element]); 350 }, 351 invalidHandler: function(form, validator) { 352 var tab; 353 if (validator.errorList.length > 0) { 354 // Check if any of the errors are in the selected tab 355 var errors_in_selected = false; 356 for (var i = 0; i < validator.errorList.length; i++) { 357 tab = self.findVerticalTab(validator.errorList[i].element); 358 if (tab && tab.parent().hasClass('selected')) { 359 errors_in_selected = true; 360 break; 361 } 362 } 363 364 // Only focus the first tab with errors if the selected tab doesn't have 365 // errors itself. We shouldn't hide a tab that contains errors! 366 if (!errors_in_selected) { 367 tab = self.findVerticalTab(validator.errorList[0].element); 368 if (tab) { 369 tab.click(); 370 } 371 } 372 373 // Same for vertical tabs 374 // Check if any of the errors are in the selected tab 375 errors_in_selected = false; 376 for (i = 0; i < validator.errorList.length; i++) { 377 tab = self.findHorizontalTab(validator.errorList[i].element); 378 if (tab && tab.parent().hasClass('selected')) { 379 errors_in_selected = true; 380 break; 381 } 382 } 383 384 // Only focus the first tab with errors if the selected tab doesn't have 385 // errors itself. We shouldn't hide a tab that contains errors! 386 if (!errors_in_selected) { 387 tab = self.findHorizontalTab(validator.errorList[0].element); 388 if (tab) { 389 tab.click(); 390 } 391 } 392 if (self.forms[f].general.scrollTo) { 393 if (!$('html, body').hasClass('cv-scrolling')) { 394 var x; 395 if ($("#" + errorel).length) { 396 $("#" + errorel).show(); 397 x = $("#" + errorel).offset().top - $("#" + errorel).height() - 100; // provides buffer in viewport 398 } 399 else { 400 var first_element = $(validator.errorList[0].element); 401 var hidden_element = first_element.is(":hidden"); 402 if (hidden_element) { 403 var original_visibility = first_element.css('visibility'); 404 var original_position = first_element.css('position'); 405 first_element.css('visibility', 'hidden'); 406 first_element.css('position', 'absolute'); 407 first_element.show(); 408 } 409 x = first_element.offset().top - first_element.height() - 100; 410 if (hidden_element) { 411 first_element.hide(); 412 first_element.css('position', original_position); 413 first_element.css('visibility', original_visibility); 414 } 415 } 416 $('html, body').addClass('cv-scrolling').animate( 417 {scrollTop: x}, 418 { 419 duration: self.forms[f].general.scrollSpeed, 420 complete: function () { 421 $('html, body').removeClass('cv-scrolling') 422 } 423 } 424 ); 425 $('.wysiwyg-toggle-wrapper a').each(function() { 426 $(this).click(); 427 $(this).click(); 428 }); 429 } 430 } 431 432 /** 433 * Notify that the form contains errors. 434 * @event clientsideValidationFormHasErrors 435 * @name clientsideValidationFormHasErrors 436 * @memberof Drupal.clientsideValidation 437 */ 438 $.event.trigger('clientsideValidationFormHasErrors', [form.target]); 439 } 440 } 441 }; 442 443 switch (parseInt(self.forms[f].errorPlacement, 10)) { 444 case 0: // CLIENTSIDE_VALIDATION_JQUERY_SELECTOR 445 if ($(self.forms[f].errorJquerySelector).length) { 446 if (!$(self.forms[f].errorJquerySelector + ' #' + errorel).length) { 447 $('<div id="' + errorel + '" class="messages error clientside-error"><ul></ul></div>').prependTo(self.forms[f].errorJquerySelector).hide(); 448 } 449 } 450 else if (!$('#' + errorel).length) { 451 $('<div id="' + errorel + '" class="messages error clientside-error"><ul></ul></div>').insertBefore('#' + f).hide(); 452 } 453 validate_options.errorContainer = '#' + errorel; 454 validate_options.errorLabelContainer = '#' + errorel + ' ul'; 455 validate_options.wrapper = 'li'; 456 break; 457 case 1: // CLIENTSIDE_VALIDATION_TOP_OF_FORM 458 if (!$('#' + errorel).length) { 459 $('<div id="' + errorel + '" class="messages error clientside-error"><ul></ul></div>').insertBefore('#' + f).hide(); 460 } 461 validate_options.errorContainer = '#' + errorel; 462 validate_options.errorLabelContainer = '#' + errorel + ' ul'; 463 validate_options.wrapper = 'li'; 464 break; 465 case 2: // CLIENTSIDE_VALIDATION_BEFORE_LABEL 466 validate_options.errorPlacement = function(error, element) { 467 var parents; 468 if (element.is(":radio")) { 469 parents = element.parents(".form-type-checkbox-tree"); 470 if(parents.length) { 471 error.insertBefore(parents.find("label").first()); 472 } 473 else { 474 parents = element.parents('.form-radios').prev('label'); 475 if (!parents.length) { 476 parents = 'label[for="'+ element.attr('id') +'"]'; 477 } 478 error.insertBefore(parents); 479 } 480 } 481 else if (element.is(":checkbox")) { 482 parents = element.parents(".form-type-checkbox-tree"); 483 if(parents.length) { 484 error.insertBefore(parents.find("label").first()); 485 } 486 else { 487 parents = element.parents('.form-radios').prev('label'); 488 if (!parents.length) { 489 parents = 'label[for="'+ element.attr('id') +'"]'; 490 } 491 error.insertBefore(parents); 492 } 493 } 494 else { 495 error.insertBefore('label[for="'+ element.attr('id') +'"]'); 496 } 497 }; 498 break; 499 case 3: // CLIENTSIDE_VALIDATION_AFTER_LABEL 500 validate_options.errorPlacement = function(error, element) { 501 var parents; 502 if (element.is(":radio")) { 503 parents = element.parents(".form-type-checkbox-tree"); 504 if(parents.length) { 505 error.insertAfter(parents.find("label").first()); 506 } 507 else { 508 parents = element.parents('.form-radios').prev('label'); 509 if (!parents.length) { 510 parents = 'label[for="'+ element.attr('id') +'"]'; 511 } 512 error.insertAfter(parents); 513 } 514 } 515 else if (element.is(":checkbox")) { 516 parents = element.parents(".form-type-checkbox-tree"); 517 if(parents.length) { 518 error.insertAfter(parents.find("label").first()); 519 } 520 else { 521 parents = element.parents('.form-checkboxes').prev('label'); 522 if (!parents.length) { 523 parents = 'label[for="'+ element.attr('id') +'"]'; 524 } 525 error.insertAfter(parents); 526 } 527 } 528 else { 529 error.insertAfter('label[for="'+ element.attr('id') +'"]'); 530 } 531 }; 532 break; 533 case 4: // CLIENTSIDE_VALIDATION_BEFORE_INPUT 534 validate_options.errorPlacement = function(error, element) { 535 error.insertBefore(element); 536 }; 537 break; 538 case 5: // CLIENTSIDE_VALIDATION_AFTER_INPUT 539 validate_options.errorPlacement = function(error, element) { 540 var parents; 541 if (element.is(":radio")) { 542 parents = element.parents(".form-type-checkbox-tree"); 543 if(parents.length) { 544 error.insertAfter(parents); 545 } 546 else { 547 parents = element.parents('.form-radios'); 548 if (!parents.length) { 549 parents = element; 550 } 551 error.insertAfter(parents); 552 } 553 } 554 else if (element.is(":checkbox")) { 555 parents = element.parents(".form-type-checkbox-tree"); 556 if(parents.length) { 557 error.insertAfter(parents); 558 } 559 else { 560 parents = element.parents('.form-checkboxes'); 561 if (!parents.length) { 562 parents = element; 563 } 564 error.insertAfter(parents); 565 } 566 } 567 else if (element.next('div.grippie').length) { 568 error.insertAfter(element.next('div.grippie')); 569 } else { 570 error.insertAfter(element); 571 } 572 }; 573 break; 574 case 6: // CLIENTSIDE_VALIDATION_TOP_OF_FIRST_FORM 575 if ($('div.messages--error').length) { 576 if ($('div.messages--error').attr('id').length) { 577 errorel = $('div.messages--error').attr('id'); 578 } 579 else { 580 $('div.messages--error').attr('id', errorel); 581 } 582 } 583 else if ($('div.messages.error').length) { 584 if ($('div.messages.error').attr('id').length) { 585 errorel = $('div.messages.error').attr('id'); 586 } 587 else { 588 $('div.messages.error').attr('id', errorel); 589 } 590 } 591 else if (!$('#' + errorel).length) { 592 $('<div id="' + errorel + '" class="messages error clientside-error"><ul></ul></div>').insertBefore('#' + f).hide(); 593 } 594 validate_options.errorContainer = '#' + errorel; 595 validate_options.errorLabelContainer = '#' + errorel + ' ul'; 596 validate_options.wrapper = 'li'; 597 break; 598 case 7: // CLIENTSIDE_VALIDATION_CUSTOM_ERROR_FUNCTION 599 validate_options.errorPlacement = function (error, element) { 600 var func = self.forms[f].customErrorFunction; 601 Drupal.myClientsideValidation[func](error, element); 602 }; 603 break; 604 } 605 606 if (!self.forms[f].includeHidden) { 607 validate_options.ignore = ':input:hidden'; 608 } 609 else { 610 validate_options.ignore = ''; 611 } 612 if(self.forms[f].general.validateTabs) { 613 if($('.vertical-tabs-pane input').length) { 614 validate_options.ignore += ' :not(.vertical-tabs-pane :input, .horizontal-tabs-pane :input)'; 615 } 616 } 617 else { 618 validate_options.ignore += (validate_options.ignore === '') ? '.horizontal-tab-hidden :input' : ', .horizontal-tab-hidden :input'; 619 } 620 //Since we can only give boolean false to onsubmit, onfocusout and onkeyup, we need 621 //a lot of if's (boolean true can not be passed to these properties). 622 if (!Boolean(parseInt(self.forms[f].general.validateOnSubmit, 10))) { 623 validate_options.onsubmit = false;
624 } 625 if (!Boolean(parseInt(self.forms[f].general.validateOnBlur, 10))) { 626 validate_options.onfocusout = false; 627 } 628 if (Boolean(parseInt(self.forms[f].general.validateOnBlurAlways, 10))) { 629 validate_options.onfocusout = function(element) { 630 if ( !this.checkable(element) ) { 631 this.element(element); 632 } 633 }; 634 } 635 if (!Boolean(parseInt(self.forms[f].general.validateOnKeyUp, 10))) { 636 validate_options.onkeyup = false; 637 } 638 // Only apply this setting if errorplacement is set to the top of the form 639 if (parseInt(self.forms[f].general.showMessages, 10) > 0 && parseInt(self.forms[f].errorPlacement, 10) === 1) { 640 var showMessages = parseInt(self.forms[f].general.showMessages, 10); 641 // Show only last message 642 if (showMessages === 2) { 643 validate_options.showErrors = function() { 644 var allErrors = this.errors(); 645 var i; 646 this.toHide = allErrors; 647 $(':input.' + this.settings.errorClass).removeClass(this.settings.errorClass); 648 for ( i = this.errorList.length -1; this.errorList[i]; i++ ) { 649 var error = this.errorList[i]; 650 this.settings.highlight && this.settings.highlight.call( this, error.element, this.settings.errorClass, this.settings.validClass ); 651 this.showLabel( error.element, error.message ); 652 } 653 if( this.errorList.length ) { 654 this.toShow = this.toShow.add( this.containers ); 655 } 656 if (this.settings.success) { 657 for ( i = 0; this.successList[i]; i++ ) { 658 this.showLabel( this.successList[i] ); 659 } 660 } 661 if (this.settings.unhighlight) { 662 var elements; 663 for ( i = 0, elements = this.validElements(); elements[i]; i++ ) { 664 this.settings.unhighlight.call( this, elements[i], this.settings.errorClass, this.settings.validClass ); 665 } 666 } 667 this.toHide = this.toHide.not( this.toShow ); 668 this.hideErrors(); 669 this.addWrapper( this.toShow ).show(); 670 }; 671 } 672 // Show only first message 673 else if(showMessages === 1) { 674 validate_options.showErrors = function() { 675 var allErrors = this.errors(); 676 var i; 677 var elements; 678 if (this.settings.unhighlight) { 679 var firstErrorElement = this.clean($(allErrors[0]).attr('for')); 680 //for attr points to name or id 681 if (typeof firstErrorElement === 'undefined') { 682 firstErrorElement = this.clean('#' + $(allErrors[0]).attr('for')); 683 } 684 for (i = 0, elements = this.elements().not($(firstErrorElement)); elements[i]; i++) { 685 this.settings.unhighlight.call( this, elements[i], this.settings.errorClass, this.settings.validClass ); 686 } 687 } 688 689 for ( i = 0; this.errorList[i] && i<1; i++ ) { 690 var error = this.errorList[i]; 691 this.settings.highlight && this.settings.highlight.call( this, error.element, this.settings.errorClass, this.settings.validClass ); 692 this.showLabel( error.element, error.message ); 693 } 694 if( this.errorList.length ) { 695 this.toShow = this.toShow.add( this.containers ); 696 } 697 if (this.settings.success) { 698 for ( i = 0; this.successList[i]; i++ ) { 699 this.showLabel( this.successList[i] ); 700 } 701 } 702 if (this.settings.unhighlight) { 703 for ( i = 0, elements = this.validElements(); elements[i]; i++ ) { 704 this.settings.unhighlight.call( this, elements[i], this.settings.errorClass, this.settings.validClass ); 705 } 706 } 707 708 this.toHide = this.toHide.not( this.toShow ); 709 this.hideErrors(); 710 this.addWrapper( this.toShow ).show(); 711 allErrors = this.errors(); 712 allErrors.splice(0,1); 713 this.toHide = allErrors; 714 this.hideErrors(); 715 }; 716 } 717 } 718 /** 719 * Let other modules alter the validation options for this form. 720 * @event clientsideValidationAlterOptions 721 * @name clientsideValidationAlterOptions 722 * @memberof Drupal.clientsideValidation 723 */ 724 $.event.trigger('clientsideValidationAlterOptions', [validate_options, f]); 725 self.validators[f] = $('#' + f).validate(validate_options); 726 727 // Disable HTML5 validation 728 if (!Boolean(parseInt(self.forms[f].general.disableHtml5Validation, 10))) { 729 $('#' + f).removeAttr('novalidate'); 730 } 731 else { 732 $('#' + f).attr('novalidate', 'novalidate'); 733 } 734 // Bind all rules 735 self.bindRules(f); 736 737 } 738 }); 739 self.time.stop('2. bindForms'); 740 }; 741 742 /** 743 * Bind all rules. 744 * @memberof Drupal.clientsideValidation 745 */ 746 Drupal.clientsideValidation.prototype.bindRules = function(formid){ 747 var self = this; 748 self.time.start('3. bindRules'); 749 var $form = $('#' + formid); 750 var hideErrordiv = function(){ 751 //wait just one milisecond until the error div is updated 752 window.setTimeout(function(){ 753 var visibles = 0; 754 // @TODO: check settings 755 $(".clientside-error ul li").each(function(){ 756 if($(this).is(':visible')){ 757 visibles++; 758 } 759 else { 760 $(this).remove(); 761 } 762 }); 763 if(visibles < 1){ 764 $(".clientside-error").hide(); 765 } 766 }, 1); 767 }; 768 if('checkboxrules' in self.forms[formid]){ 769 self.time.start('checkboxrules'); 770 $.each (self.forms[formid].checkboxrules, function(r) { 771 var $checkboxes = $form.find(this.checkboxgroupminmax[2]).find('input[type="checkbox"]'); 772 if ($checkboxes.length) { 773 var identifier = 'require-one-' + this.checkboxgroupminmax[2].substring(1); 774 var min = this.checkboxgroupminmax[0]; 775 var message = this.messages.checkboxgroupminmax; 776 $checkboxes.addClass(identifier); 777 $checkboxes.each(function(){ 778 var $checkbox = $(this); 779 var newrule = { 780 require_from_group: [min, '.' + identifier] 781 }; 782 $checkbox.rules("add", newrule); 783 $checkbox.change(hideErrordiv); 784 785 if (typeof self.validators[formid].settings.messages[$checkbox.attr('name')] === 'undefined') { 786 self.validators[formid].settings.messages[$checkbox.attr('name')] = {}; 787 } 788 $.extend(self.validators[formid].settings.messages[$checkbox.attr('name')], { 789 require_from_group: message 790 }); 791 }); 792 793 if (typeof self.validators[formid].settings.messages['.' + identifier] === 'undefined') { 794 self.validators[formid].settings.messages['.' + identifier] = {}; 795 } 796 $.extend(self.validators[formid].settings.messages['.' + identifier], { 797 require_from_group: message 798 }); 799 } 800 }); 801 self.time.stop('checkboxrules'); 802 } 803 if('daterangerules' in self.forms[formid]){ 804 self.time.start('daterangerules'); 805 $.each (self.forms[formid].daterangerules, function(r) { 806 $form.find('#' + r).find('input, select').not('input[type=image]').each(function(){ 807 // Make sure we are working with the copy of rules object. 808 var rule = $.extend(true, {}, self.forms[formid].daterangerules[r]); 809 if (typeof self.validators[formid].settings.messages[r] === 'undefined') { 810 self.validators[formid].settings.messages[r] = {}; 811 } 812 $.extend(self.validators[formid].settings.messages[r], rule.messages); 813 delete rule.messages; 814 $(this).rules("add", rule); 815 $(this).blur(hideErrordiv); 816 }); 817 }); 818 self.time.stop('daterangerules'); 819 } 820 821 if('dateminrules' in self.forms[formid]){ 822 self.time.start('dateminrules'); 823 $.each (self.forms[formid].dateminrules, function(r) { 824 $form.find('#' + r).find('input, select').not('input[type=image]').each(function(){ 825 // Make sure we are working with the copy of rules object. 826 var rule = $.extend(true, {}, self.forms[formid].dateminrules[r]); 827 if (typeof self.validators[formid].settings.messages[r] === 'undefined') { 828 self.validators[formid].settings.messages[r] = {}; 829 } 830 $.extend(self.validators[formid].settings.messages[r], rule.messages); 831 delete rule.messages; 832 $(this).rules("add", rule); 833 $(this).blur(hideErrordiv); 834 }); 835 }); 836 self.time.stop('dateminrules'); 837 } 838 839 if('datemaxrules' in self.forms[formid]){ 840 self.time.start('datemaxrules'); 841 $.each (self.forms[formid].datemaxrules, function(r) { 842 $form.find('#' + r).find('input, select').not('input[type=image]').each(function(){ 843 // Make sure we are working with the copy of rules object. 844 var rule = $.extend(true, {}, self.forms[formid].datemaxrules[r]); 845 if (typeof self.validators[formid].settings.messages[r] === 'undefined') { 846 self.validators[formid].settings.messages[r] = {}; 847 } 848 $.extend(self.validators[formid].settings.messages[r], rule.messages); 849 delete rule.messages; 850 $(this).rules("add", rule); 851 $(this).blur(hideErrordiv); 852 }); 853 }); 854 self.time.stop('datemaxrules'); 855 } 856 857 if ('rules' in self.forms[formid]) { 858 self.time.start('rules'); 859 // Make sure we are working with the copy of rules object. 860 var rules = $.extend(true, {}, self.forms[formid].rules); 861 // :input can be slow, see http://jsperf.com/input-vs-input/2 862 $form.find('input, textarea, select').each(function(idx, elem) { 863 var rule = rules[elem.name]; 864 if (rule) { 865 var $elem = $(elem); 866 if (typeof self.validators[formid].settings.messages[elem.name] === 'undefined') { 867 self.validators[formid].settings.messages[elem.name] = {}; 868 } 869 $.extend(self.validators[formid].settings.messages[elem.name], rule.messages); 870 delete rule.messages; 871 $elem.rules("add",rule); 872 $elem.change(hideErrordiv); 873 } 874 }); 875 self.time.stop('rules'); 876 } 877 self.time.stop('3. bindRules'); 878 }; 879 880 /** 881 * Add extra rules. 882 * @memberof Drupal.clientsideValidation 883 */ 884 Drupal.clientsideValidation.prototype.addExtraRules = function(){ 885 var self = this; 886 887 $.validator.addMethod("numberDE", function(value, element) { 888 return this.optional(element) || /^-?(?:\d+|\d{1,3}(?:\.\d{3})+)(?:,\d+)?$/.test(value); 889 }); 890 891 $.validator.addMethod("min_comma", function(value, element, param) { 892 var real_val = Number(value.replace(',', '.')); 893 return this.optional(element) || real_val >= param; 894 }); 895 896 $.validator.addMethod("max_comma", function(value, element, param) { 897 var real_val = Number(value.replace(',', '.')); 898 return this.optional(element) || real_val <= param; 899 }); 900 901 $.validator.addMethod("range_comma", function(value, element, param) { 902 var real_val = Number(value.replace(',', '.')); 903 return this.optional(element) || (real_val >= param[0] && real_val <= param[1]); 904 }); 905 906 // Min a and maximum b checkboxes from a group 907 $.validator.addMethod("checkboxgroupminmax", function(value, element, param) {
908 var amountChecked = $(param[2]).find('input:checked').length; 909 return (amountChecked >= param[0] && amountChecked <= param[1]); 910 }, $.format('Minimum {0}, maximum {1}')); 911 912 // Allow integers, same as digits but including a leading '-' 913 $.validator.addMethod("digits_negative", function(value, element) { 914 return this.optional(element) || /^-?\d+$/.test(value); 915 }, $.format('Please enter only digits.')); 916 917 // One of the values 918 $.validator.addMethod("oneOf", function(value, element, param) { 919 for (var p in param.values) { 920 if (param.values[p] === value && param.caseSensitive) { 921 return !param.negate; 922 } 923 else if (param.values[p].toLowerCase() === value.toLowerCase() && !param.caseSensitive) { 924 return !param.negate; 925 } 926 } 927 return param.negate; 928 }, $.format('')); 929 930 $.validator.addMethod("specificVals", function(value, element, param){ 931 for (var i in value) { 932 if(param.indexOf(value[i]) === -1) { 933 return false; 934 } 935 } 936 return true; 937 }); 938 939 $.validator.addMethod("blacklist", function(value, element, param) { 940 if (typeof(value) !== 'object') { 941 value = value.split(' '); 942 } 943 for (var i in value) { 944 if(param.values.indexOf(value[i]) !== -1) { 945 return param.negate; 946 } 947 } 948 return !param.negate; 949 }); 950 951 // Default regular expression support 952 var ajaxPCREfn = function(value, element, param) { 953 var result = false;
954 $.ajax({ 955 'url': Drupal.settings.basePath + 'clientside_validation/ajax', 956 'type': "POST", 957 'data': { 958 'value': value, 959 'param': param 960 }, 961 'dataType': 'json', 962 'async': false, 963 'success': function(res){ 964 result = res; 965 } 966 }); 967 if (result.result === false) { 968 if (result.message.length) { 969 $.extend($.validator.messages, { 970 "regexMatchPCRE": result.message 971 }); 972 } 973 } 974 return result.result; 975 }; 976 977 // Regular expression support using XRegExp 978 var xregexPCREfn = function(value, element, param) { 979 if (window.XRegExp && XRegExp.version ) { 980 try { 981 var result = true; 982 for (var i = 0; i < param.expressions.length; i++) { 983 var reg = param.expressions[i]; 984 var delim = reg.lastIndexOf(reg.charAt(0)); 985 // Only allow supported modifiers 986 var modraw = reg.substr(delim + 1) || ''; 987 var mod = ''; 988 if (modraw !== '') { 989 for (var l = 0; l < 6; l++) { 990 if (modraw.indexOf('gimnsx'[l]) !== -1) { 991 mod += 'gimnsx'[l]; 992 } 993 } 994 } 995 reg = reg.substring(1, delim); 996 if (!(new XRegExp(reg, mod).test(value))) { 997 result = false;
998 if (param.messages[i].length) { 999 $.extend($.validator.messages, { 1000 "regexMatchPCRE": param.messages[i] 1001 }); 1002 } 1003 } 1004 } 1005 return result; 1006 } 1007 catch (e) { 1008 return ajaxPCREfn(value, element, param); 1009 } 1010 } 1011 else { 1012 return ajaxPCREfn(value, element, param); 1013 } 1014 }; 1015 1016 // Decide which one to use 1017 if (self.data.general.usexregxp) { 1018 $.validator.addMethod("regexMatchPCRE", xregexPCREfn, $.format('The value does not match the expected format.')); 1019 } 1020 else { 1021 $.validator.addMethod("regexMatchPCRE", ajaxPCREfn, $.format('The value does not match the expected format.')); 1022 } 1023 1024 // Unique values 1025 $.validator.addMethod("notEqualTo", function(value, element, param) { 1026 var ret = true; 1027 $.each(param, function (index, selector){ 1028 var $target = $(selector); 1029 $target.unbind(".validate-notEqualTo").bind("blur.validate-notEqualTo", function() { 1030 $(element).valid(); 1031 }); 1032 ret = ret && ($target.val() !== value); 1033 }); 1034 return ret; 1035 }, $.format('Please don\'t enter the same value again.')); 1036 1037 $.validator.addMethod("regexMatch", function(value, element, param) { 1038 if (this.optional(element) && value === '') { 1039 return this.optional(element); 1040 } 1041 else { 1042 var modifier = param.regex[1]; 1043 var valid_modifiers = ['i', 'g', 'm']; 1044 if ($.inArray(modifier, valid_modifiers) === -1) { 1045 modifier = ''; 1046 } 1047 var regexp = new RegExp(param.regex[0], modifier); 1048 if(regexp.test(value)){ 1049 return !param.negate; 1050 } 1051 return param.negate; 1052 } 1053 1054 }, $.format('The value does not match the expected format.')); 1055 1056 $.validator.addMethod("captcha", function (value, element, param) { 1057 var result = false;
1058 var sid = $(element).closest('.captcha').find('input[name=captcha_sid]').val(); 1059 $.ajax({ 1060 'url': Drupal.settings.basePath + 'clientside_validation/captcha', 1061 'type': "POST", 1062 'data': { 1063 'value': value, 1064 'param': [sid, param.captcha_validate, param.token, param.captcha_token] 1065 }, 1066 'dataType': 'json', 1067 'async': false, 1068 'success': function(res){ 1069 result = res; 1070 } 1071 }); 1072 if (result.result === false) { 1073 if (typeof result.message !== 'undefined' && result.message.length) { 1074 $.extend($.validator.messages, { 1075 "captcha": result.message 1076 }); 1077 } 1078 } 1079 return result.result; 1080 }, $.format('Wrong answer.')); 1081 1082 $.validator.addMethod("rangewords", function(value, element, param) { 1083 return this.optional(element) || (param[0] <= $.trim(value).split(/\s+/).length && value.split(/\s+/).length <= param[1]); 1084 }, $.format('The value must be between {0} and {1} words long')); 1085 1086 $.validator.addMethod("minwords", function(value, element, param) { 1087 return this.optional(element) || param <= $.trim(value).split(/\s+/).length; 1088 }, $.format('The value must be more than {0} words long')); 1089 1090 $.validator.addMethod("maxwords", function(value, element, param) { 1091 return this.optional(element) || $.trim(value).split(/\s+/).length <= param; 1092 }, $.format('The value must be fewer than {0} words long')); 1093 1094 $.validator.addMethod("plaintext", function(value, element, param){ 1095 var result = param.negate ? (value !== strip_tags(value, param.tags)) : (value === strip_tags(value, param.tags)); 1096 return this.optional(element) || result; 1097 }, $.format('The value must be plaintext')); 1098 1099 $.validator.addMethod("selectMinlength", function(value, element, param) { 1100 var result = $(element).find('option:selected').length >= param.min; 1101 if (param.negate) { 1102 result = !result; 1103 } 1104 return this.optional(element) || result; 1105 }, $.format('You must select at least {0} values')); 1106 1107 $.validator.addMethod("selectMaxlength", function(value, element, param) { 1108 var result = $(element).find('option:selected').length <= param.max; 1109 if (param.negate) { 1110 result = !result; 1111 } 1112 return this.optional(element) || result; 1113 }, $.format('You must select a maximum of {0} values')); 1114 1115 $.validator.addMethod("selectRangelength", function(value, element, param) { 1116 var result = ($(element).find('option:selected').length >= param.range[0] && $(element).find('option:selected').length <= param.range[1]); 1117 if (param.negate) { 1118 result = !result; 1119 } 1120 return this.optional(element) || result; 1121 }, $.format('You must select at between {0} and {1} values')); 1122 1123 $.validator.addMethod("datemin", function(value, element, param) { 1124 //Assume [month], [day], and [year] ?? 1125 var dayelem, monthelem, yearelem, name, $form, element_name; 1126 $form = $(element).closest('form'); 1127 element_name = $(element).attr('name'); 1128 if (element_name.indexOf('[day]') > 0) { 1129 dayelem = $(element); 1130 name = dayelem.attr('name').replace('[day]', ''); 1131 monthelem = $form.find("[name='" + name + "[month]']"); 1132 yearelem = $form.find("[name='" + name + "[year]']"); 1133 } 1134 else if (element_name.indexOf('[month]') > 0) { 1135 monthelem = $(element); 1136 name = monthelem.attr('name').replace('[month]', ''); 1137 dayelem = $form.find("[name='" + name + "[day]']"); 1138 yearelem = $form.find("[name='" + name + "[year]']"); 1139 } 1140 else if ($(element).attr('name').indexOf('[year]') > 0) { 1141 yearelem = $(element); 1142 name = yearelem.attr('name').replace('[year]', ''); 1143 dayelem = $form.find("[name='" + name + "[day]']"); 1144 monthelem = $form.find("[name='" + name + "[month]']"); 1145 } 1146 1147 if (parseInt(yearelem.val(), 10) < parseInt(param[0], 10)) { 1148 return false; 1149 } 1150 else if (parseInt(yearelem.val(), 10) === parseInt(param[0], 10)){ 1151 if (parseInt(monthelem.val(), 10) < parseInt(param[1], 10)){ 1152 return false;
1153 } 1154 else if (parseInt(monthelem.val(), 10) === parseInt(param[1], 10)){ 1155 if(parseInt(dayelem.val(), 10) < parseInt(param[2], 10)) { 1156 return false; 1157 } 1158 } 1159 } 1160 yearelem.once('daterange', function() { 1161 $(this).change(function(){$(this).trigger('focusout').trigger('blur');}); 1162 }).removeClass('error'); 1163 monthelem.once('daterange', function() { 1164 $(this).change(function(){$(this).trigger('focusout').trigger('blur');}); 1165 }).removeClass('error'); 1166 dayelem.once('daterange', function() { 1167 $(this).change(function(){$(this).trigger('focusout').trigger('blur');}); 1168 }).removeClass('error'); 1169 return true; 1170 }); 1171 1172 $.validator.addMethod("datemax", function(value, element, param) { 1173 //Assume [month], [day], and [year] ?? 1174 var dayelem, monthelem, yearelem, name, $form, element_name; 1175 $form = $(element).closest('form'); 1176 element_name = $(element).attr('name'); 1177 if (element_name.indexOf('[day]') > 0) { 1178 dayelem = $(element); 1179 name = dayelem.attr('name').replace('[day]', ''); 1180 monthelem = $form.find("[name='" + name + "[month]']"); 1181 yearelem = $form.find("[name='" + name + "[year]']"); 1182 } 1183 else if (element_name.indexOf('[month]') > 0) { 1184 monthelem = $(element); 1185 name = monthelem.attr('name').replace('[month]', ''); 1186 dayelem = $form.find("[name='" + name + "[day]']"); 1187 yearelem = $form.find("[name='" + name + "[year]']"); 1188 } 1189 else if (element_name.indexOf('[year]') > 0) { 1190 yearelem = $(element); 1191 name = yearelem.attr('name').replace('[year]', ''); 1192 dayelem = $form.find("[name='" + name + "[day]']"); 1193 monthelem = $form.find("[name='" + name + "[month]']"); 1194 1195 } 1196 1197 if (parseInt(yearelem.val(), 10) > parseInt(param[0], 10)) { 1198 return false; 1199 } 1200 else if (parseInt(yearelem.val(), 10) === parseInt(param[0], 10)){ 1201 if (parseInt(monthelem.val(), 10) > parseInt(param[1], 10)){ 1202 return false; 1203 } 1204 else if (parseInt(monthelem.val(), 10) === parseInt(param[1], 10)){ 1205 if(parseInt(dayelem.val(), 10) > parseInt(param[2], 10)) { 1206 return false; 1207 } 1208 } 1209 } 1210 yearelem.once('daterange', function() { 1211 $(this).change(function(){$(this).trigger('focusout').trigger('blur');}); 1212 }).removeClass('error'); 1213 monthelem.once('daterange', function() { 1214 $(this).change(function(){$(this).trigger('focusout').trigger('blur');}); 1215 }).removeClass('error'); 1216 dayelem.once('daterange', function() { 1217 $(this).change(function(){$(this).trigger('focusout').trigger('blur');}); 1218 }).removeClass('error'); 1219 return true; 1220 }); 1221 1222 $.validator.addMethod("daterange", function(value, element, param) { 1223 //Assume [month], [day], and [year] ?? 1224 var dayelem, monthelem, yearelem, name, $form, element_name; 1225 $form = $(element).closest('form'); 1226 element_name = $(element).attr('name'); 1227 if (element_name.indexOf('[day]') > 0) { 1228 dayelem = $(element); 1229 name = dayelem.attr('name').replace('[day]', ''); 1230 monthelem = $form.find("[name='" + name + "[month]']"); 1231 yearelem = $form.find("[name='" + name + "[year]']"); 1232 } 1233 else if (element_name.indexOf('[month]') > 0) { 1234 monthelem = $(element); 1235 name = monthelem.attr('name').replace('[month]', ''); 1236 dayelem = $form.find("[name='" + name + "[day]']"); 1237 yearelem = $form.find("[name='" + name + "[year]']"); 1238 } 1239 else if (element_name.indexOf('[year]') > 0) { 1240 yearelem = $(element); 1241 name = yearelem.attr('name').replace('[year]', ''); 1242 dayelem = $form.find("[name='" + name + "[day]']"); 1243 monthelem = $form.find("[name='" + name + "[month]']"); 1244 } 1245 1246 if (parseInt(yearelem.val(), 10) < parseInt(param[0][0], 10)) { 1247 return false; 1248 } 1249 else if (parseInt(yearelem.val(), 10) === parseInt(param[0][0], 10)){ 1250 if (parseInt(monthelem.val(), 10) < parseInt(param[0][1], 10)){ 1251 return false;
1252 } 1253 else if (parseInt(monthelem.val(), 10) === parseInt(param[0][1], 10)){ 1254 if(parseInt(dayelem.val(), 10) < parseInt(param[0][2], 10)) { 1255 return false; 1256 } 1257 } 1258 } 1259 1260 if (parseInt(yearelem.val(), 10) > parseInt(param[1][0], 10)) { 1261 return false; 1262 } 1263 else if (parseInt(yearelem.val(), 10) === parseInt(param[1][0], 10)){ 1264 if (parseInt(monthelem.val(), 10) > parseInt(param[1][1], 10)){ 1265 return false; 1266 } 1267 else if (parseInt(monthelem.val(), 10) === parseInt(param[1][1], 10)){ 1268 if(parseInt(dayelem.val(), 10) > parseInt(param[1][2], 10)) { 1269 return false; 1270 } 1271 } 1272 } 1273 yearelem.once('daterange', function() { 1274 $(this).change(function(){$(this).trigger('focusout').trigger('blur');}); 1275 }).removeClass('error'); 1276 monthelem.once('daterange', function() { 1277 $(this).change(function(){$(this).trigger('focusout').trigger('blur');}); 1278 }).removeClass('error'); 1279 dayelem.once('daterange', function() { 1280 $(this).change(function(){$(this).trigger('focusout').trigger('blur');}); 1281 }).removeClass('error'); 1282 return true; 1283 }); 1284 1285 $.validator.addMethod("dateFormat", function(value, element, param) { 1286 try{ 1287 var parts = value.split(param.splitter); 1288 var expectedpartscount = 0; 1289 var day = parseInt(parts[param.daypos], 10); 1290 1291 var month = parseInt(parts[param.monthpos], 10); 1292 if (isNaN(month)) { 1293 var date_parts = param.format.split(param.splitter); 1294 var full_idx = date_parts.indexOf("F"); 1295 var abbr_idx = date_parts.indexOf("M"); 1296 var mopos = Math.max(full_idx, abbr_idx); 1297 if (parseInt(mopos) > -1) { 1298 param.monthpos = mopos; 1299 date = new Date(parts[param.monthpos] + " 1, 2000"); 1300 month = date.getMonth(); 1301 } 1302 else { 1303 if (typeof Drupal.settings.clientsideValidation.general.months[parts[param.monthpos]] !== 'undefined') { 1304 month = Drupal.settings.clientsideValidation.general.months[parts[param.monthpos]]; 1305 } 1306 else { 1307 month = new Date(parts[param.monthpos] + " 1, 2000"); 1308 month = month.getMonth(); 1309 } 1310 } 1311 } 1312 else { 1313 month--; 1314 } 1315 1316 var year = parseInt(parts[param.yearpos], 10); 1317 var date = new Date(); 1318 var result = true; 1319 if (param.daypos !== false && parts[param.daypos].toString().length !== parts[param.daypos].length){ 1320 result = false;
1321 } 1322 if (param.monthpos !== false && parts[param.monthpos].toString().length !== parts[param.monthpos].length){ 1323 result = false; 1324 } 1325 if (param.yearpos !== false && parts[param.yearpos].toString().length !== parts[param.yearpos].length){ 1326 result = false; 1327 } 1328 if (param.yearpos !== false){ 1329 expectedpartscount++; 1330 date.setFullYear(year); 1331 if (year !== date.getFullYear()) { 1332 result = false; 1333 } 1334 } 1335 if (param.monthpos !== false) { 1336 expectedpartscount++; 1337 date.setMonth(month, 1); 1338 if (month !== date.getMonth()) { 1339 result = false; 1340 } 1341 } 1342 if (param.daypos !== false) { 1343 expectedpartscount++; 1344 date.setDate(day); 1345 if (day !== date.getDate()) { 1346 result = false; 1347 } 1348 } 1349 if (expectedpartscount !== parts.length) { 1350 result = false; 1351 } 1352 return this.optional(element) || result; 1353 } catch(e) { 1354 return this.optional(element) || false; 1355 } 1356 }, $.format('The date is not in a valid format')); 1357 1358 // Require one of several 1359 $.validator.addMethod("requireOneOf", function(value, element, param) { 1360 var ret = false;
1361 if (value === "") { 1362 $.each(param, function(index, name) { 1363 // @TODO: limit to current form 1364 if (!ret && $("[name='" + name + "']").val().length) { 1365 ret = true; 1366 } 1367 }); 1368 } 1369 else { 1370 $(element).removeClass("error"); 1371 ret = true; 1372 } 1373 $(element).blur(function () { 1374 $.each(param, function(index, name) { 1375 // @TODO: limit to current form 1376 $("[name='" + name + "']").valid(); 1377 }); 1378 }); 1379 return ret; 1380 }, $.format('Please fill in at least one of the fields')); 1381 1382 // Support for Drupal urls. 1383 $.validator.addMethod("drupalURL", function(value, element) { 1384 var result = false; 1385 if (this.settings['name_event'] == 'onkeyup') { 1386 return true; 1387 } 1388 if ($.validator.methods.url.call(this, value, element)) { 1389 return true; 1390 } 1391 $.ajax({ 1392 'url': Drupal.settings.basePath + 'clientside_validation/drupalURL', 1393 'type': "POST", 1394 'data': { 1395 'value': value 1396 }, 1397 'dataType': 'json', 1398 'async': false, 1399 'success': function(res){ 1400 result = res; 1401 } 1402 }); 1403 return result.result; 1404 }, $.format('Please fill in a valid url')); 1405 1406 // Support for phone 1407 $.validator.addMethod("phone", function(value, element, param) { 1408 var country_code = param; 1409 var result = false;
1410 $.ajax({ 1411 'url': Drupal.settings.basePath + 'clientside_validation/phone', 1412 'type': "POST", 1413 'data': { 1414 'value': value, 1415 'country_code': country_code 1416 }, 1417 'dataType': 'json', 1418 'async': false, 1419 'success': function(res){ 1420 result = res; 1421 } 1422 }); 1423 return this.optional(element) || result.result; 1424 1425 }, $.format('Please fill in a valid phone number')); 1426 1427 // EAN code 1428 $.validator.addMethod("validEAN", function(value, element) { 1429 if (this.optional(element) && value === '') { 1430 return this.optional(element); 1431 } 1432 else { 1433 if (value.length > 13) { 1434 return false; 1435 } 1436 else if (value.length !== 13) { 1437 value = '0000000000000'.substr(0, 13 - value.length).concat(value); 1438 } 1439 if (value === '0000000000000') { 1440 return false; 1441 } 1442 if (isNaN(parseInt(value, 10)) || parseInt(value, 10) === 0) { 1443 return false; 1444 } 1445 var runningTotal = 0; 1446 for (var c = 0; c < 12; c++) { 1447 if (c % 2 === 0) { 1448 runningTotal += 3 * parseInt(value.substr(c, 1), 10); 1449 } 1450 else { 1451 runningTotal += parseInt(value.substr(c, 1), 10); 1452 } 1453 } 1454 var rem = runningTotal % 10; 1455 if (rem !== 0) { 1456 rem = 10 - rem; 1457 } 1458 1459 return rem === parseInt(value.substr(12, 1), 10); 1460 1461 } 1462 }, $.format('Not a valid EAN number.')); 1463 1464 $.validator.addMethod("require_from_group", function(value, element, options) { 1465 var $fields = $(options[1], element.form), 1466 $fieldsFirst = $fields.eq(0), 1467 validator = $fieldsFirst.data("valid_req_grp") ? $fieldsFirst.data("valid_req_grp") : $.extend({}, this), 1468 isValid = $fields.filter(function() { 1469 return validator.elementValue(this); 1470 }).length >= options[0]; 1471 1472 // Store the cloned validator for future validation 1473 $fieldsFirst.data("valid_req_grp", validator); 1474 1475 // If element isn't being validated, run each require_from_group field's validation rules 1476 if (!$(element).data("being_validated")) { 1477 $fields.data("being_validated", true); 1478 $fields.each(function() { 1479 if (jQuery.data(this, 'clientside_validation_validated')) { 1480 return; 1481 } else { 1482 jQuery.data(this, 'clientside_validation_validated', true); 1483 validator.element(this); 1484 } 1485 }); 1486 $fields.data("being_validated", false); 1487 } 1488 return isValid; 1489 }, $.validator.format("Please fill at least {0} of these fields.")); 1490 1491 1492 // Enhances the provided "remote" method by adding our own success and 1493 // progress handling. 1494 $.validator.methods.original_remote = $.validator.methods.remote; 1495 $.validator.methods.remote = function(value, element, param) { 1496 if ( this.optional(element) ) { 1497 return "dependency-mismatch"; 1498 } 1499 1500 var previous = this.previousValue(element); 1501 if (!this.settings.messages[element.name] ) { 1502 this.settings.messages[element.name] = {}; 1503 } 1504 previous.originalMessage = this.settings.messages[element.name].remote; 1505 this.settings.messages[element.name].remote = previous.message; 1506 1507 param = typeof param === "string" && {url:param} || param; 1508 1509 if ( previous.old === value ) { 1510 return previous.valid; 1511 } 1512 1513 previous.old = value; 1514 var validator = this; 1515 this.startRequest(element); 1516 var data = {}; 1517 data[element.name] = value; 1518 1519 $.ajax($.extend(true, { 1520 url: param, 1521 mode: "abort", 1522 port: "validate" + element.name, 1523 dataType: "json", 1524 progress: { 1525 type: 'throbber', 1526 message: Drupal.t('Please wait...') 1527 }, 1528 data: data, 1529 // We provide our own success handling to ensure the pending array is 1530 // cleaned before executing showErrors(). That way handling highlight 1531 // unhighlight is easier for pending elements (avoid) flickering. 1532 // However we still need the original success handler of Drupal.ajax to 1533 // do the progress handling. 1534 success: function(response) { 1535 // Remove element from pending list as we now know what state it has.
1536 delete validator.pending[element.name]; 1537 validator.settings.messages[element.name].remote = previous.originalMessage; 1538 var valid = response === true || response === "true"; 1539 if ( valid ) { 1540 var submitted = validator.formSubmitted; 1541 // Don't invoke this because this will avoid that the highlight / 1542 // unhighlight handlers are called. 1543 // validator.prepareElement(element); 1544 validator.formSubmitted = submitted; 1545 validator.successList.push(element); 1546 delete validator.invalid[element.name]; 1547 validator.showErrors(); 1548 } else { 1549 var errors = {}; 1550 var message = response || validator.defaultMessage( element, "remote" ); 1551 errors[element.name] = previous.message = $.isFunction(message) ? message(value) : message; 1552 validator.invalid[element.name] = true; 1553 validator.showErrors(errors); 1554 } 1555 previous.valid = valid; 1556 validator.stopRequest(element, valid); 1557 }, 1558 beforeSend: function () { 1559 // Disable the element that received the change to prevent user interface 1560 // interaction while the Ajax request is in progress. ajax.ajaxing prevents 1561 // the element from triggering a new request, but does not prevent the user 1562 // from changing its value. 1563 $(element).addClass('progress-disabled').attr('disabled', true); 1564 1565 // Insert progressbar or throbber. 1566 if (this.progress.type == 'bar') { 1567 var progressBar = new Drupal.progressBar('ajax-progress-' + element.id, eval(this.progress.update_callback), this.progress.method, eval(this.progress.error_callback)); 1568 if (this.progress.message) { 1569 progressBar.setProgress(-1, this.progress.message); 1570 } 1571 if (this.progress.url) { 1572 progressBar.startMonitoring(this.progress.url, this.progress.interval || 1500); 1573 } 1574 this.progress.element = $(progressBar.element).addClass('ajax-progress ajax-progress-bar'); 1575 this.progress.object = progressBar; 1576 $(element).after(this.progress.element); 1577 } 1578 else if (this.progress.type == 'throbber') { 1579 this.progress.element = $('<div class="ajax-progress ajax-progress-throbber"><div class="throbber"> </div></div>'); 1580 if (this.progress.message) { 1581 $('.throbber', this.progress.element).after('<div class="message">' + this.progress.message + '</div>'); 1582 } 1583 $(element).after(this.progress.element); 1584 } 1585 }, 1586 complete: function() { 1587 // Remove the progress element. 1588 if (this.progress.element) { 1589 $(this.progress.element).remove(); 1590 } 1591 if (this.progress.object) { 1592 this.progress.object.stopMonitoring(); 1593 } 1594 $(element).removeClass('progress-disabled').removeAttr('disabled'); 1595 } 1596 }, param)); 1597 return "pending"; 1598 }; 1599 1600 1601 /** 1602 * Allow other modules to add more rules. 1603 * @event clientsideValidationAddCustomRules 1604 * @name clientsideValidationAddCustomRules 1605 * @memberof Drupal.clientsideValidation 1606 */ 1607 $.event.trigger('clientsideValidationAddCustomRules'); 1608 1609 1610 /** 1611 * strip illegal tags 1612 * @memberof Drupal.clientsideValidation 1613 * @private 1614 */ 1615 function strip_tags (input, allowed) { 1616 allowed = (((allowed || "") + "").toLowerCase().match(/<[a-z][a-z0-9]*>/g) || []).join(''); // making sure the allowed arg is a string containing only tags in lowercase (<a><b><c>) 1617 var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi, 1618 commentsAndPhpTags = /<!--[\s\S]*?-->|<\?(?:php)?[\s\S]*?\?>/gi; 1619 return input.replace(commentsAndPhpTags, '').replace(tags, function ($0, $1) { 1620 return allowed.indexOf('<' + $1.toLowerCase() + '>') > -1 ? $0 : ''; 1621 }); 1622 } 1623 }; 1624 1625 Drupal.behaviors.ZZZClientsideValidation = { 1626 attach: function () { 1627 function changeAjax(ajax_el) { 1628 var origBeforeSubmit = Drupal.ajax[ajax_el].options.beforeSubmit; 1629 Drupal.ajax[ajax_el].options.beforeSubmit = function (form_values, element, options) { 1630 var ret = origBeforeSubmit(form_values, element, options); 1631 // If this function didn't return anything, just set the return value to true. 1632 // If it did return something, allow it to prevent submit if necessary. 1633 if (typeof ret === 'undefined') { 1634 ret = true; 1635 } 1636 var id = element.is('form') ? element.attr('id') : element.closest('form').attr('id'); 1637 if (id && Drupal.myClientsideValidation.validators[id] && Drupal.myClientsideValidation.validators[id].form) { 1638 Drupal.myClientsideValidation.validators[id].onsubmit = false;
1639 ret = ret && Drupal.myClientsideValidation.validators[id].form(); 1640 if (!ret) { 1641 Drupal.ajax[ajax_el].ajaxing = false; 1642 } 1643 } 1644 return ret; 1645 }; 1646 } 1647 // Set validation for ctools modal forms 1648 for (var ajax_el in Drupal.ajax) { 1649 if (Drupal.ajax.hasOwnProperty(ajax_el) && typeof Drupal.ajax[ajax_el] !== 'undefined') { 1650 var $ajax_el = $(Drupal.ajax[ajax_el].element); 1651 var ajax_form = $ajax_el.is('form') ? $ajax_el.attr('id') : $ajax_el.closest('form').attr('id'); 1652 var change_ajax = true; 1653 if (ajax_form != null && typeof Drupal.myClientsideValidation.forms[ajax_form] !== 'undefined') { 1654 change_ajax = Boolean(parseInt(Drupal.myClientsideValidation.forms[ajax_form].general.validateBeforeAjax, 10)); 1655 } 1656 if (!$ajax_el.hasClass('cancel') && change_ajax) { 1657 changeAjax(ajax_el); 1658 } 1659 } 1660 } 1661 } 1662 }; 1663 1664 /** 1665 * Integrate with the states handling. 1666 * 1667 * Prvides the condtions: 1668 * * clientside_validation: Value is valid. 1669 * * clientside_validated: Value was validated actually. 1670 */ 1671 if (typeof Drupal.states != 'undefined') { 1672 Drupal.states.Trigger.states.clientside_validation = function (element) { 1673 element 1674 .bind('clientsideValidationValid', function () { 1675 element.trigger({type: 'state:clientside_validation', value: true}); 1676 element.trigger({type: 'state:clientside_validated', value: true}); 1677 }) 1678 .bind('clientsideValidationInvalid', function () { 1679 element.trigger({type: 'state:clientside_validation', value: false}); 1680 element.trigger({type: 'state:clientside_validated', value: true}); 1681 }) 1682 .bind('change keyup', function () { 1683 element.trigger({type: 'state:clientside_validated', value: false}); 1684 }); 1685 1686 Drupal.states.postponed.push($.proxy(function () { 1687 element.trigger({type: 'state:clientside_validation', value: true}); 1688 element.trigger({type: 'state:clientside_validated', value: false}); 1689 }, window)); 1690 }; 1691 } 1692})(jQuery);
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.