1/* 2 Class: FormCheck 3 Performs different tests on forms and indicates errors. 4 5 Usage: 6 Works with these types of fields : 7 - input (text, radio, checkbox) 8 - textarea 9 - select 10 11 You just need to add a specific class to each fields you want to check. 12 For example, if you add the class 13 (code) 14 validate['required','length[4, -1]','differs[email]','digit'] 15 (end code) 16 the value's field must be set (required) with a minimum length of four chars (4, -1), 17 must differs of the input named email (differs[email]), and must be digit. 18 19 You can perform check during the datas entry or on the submit action, shows errors as tips or in a div before or after the field, 20 show errors one by one or all together, show a list of all errors at the top of the form, localize error messages, add new regex check, ... 21 22 The layout is design only with css. Now I added a hack to use transparent png with IE6, so you can use png images in formcheck.css (works only for theme, so the file must be named formcheck.css). It can also works with multiple forms on a single html page. 23 The class supports now internationalization. To use it, simply specify a new <script> element in your html head, like this : <script type="text/javascript" src="formcheck/lang/fr.js"></script>. 24 25 If you add the class 26 (code) 27 validate['submit'] 28 (end code) 29 to an element like an anchor (or anything else), this element will act as a submit button. 30 31 N.B. : you must load the language script before the formcheck and this method overpass the old way. You can create new languages following existing ones. You can otherwise still specifiy the alerts' strings when you initialize the Class, with options. 32 If you don't use a language script, the alert will be displayed in english. 33 34 Test type: 35 You can perform various test on fields by addind them to the validate class. Be careful to *not use space chars*. Here is the list of them. 36 37 required - The field becomes required. This is a regex, you can change it with class options. 38 alpha - The value is restricted to alphabetic chars. This is a regex, you can change it with class options. 39 alphanum - The value is restricted to alphanumeric characters only. This is a regex, you can change it with class options. 40 nodigit - The field doesn't accept digit chars. This is a regex, you can change it with class options. 41 digit - The value is restricted to digit (no floating point number) chars, you can pass two arguments (f.e. digit[21,65]) to limit the number between them. Use -1 as second argument to not set a maximum. 42 number - The value is restricted to number, including floating point number. This is a regex, you can change it with class options. 43 email - The value is restricted to valid email. This is a regex, you can change it with class options. 44 phone - The value is restricted to phone chars. This is a regex, you can change it with class options. 45 url: - The value is restricted to url. This is a regex, you can change it with class options. 46 confirm - The value has to be the same as the one passed in argument. f.e. confirm[password]. 47 differs - The value has to be diferent as the one passed in argument. f.e. differs[user]. 48 length - The value length is restricted by argument (f.e. length[6,10]). Use -1 as second argument to not set a maximum. 49 50 You can also use a custom function to check a field. For example, if you have a field with class 51 (code) 52 validate['required','%customCheck'] 53 (end code) 54 the function customCheck(el) will be called to validate the field. '%customcheck' works with other validate(s) together, and '~customcheck' works if the element pass the other validate(s). 55 Here is an example of what customCheck could look : 56 (code) 57 function customCheck(el){ 58 if (!el.value.test(/^[A-Z]/)) { 59 el.errors.push("Username should begin with an uppercase letter"); 60 return false; 61 } else { 62 return true; 63 } 64 } 65 (end code) 66 67 It is now possible to register new fields after a new FormCheck call by using <FormCheck::register> (see <FormCheck::dispose> too). 68 69 Parameters: 70 When you initialize the class with addEvent, you can set some options. If you want to modify regex, you must do it in a hash, like for display or alert. You can also add new regex check method by adding the regex and an alert with the same name. 71 72 Required: 73 74 form_id - The id of the formular. This is required. 75 76 Optional: 77 78 submitByAjax - you can set this to true if you want to submit your form with ajax. You should use provided events to handle the ajax request (see below). By default it is false. 79 ajaxResponseDiv - id of element to inject ajax response into (can also use onAjaxSuccess). By default it is false. 80 ajaxEvalScripts - use evalScripts in the Request response. Can be true or false, by default it is false. 81 onAjaxRequest - Function to fire when the Request event starts. 82 onAjaxSuccess - Function to fire when the Request receives . Args: response [the request response] - see Mootools docs for Request.onSuccess. 83 onAjaxFailure - Function to fire if the Request fails. 84 85 tipsClass - The class to apply to tipboxes' errors. By default it is 'fc-tbx'. 86 errorClass - The class to apply to alertbox (not tips). By default it is 'fc-error'. 87 fieldErrorClass - The class to apply to fields with errors, except for radios. You should also turn on options.addClassErrorToField. By default it is 'fc-field-error'
88 89 trimValue - If set to true, strip whitespace (or other characters) from the beginning and end of values. By default it is false. 90 validateDisabled - If set to true, disabled input will be validated too, otherwise not. 91 92 Display: 93 This is a hash of display settings. in here you can modify. 94 95 showErrors - 0 : onSubmit, 1 : onSubmit & onBlur, by default it is 1. 96 titlesInsteadNames - 0 : When you do a check using differs or confirm, it takes the field name for the alert. If it's set to 1, it will use the title instead of the name. 97 errorsLocation - 1 : tips, 2 : before, 3 : after, by default it is 1. 98 indicateErrors - 0 : none, 1 : one by one, 2 : all, by default it is 1. 99 indicateErrorsInit - 0 : determine if the form must be checked on initialize. Could be usefull to force the user to update fields that don't validate. 100 keepFocusOnError - 0 : normal behaviour, 1 : the current field keep the focus as it remain errors. By default it is 0. 101 checkValueIfEmpty - 0 : When you leave a field and you have set the showErrors option to 1, the value is tested only if a value has been set. 1 : The value is tested in any case. By default it is 1. 102 addClassErrorToField - 0 : no class is added to the field, 1 : the options.fieldErrorClass is added to the field with an error (except for radio). By default it is 0. 103 104 fixPngForIe - 0 : do nothing, 1 : fix png alpha for IE6 in formcheck.css. By default it is 1. 105 replaceTipsEffect - 0 : No effect on tips replace when we resize the broswer, 1: tween transition on browser resize; 106 closeTipsButton - 0 : the close button of the tipbox is hidden, 1 : the close button of the tipbox is visible. By default it is 1. 107 flashTips - 0 : normal behaviour, 1 : the tipbox "flash" (disappear and reappear) if errors remain when the form is submitted. By default it is 0. 108 tipsPosition - 'right' : the tips box is placed on the right part of the field, 'left' to place it on the left part. By default it is 'right'. 109 tipsOffsetX - Horizontal position of the tips box (margin-left), , by default it is 100 (px). 110 tipsOffsetY - Vertical position of the tips box (margin-bottom), , by default it is -10 (px). 111 112 listErrorsAtTop - List all errors at the top of the form, , by default it is false. 113 scrollToFirst - Smooth scroll the page to first error and focus on it, by default it is true. 114 fadeDuration - Transition duration (in ms), by default it is 300. 115 116 Alerts: 117 This is a hash of alerts settings. in here you can modify strings to localize or wathever else. %0 and %1 represent the argument. 118 119 required - "This field is required." 120 alpha - "This field accepts alphabetic characters only." 121 alphanum - "This field accepts alphanumeric characters only." 122 nodigit - "No digits are accepted." 123 digit - "Please enter a valid integer." 124 digitmin - "The number must be at least %0" 125 digitltd - "The value must be between %0 and %1" 126 number - "Please enter a valid number." 127 email - "Please enter a valid email: <br /><span>E.g. [email protected]</span>" 128 phone - "Please enter a valid phone." 129 url - "Please enter a valid url: <br /><span>E.g. http://www.domain.com</span>" 130 confirm - "This field is different from %0" 131 differs - "This value must be different of %0" 132 length_str - "The length is incorrect, it must be between %0 and %1" 133 length_fix - "The length is incorrect, it must be exactly %0 characters" 134 lengthmax - "The length is incorrect, it must be at max %0" 135 lengthmin - "The length is incorrect, it must be at least %0" 136 checkbox - "Please check the box" 137 radios - "Please select a radio" 138 select - "Please choose a value" 139 140 Example: 141 You can initialize a formcheck (no scroll, custom classes and alert) by adding for example this in your html head this code : 142 143 (code) 144 <script type="text/javascript"> 145 window.addEvent('domready', function() { 146 var myCheck = new FormCheck('form_id', { 147 tipsClass : 'tips_box', 148 display : { 149 scrollToFirst : false 150 }, 151 alerts : { 152 required : 'This field is ablolutely required! Please enter a value' 153 } 154 }) 155 }); 156 </script> 157 (end code) 158 159 About: 160 formcheck.js v.1.4.2 for mootools v1.2 - 12 / 2008 161 162 by Floor SA (http://www.floor.ch) MIT-style license 163 164 Created by Luca Pillonel, last modified by Luca Pillonel 165 166 Credits: 167 This class was inspired by fValidator by Fabio Zendhi Nagao (http://zend.lojcomm.com.br) 168 169 Thanks to oneZ, Blots, huughelmink, chdeliens, and everyone who contribute to this project... 170*/ 171 172var FormCheck = new Class({ 173 174 Implements: [Options, Events], 175 176 options : { 177 178 tipsClass: 'fc-tbx', //tips error class 179 errorClass: 'fc-error', //div error class 180 fieldErrorClass: 'fc-field-error', //error class for elements 181
182 trimValue : false, //trim (remove whitespaces before and after) the value 183 validateDisabled : false, //skip validation on disabled input if set to false. 184 185 submitByAjax : false, //false : standard submit way, true : submit by ajax 186 ajaxResponseDiv : false, //element to inject ajax response into (can also use onAjaxSuccess) [cronix] 187 ajaxEvalScripts : false, //use evalScripts in the Request response [cronix] 188 onAjaxRequest : $empty, //Function to fire when the Request event starts 189 onAjaxSuccess : $empty, //Function to fire when the Request receives . Args: response [the request response] - see Mootools docs for Request.onSuccess 190 onAjaxFailure : $empty, //Function to fire if the Request fails 191 192 display : { 193 showErrors : 0, 194 titlesInsteadNames : 0, 195 errorsLocation : 1, 196 indicateErrors : 1, 197 indicateErrorsInit : 0, 198 keepFocusOnError : 0, 199 checkValueIfEmpty : 1, 200 addClassErrorToField : 0, 201 fixPngForIe : 1, 202 replaceTipsEffect : 1, 203 flashTips : 0, 204 closeTipsButton : 1, 205 tipsPosition : "right", 206 tipsOffsetX : -45, 207 tipsOffsetY : 0, 208 listErrorsAtTop : false, 209 scrollToFirst : true, 210 fadeDuration : 300 211 }, 212 213 alerts : { 214 required: "This field is required.", 215 alpha: "This field accepts alphabetic characters only.", 216 alphanum: "This field accepts alphanumeric characters only.", 217 nodigit: "No digits are accepted.", 218 digit: "Please enter a valid integer.", 219 digitltd: "The value must be between %0 and %1", 220 number: "Please enter a valid number.", 221 email: "Please enter a valid email.", 222 phone: "Please enter a valid phone.", 223 url: "Please enter a valid url.", 224 225 confirm: "This field is different from %0", 226 differs: "This value must be different of %0", 227 length_str: "The length is incorrect, it must be between %0 and %1", 228 length_fix: "The length is incorrect, it must be exactly %0 characters", 229 lengthmax: "The length is incorrect, it must be at max %0", 230 lengthmin: "The length is incorrect, it must be at least %0", 231 checkbox: "Please check the box", 232 radios: "Please select a radio", 233 select: "Please choose a value" 234 }, 235 236 regexp : { 237 required : /[^.*]/, 238 alpha : /^[a-z ._-]+$/i, 239 alphanum : /^[a-z0-9 ._-]+$/i, 240 digit : /^[-+]?[0-9]+$/, 241 nodigit : /^[^0-9]+$/, 242 number : /^[-+]?\d*\.?\d+$/, 243 email : /^[a-z0-9._%\-+]+@[a-z0-9.-]+\.[a-z]{2,4}$/i, 244 phone : /^[\d\s ().-]+$/, 245 url : /^(http|https|ftp)\:\/\/[a-z0-9\-\.]+\.[a-z]{2,3}(:[a-z0-9]*)?\/?([a-z0-9\-\._\?\,\'\/\\\+&%\$#\=~])*$/i 246 } 247 }, 248 249 /* 250 Constructor: initialize 251 Constructor 252 253 Add event on formular and perform some stuff, you now, like settings, ... 254 */ 255 initialize : function(form, options) { 256 if (this.form = $(form)) { 257 258 this.form.isValid = true; 259 this.regex = ['length']; 260 this.setOptions(options); 261 262 //internalization 263 if (typeof(formcheckLanguage) != 'undefined') this.options.alerts = $merge(this.options.alerts, formcheckLanguage); 264 265 this.validations = []; 266 this.alreadyIndicated = false; 267 this.firstError = false; 268 269 var regex = new Hash(this.options.regexp); 270 regex.each(function(el, key) { 271 this.regex.push(key); 272 }, this); 273 274 this.form.getElements("*[class*=validate]").each(function(el) { 275 this.register(el); 276 }, this); 277 278 this.form.addEvents({ 279 "submit": this.onSubmit.bind(this) 280 }); 281 282 if(this.options.display.fixPngForIe) this.fixIeStuffs(); 283 document.addEvent('mousewheel', function(){ 284 this.isScrolling = false; 285 }.bind(this)); 286 } 287 }, 288 289 /* 290 Function: register 291 Allows you to declare afterward new fields to the formcheck, to check dynamically loaded fields for example. 292 293 Example: 294 (code) 295 <script type="text/javascript"> 296 window.addEvent('domready', function() { 297 formcheck = new FormCheck('form_id'); 298 }); 299 300 // ...some code... 301 302 var newField = new Element('input', { 303 class : "validate['required']", 304 name : "new-field" 305 }).inject('form_id'); 306 formcheck.register(newField); 307 308 new Element('input', { 309 class : "validate['required']", 310 name : "another-field", 311 id : "another-field" 312 }).inject('form_id');
313 formcheck.register($('another-field')); 314 </script> 315 (end code) 316 317 See also: 318 <FormCheck::dispose> 319 */ 320 register : function(el) { 321 el.validation = []; 322 el.getProperty("class").split(' ').each(function(classX) { 323 if(classX.match(/^validate(\[.+\])$/)) { 324 var validators = eval(classX.match(/^validate(\[.+\])$/)[1]); 325 for(var i = 0; i < validators.length; i++) { 326 el.validation.push(validators[i]); 327 if (validators[i].match(/^confirm\[/)) { 328 var field = eval(validators[i].match(/^.+(\[.+\])$/)[1].replace(/([A-Z0-9\._-]+)/i, "'$1'")); 329 if (this.form[field].validation.contains('required')){ 330 el.validation.push('required'); 331 } 332 333 } 334 } 335 this.addListener(el); 336 } 337 }, this); 338 }, 339 340 /* 341 Function: dispose 342 Allows you to remove a declared field from formCheck 343 344 Example: 345 (code) 346 <script type="text/javascript"> 347 window.addEvent('domready', function() { 348 formcheck = new FormCheck('form_id'); 349 }); 350 351 // ...some code... 352 353 formcheck.dispose($('obsolete-field')); 354 </script> 355 (end code) 356 357 See also: 358 <FormCheck::register> 359 */ 360 dispose : function(element) { 361 this.validations.erase(element); 362 }, 363 364 /* 365 Function: addListener 366 Private method 367 368 Add listener on fields 369 */ 370 addListener : function(el) { 371 this.validations.push(el); 372 el.errors = []; 373 374 if (this.options.display.indicateErrorsInit) { 375 this.validations.each(function(el) { 376 if(!this.manageError(el,'submit')) this.form.isValid = false; 377 }, this); 378 return true; 379 } 380 381 if (el.validation[0] == 'submit') { 382 el.addEvent('click', function(e){ 383 this.onSubmit(e); 384 }.bind(this)); 385 return true; 386 } 387 388 if (this.isChildType(el) == false) el.addEvent('blur', function() { 389 (function(){ 390 if(!this.fxRunning && (el.element || this.options.display.showErrors == 1) && (this.options.display.checkValueIfEmpty || el.value)) 391 this.manageError(el, 'blur') 392 }.bind(this)).delay(100); 393 }.bind(this)) 394 //We manage errors on radio 395 else if (this.isChildType(el) == true) { 396 //We get all radio from the same group and add a blur option 397 var nlButtonGroup = this.form.getElements('input[name="'+ el.getProperty("name") +'"]'); 398 nlButtonGroup.each(function(radio){ 399 radio.addEvent('blur', function(){ 400 (function(){ 401 if((el.element || this.options.display.showErrors == 1) && (this.options.display.checkValueIfEmpty || el.value)) this.manageError(el, 'click'); 402 }.bind(this)).delay(100); 403 }.bind(this)) 404 },this); 405 } 406 }, 407 408 /* 409 Function: validate 410 Private method 411 412 Dispatch check to other methods 413 */ 414 validate : function(el) { 415 el.errors = []; 416 el.isOk = true; 417 418 //skip validation and trim if specified 419 if (!this.options.validateDisabled && el.get('disabled')) return true; 420 if (this.options.trimValue && el.value) el.value = el.value.trim(); 421 422 el.validation.each(function(rule) { 423 if(this.isChildType(el)) { 424 if (this.validateGroup(el) == false) { 425 el.isOk = false; 426 } 427 } else { 428 var ruleArgs = []; 429 430 if(rule.match(/^.+\[/)) { 431 var ruleMethod = rule.split('[')[0]; 432 ruleArgs = eval(rule.match(/^.+(\[.+\])$/)[1].replace(/([A-Z0-9\._-]+)/i, "'$1'")); 433 } else var ruleMethod = rule; 434 435 if (this.regex.contains(ruleMethod) && el.get('tag') != "select") { 436 if (this.validateRegex(el, ruleMethod, ruleArgs) == false) { 437 el.isOk = false; 438 } 439 } 440 441 if (ruleMethod == 'confirm') { 442 if (this.validateConfirm(el, ruleArgs) == false) { 443 el.isOk = false; 444 } 445 } 446 if (ruleMethod == 'differs') { 447 if (this.validateDiffers(el, ruleArgs) == false) { 448 el.isOk = false; 449 } 450 } 451 if (el.get('tag') == "select" || (el.type == "checkbox" && ruleMethod == 'required')) { 452 if (this.simpleValidate(el) == false) { 453 el.isOk = false; 454 } 455 } 456 if(rule.match(/%[A-Z0-9\._-]+$/i) || (el.isOk && rule.match(/~[A-Z0-9\._-]+$/i))) { 457 if(eval(rule.slice(1)+'(el)') == false) { 458 el.isOk = false; 459 } 460 } 461 } 462 }, this); 463 464 if (el.isOk) return true; 465 else return false; 466 }, 467 468 /* 469 Function: simpleValidate 470 Private method 471 472 Perform simple check for select fields and checkboxes 473 */ 474 simpleValidate : function(el) { 475 if (el.get('tag') == 'select' && el.selectedIndex <= 0) { 476 el.errors.push(this.options.alerts.select); 477 return false; 478 } else if (el.type == "checkbox" && el.checked == false) { 479 el.errors.push(this.options.alerts.checkbox); 480 return false; 481 } 482 return true; 483 }, 484 485 /* 486 Function: validateRegex 487 Private method 488 489 Perform regex validations 490 */ 491 validateRegex : function(el, ruleMethod, ruleArgs) { 492 var msg = ""; 493 if (ruleArgs[1] && ruleMethod == 'length') { 494 if (ruleArgs[1] == -1) { 495 this.options.regexp.length = new RegExp("^[\\s\\S]{"+ ruleArgs[0] +",}$"); 496 msg = this.options.alerts.lengthmin.replace("%0",ruleArgs[0]); 497 } else if(ruleArgs[0] == ruleArgs[1]) { 498 this.options.regexp.length = new RegExp("^[\\s\\S]{"+ ruleArgs[0] +"}$"); 499 msg = this.options.alerts.length_fix.replace("%0",ruleArgs[0]); 500 } else { 501 this.options.regexp.length = new RegExp("^[\\s\\S]{"+ ruleArgs[0] +","+ ruleArgs[1] +"}$"); 502 msg = this.options.alerts.length_str.replace("%0",ruleArgs[0]).replace("%1",ruleArgs[1]); 503 } 504 } else if (ruleArgs[0] && ruleMethod == 'length') { 505 this.options.regexp.length = new RegExp("^.{0,"+ ruleArgs[0] +"}$"); 506 msg = this.options.alerts.lengthmax.replace("%0",ruleArgs[0]); 507 } else { 508 msg = this.options.alerts[ruleMethod]; 509 } 510 if (ruleArgs[1] && ruleMethod == 'digit') { 511 var regres = true; 512 if (!this.options.regexp.digit.test(el.value)) { 513 el.errors.push(this.options.alerts[ruleMethod]); 514 regres = false; 515 } 516 if (ruleArgs[1] == -1) { 517 if (el.value >= ruleArgs[0]) var valueres = true; else var valueres = false; 518 msg = this.options.alerts.digitmin.replace("%0",ruleArgs[0]); 519 } else { 520 if (el.value >= ruleArgs[0] && el.value <= ruleArgs[1]) var valueres = true; else var valueres = false; 521 msg = this.options.alerts.digitltd.replace("%0",ruleArgs[0]).replace("%1",ruleArgs[1]); 522 } 523 if (regres == false || valueres == false) { 524 el.errors.push(msg); 525 return false; 526 } 527 } else if (this.options.regexp[ruleMethod].test(el.value) == false) { 528 el.errors.push(msg); 529 return false; 530 } 531 return true; 532 }, 533 534 /* 535 Function: validateConfirm 536 Private method 537 538 Perform confirm validations 539 */ 540 validateConfirm: function(el,ruleArgs) { 541 542 var confirm = ruleArgs[0]; 543 if(el.value != this.form[confirm].value){ 544 if (this.options.display.titlesInsteadNames) 545 var msg = this.options.alerts.confirm.replace("%0",this.form[confirm].getProperty('title'));
546 else 547 var msg = this.options.alerts.confirm.replace("%0",confirm); 548 el.errors.push(msg); 549 return false; 550 } 551 return true; 552 }, 553 554 /* 555 Function: validateDiffers 556 Private method 557 558 Perform differs validations 559 */ 560 validateDiffers: function(el,ruleArgs) { 561 var differs = ruleArgs[0]; 562 if(el.value == this.form[differs].value){ 563 if (this.options.display.titlesInsteadNames) 564 var msg = this.options.alerts.differs.replace("%0",this.form[differs].getProperty('title')); 565 else 566 var msg = this.options.alerts.differs.replace("%0",differs); 567 el.errors.push(msg); 568 return false; 569 } 570 return true; 571 }, 572 573 /* 574 Function: isChildType 575 Private method 576 577 Determine if the field is a group of radio or not. 578 */ 579 isChildType: function(el) { 580 return ($defined(el.type) && el.type == 'radio') ? true : false; 581 }, 582 583 /* 584 Function: validateGroup 585 Private method 586 587 Perform radios validations 588 */ 589 validateGroup : function(el) { 590 el.errors = []; 591 var nlButtonGroup = this.form[el.getProperty("name")]; 592 el.group = nlButtonGroup; 593 var cbCheckeds = false; 594 595 for(var i = 0; i < nlButtonGroup.length; i++) { 596 if(nlButtonGroup[i].checked) { 597 cbCheckeds = true; 598 } 599 } 600 if(cbCheckeds == false) { 601 el.errors.push(this.options.alerts.radios); 602 return false; 603 } else { 604 return true; 605 } 606 }, 607 608 /* 609 Function: listErrorsAtTop 610 Private method 611 612 Display errors 613 */ 614 listErrorsAtTop : function(obj) { 615 if(!this.form.element) { 616 this.form.element = new Element('div', {'id' : 'errorlist', 'class' : this.options.errorClass}).injectTop(this.form); 617 } 618 if ($type(obj) == 'collection') { 619 new Element('p').set('html',"<span>" + obj[0].name + " : </span>" + obj[0].errors[0]).injectInside(this.form.element); 620 } else { 621 if ((obj.validation.contains('required') && obj.errors.length > 0) || (obj.errors.length >
621 0 && obj.value && obj.validation.contains('required') == false)) { 622 obj.errors.each(function(error) { 623 new Element('p').set('html',"<span>" + obj.name + " : </span>" + error).injectInside(this.form.element); 624 }, this); 625 } 626 } 627 }, 628 629 /* 630 Function: manageError 631 Private method 632 633 Manage display of errors boxes 634 */ 635 manageError : function(el, method) { 636 var isValid = this.validate(el); 637 if ((!isValid && el.validation.flatten()[0].contains('confirm[')) || (!isValid && el.validation.contains('required')) || (!el.validation.contains('required') && el.value && !isValid)) { 638 if(this.options.display.listErrorsAtTop == true && method == 'submit') 639 this.listErrorsAtTop(el, method); 640 if (this.options.display.indicateErrors == 2 ||this.alreadyIndicated == false || el.name == this.alreadyIndicated.name) 641 { 642 if(!this.firstError) this.firstError = el; 643 644 this.alreadyIndicated = el; 645 646 if (this.options.display.keepFocusOnError && el.name == this.firstError.name) (function(){el.focus()}).delay(20); 647 this.addError(el); 648 return false; 649 } 650 } else if ((isValid || (!el.validation.contains('required') && !el.value)) && el.element) { 651 this.removeError(el); 652 return true; 653 } 654 return true; 655 }, 656 657 /* 658 Function: addError 659 Private method 660 661 Add error message 662 */ 663 addError : function(obj) { 664 if(!obj.element && this.options.display.indicateErrors != 0) { 665 if (this.options.display.errorsLocation == 1) { 666 var pos = (this.options.display.tipsPosition == 'left') ? obj.getCoordinates().left : obj.getCoordinates().right; 667 var options = { 668 'opacity' : 0, 669 'position' : 'absolute', 670 'float' : 'left', 671 'left' : pos + this.options.display.tipsOffsetX 672 } 673 obj.element = new Element('div', {'class' : this.options.tipsClass, 'styles' : options}).injectInside(document.body); 674 this.addPositionEvent(obj); 675 } else if (this.options.display.errorsLocation == 2){ 676 obj.element = new Element('div', {'class' : this.options.errorClass, 'styles' : {'opacity' : 0}}).injectBefore(obj); 677 } else if (this.options.display.errorsLocation == 3){ 678 obj.element = new Element('div', {'class' : this.options.errorClass, 'styles' : {'opacity' : 0}}); 679 if ($type(obj.group) == 'object' || $type(obj.group) == 'collection') 680 obj.element.injectAfter(obj.group[obj.group.length-1]); 681 else 682 obj.element.injectAfter(obj); 683 } 684 } 685 if (obj.element && obj.element != true) { 686 obj.element.empty(); 687 if (this.options.display.errorsLocation == 1) { 688 var errors = []; 689 obj.errors.each(function(error) { 690 errors.push(new Element('p').set('html', error)); 691 }); 692 var tips = this.makeTips(errors).injectInside(obj.element); 693 if(this.options.display.closeTipsButton) { 694 tips.getElements('a.close').addEvent('mouseup', function(){ 695 this.removeError(obj); 696 }.bind(this)); 697 } 698 obj.element.setStyle('top', obj.getCoordinates().top - tips.getCoordinates().height + this.options.display.tipsOffsetY); 699 } else { 700 obj.errors.each(function(error) { 701 new Element('p').set('html',error).injectInside(obj.element); 702 }); 703 } 704 705 if (!this.options.display.fadeDuration || Browser.Engine.trident && Browser.Engine.version == 5 && this.options.display.errorsLocation < 2) { 706 obj.element.setStyle('opacity', 1); 707 } else { 708 obj.fx = new Fx.Tween(obj.element, { 709 'duration' : this.options.display.fadeDuration, 710 'ignore' : true, 711 'onStart' : function(){ 712 this.fxRunning = true; 713 }.bind(this), 714 'onComplete' : function() { 715 this.fxRunning = false; 716 if (obj.element && obj.element.getStyle('opacity').toInt() == 0) { 717 obj.element.destroy(); 718 obj.element = false; 719 } 720 }.bind(this) 721 }) 722 if(obj.element.getStyle('opacity').toInt() != 1) obj.fx.start('opacity', 1); 723 } 724 } 725 if (this.options.display.addClassErrorToField && this.isChildType(obj) == false){ 726 obj.addClass(this.options.fieldErrorClass); 727 obj.element = obj.element || true; 728 } 729 730 }, 731 732 /* 733 Function: addPositionEvent 734 735 Update tips position after a browser resize 736 */ 737 addPositionEvent : function(obj) { 738 if(this.options.display.replaceTipsEffect) { 739 obj.event = function(){ 740 new Fx.Morph(obj.element, { 741 'duration' : this.options.display.fadeDuration 742 }).start({ 743 'left':[obj.element.getStyle('left'), obj.getCoordinates().right + this.options.display.tipsOffsetX], 744 'top':[obj.element.getStyle('top'), obj.getCoordinates().top - obj.element.getCoordinates().height + this.options.display.tipsOffsetY] 745 }); 746 }.bind(this); 747 748 } else { 749 obj.event = function(){ 750 obj.element.setStyles({ 751 'left':obj.getCoordinates().right + this.options.display.tipsOffsetX, 752 'top':obj.getCoordinates().top - obj.element.getCoordinates().height + this.options.display.tipsOffsetY 753 }); 754 }.bind(this) 755 } 756 window.addEvent('resize', obj.event); 757 }, 758 759 /* 760 Function: removeError 761 Private method 762 763 Remove the error display 764 */ 765 removeError : function(obj) { 766 this.alreadyIndicated = false; 767 obj.errors = []; 768 obj.isOK = true; 769 window.removeEvent('resize', obj.event); 770 if (this.options.display.errorsLocation >= 2 && obj.element) { 771 new Fx.Tween(obj.element, { 772 'duration': this.options.display.fadeDuration 773 }).start('height', 0); 774 } 775 if (!this.options.display.fadeDuration || Browser.Engine.trident && Browser.Engine.version == 5 && this.options.display.errorsLocation == 1 && obj.element) { 776 this.fxRunning = true; 777 obj.element.destroy(); 778 obj.element = false; 779 (function(){this.fxRunning = false}.bind(this)).delay(200); 780 } else if (obj.element && obj.element != true) { 781 obj.fx.start('opacity', 0); 782 } 783 784 if (this.options.display.addClassErrorToField && !this.isChildType(obj)) 785 obj.removeClass(this.options.fieldErrorClass); 786 }, 787 788 /* 789 Function: focusOnError 790 Private method 791 792 Create set the focus to the first field with an error if needed 793 */ 794 focusOnError : function (obj) { 795 if (this.options.display.scrollToFirst && !this.alreadyFocused && !this.isScrolling) { 796 if (!this.options.display.indicateErrors || !this.options.display.errorsLocation) { 797 var dest = obj.getCoordinates().top-30; 798 } else if (this.alreadyIndicated.element) { 799 switch (this.options.display.errorsLocation){ 800 case 1 : 801 var dest = obj.element.getCoordinates().top; 802 break; 803 case 2 : 804 var dest = obj.element.getCoordinates().top-30; 805 break; 806 case 3 : 807 var dest = obj.getCoordinates().top-30; 808 break; 809 } 810 this.isScrolling = true; 811 } 812 if (window.getScroll.y != dest) { 813 new Fx.Scroll(window, { 814 onComplete : function() { 815 this.isScrolling = false; 816 obj.focus(); 817 }.bind(this) 818 }).start(0,dest); 819 } else { 820 this.isScrolling = false; 821 obj.focus(); 822 } 823 this.alreadyFocused = true; 824 } 825 }, 826 827 /* 828 Function: fixIeStuffs 829 Private method 830 831 Fix png for IE6 832 */ 833 fixIeStuffs : function () { 834 if (Browser.Engine.trident4) { 835 //We fix png stuffs 836 var rpng = new RegExp('url\\(([\.a-zA-Z0-9_/:-]+\.png)\\)'); 837 var search = new RegExp('(.+)formcheck\.css'); 838 for (var i = 0; i < document.styleSheets.length; i++){ 839 if (document.styleSheets[i].href.match(/formcheck\.css$/)) { 840 var root = document.styleSheets[i].href.replace(search, '$1'); 841 var count = document.styleSheets[i].rules.length; 842 for (var j = 0; j < count; j++){ 843 var cssstyle = document.styleSheets[i].rules[j].style; 844 var bgimage = root + cssstyle.backgroundImage.replace(rpng, '$1'); 845 if (bgimage && bgimage.match(/\.png/i)){ 846 var scale = (cssstyle.backgroundRepeat == 'no-repeat') ? 'crop' : 'scale'; 847 cssstyle.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, src=\'' + bgimage + '\', sizingMethod=\''+ scale +'\')'; 848 cssstyle.backgroundImage = "none"; 849 } 850 } 851 } 852 } 853 } 854 }, 855 856 /* 857 Function: makeTips 858 Private method 859 860 Create tips boxes 861 */ 862 makeTips : function(txt) { 863 var table = new Element('table'); 864 table.cellPadding ='0'; 865 table.cellSpacing ='0'; 866 table.border ='0'; 867 868 var tbody = new Element('tbody').injectInside(table); 869 var tr1 = new Element('tr').injectInside(tbody); 870 new Element('td', {'class' : 'tl'}).injectInside(tr1); 871 new Element('td', {'class' : 't'}).injectInside(tr1); 872 new Element('td', {'class' : 'tr'}).injectInside(tr1); 873 var tr2 = new Element('tr').injectInside(tbody); 874 new Element('td', {'class' : 'l'}).injectInside(tr2); 875 var cont = new Element('td', {'class' : 'c'}).injectInside(tr2); 876 var errors = new Element('div', {'class' : 'err'}).injectInside(cont); 877 txt.each(function(error) { 878 error.injectInside(errors); 879 });
880 if (this.options.display.closeTipsButton) new Element('a',{'class' : 'close'}).injectInside(cont); 881 new Element('td', {'class' : 'r'}).injectInside(tr2); 882 var tr3 = new Element('tr').injectInside(tbody); 883 new Element('td', {'class' : 'bl'}).injectInside(tr3); 884 new Element('td', {'class' : 'b'}).injectInside(tr3); 885 new Element('td', {'class' : 'br'}).injectInside(tr3); 886 return table; 887 }, 888 889 /* 890 Function: reinitialize 891 Private method 892 893 Reinitialize form before submit check 894 */ 895 reinitialize: function() { 896 this.validations.each(function(el) { 897 if (el.element) { 898 el.errors = []; 899 el.isOK = true; 900 if(this.options.display.flashTips == 1) { 901 el.element.destroy(); 902 el.element = false; 903 } 904 } 905 }, this); 906 if (this.form.element) this.form.element.empty(); 907 this.alreadyFocused = false; 908 this.firstError = false; 909 this.elementToRemove = this.alreadyIndicated; 910 this.alreadyIndicated = false; 911 this.form.isValid = true; 912 }, 913 914 /* 915 Function: submitByAjax 916 Private method 917 918 Send the form by ajax, and replace the form with response 919 */ 920 921 submitByAjax: function() { 922 var url = this.form.getProperty('action'); 923 this.fireEvent('ajaxRequest'); 924 new Request({ 925 url: url, 926 method: this.form.getProperty('method'), 927 data : this.form.toQueryString(), 928 evalScripts: this.options.ajaxEvalScripts, 929 onFailure: function(instance){ 930 this.fireEvent('ajaxFailure', instance); 931 }.bind(this), 932 onSuccess: function(result){ 933 this.fireEvent('ajaxSuccess', result); 934 if(this.options.ajaxResponseDiv) $(this.options.ajaxResponseDiv).set('html',result); 935 }.bind(this) 936 }).send(); 937 }, 938 939 /* 940 Function: onSubmit 941 Private method 942 943 Perform check on submit action 944 */ 945 onSubmit: function(event) { 946 this.reinitialize(); 947 948 this.validations.each(function(el) { 949 var validation = this.manageError(el,'submit'); 950 if(!validation) this.form.isValid = false; 951 }, this); 952 953 if (this.form.isValid) { 954 if (this.options.submitByAjax) { 955 new Event(event).stop(); 956 this.submitByAjax(); 957 } 958 } else { 959 new Event(event).stop(); 960 if (this.elementToRemove && this.elementToRemove != this.firstError && this.options.display.indicateErrors == 1) { 961 this.removeError(this.elementToRemove); 962 } 963 this.focusOnError(this.firstError) 964 } 965 } 966});
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.