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: 'formlineinputerror', //error class for elements 181 divErrorClass: 'formlineerror', //error class for div container elements 182 183 trimValue : false, //trim (remove whitespaces before and after) the value 184 validateDisabled : false, //skip validation on disabled input if set to false. 185 186 submitByAjax : false, //false : standard submit way, true : submit by ajax 187 customSubmit : false, //false : standard submit way, true : submit by onFormIsValid event 188 ajaxResponseDiv : false, //element to inject ajax response into (can also use onAjaxSuccess) [cronix] 189 ajaxEvalScripts : false, //use evalScripts in the Request response [cronix] 190 onAjaxRequest : $empty, //Function to fire when the Request event starts 191 onAjaxSuccess : $empty, //Function to fire when the Request receives . Args: response [the request response] - see Mootools docs for Request.onSuccess 192 onAjaxFailure : $empty, //Function to fire if the Request fails 193 onFormIsValid : $empty, //Function to fire when form is valid 194 195 display : { 196 showErrors : 0, 197 titlesInsteadNames : 0, 198 errorsLocation : 1, 199 indicateErrors : 1, 200 indicateErrorsInit : 0, 201 keepFocusOnError : 0, 202 checkValueIfEmpty : 1, 203 addClassErrorToField : 0, 204 fixPngForIe : 1, 205 replaceTipsEffect : 1, 206 flashTips : 0, 207 closeTipsButton : 1, 208 tipsPosition : "right", 209 tipsOffsetX : -75, 210 tipsOffsetY : 10, 211 listErrorsAtTop : false, 212 scrollToFirst : true, 213 fadeDuration : 300 214 }, 215 216 alerts : { 217 required: "Champs requis", 218 alpha: "This field accepts alphabetic characters only.", 219 alphanum: "Ce champs n'accepte que des charactères alphanumérique", 220 nodigit: "No digits are accepted.", 221 digit: "Please enter a valid integer.", 222 digitltd: "The value must be between %0 and %1", 223 number: "Please enter a valid number.", 224 email: "Adresse email invalide.", 225 phone: "Please enter a valid phone.", 226 url: "Please enter a valid url.", 227 228 confirm: "Champs de confirmation incorrect", 229 differs: "This value must be different of %0", 230 length_str: "The length is incorrect, it must be between %0 and %1", 231 length_fix: "The length is incorrect, it must be exactly %0 characters", 232 lengthmax: "The length is incorrect, it must be at max %0", 233 lengthmin: "Nb de caractères minimum : %0", 234 checkbox: "Please check the box", 235 radios: "Champs requis", 236 select: "Champs requis" 237 }, 238 239 regexp : { 240 required : /[^.*]/, 241 alpha : /^[a-z ._-]+$/i, 242 alphanum : /^[a-z0-9 ._-]+$/i, 243 digit : /^[-+]?[0-9]+$/, 244 nodigit : /^[^0-9]+$/, 245 number : /^[-+]?\d*\.?\d+$/, 246 email : /^[a-z0-9._%-]+@[a-z0-9.-]+\.[a-z]{2,4}$/i, 247 phone : /^[\d\s ().-]+$/, 248 url : /^(http|https|ftp)\:\/\/[a-z0-9\-\.]+\.[a-z]{2,3}(:[a-z0-9]*)?\/?([a-z0-9\-\._\?\,\'\/\\\+&%\$#\=~])*$/i 249 } 250 }, 251 252 /* 253 Constructor: initialize 254 Constructor 255 256 Add event on formular and perform some stuff, you now, like settings, ... 257 */ 258 initialize : function(form, options) { 259 if (this.form = $(form)) { 260 261 this.form.isValid = true; 262 this.regex = ['length']; 263 this.setOptions(options); 264 265 //internalization 266 if (typeof(formcheckLanguage) != 'undefined') this.options.alerts = $merge(this.options.alerts, formcheckLanguage); 267 268 this.validations = []; 269 this.alreadyIndicated = false; 270 this.firstError = false; 271 272 var regex = new Hash(this.options.regexp); 273 regex.each(function(el, key) { 274 this.regex.push(key); 275 }, this); 276 277 this.form.getElements("*[class*=validate]").each(function(el) { 278 this.register(el); 279 }, this); 280 281 this.form.addEvents({ 282 "submit": this.onSubmit.bind(this) 283 }); 284 285 if(this.options.display.fixPngForIe) this.fixIeStuffs(); 286 document.addEvent('mousewheel', function(){ 287 this.isScrolling = false; 288 }.bind(this)); 289 } 290 }, 291 292 /* 293 Function: register 294 Allows you to declare afterward new fields to the formcheck, to check dynamically loaded fields for example. 295 296 Example: 297 (code) 298 <script type="text/javascript"> 299 window.addEvent('domready', function() { 300 formcheck = new FormCheck('form_id'); 301 }); 302 303 // ...some code... 304 305 var newField = new Element('input', { 306 class : "validate['required']", 307 name : "new-field" 308 }).inject('form_id'); 309 formcheck.register(newField); 310 311 new Element('input', { 312 class : "validate['required']", 313 name : "another-field", 314 id : "another-field" 315 }).inject('form_id');
316 formcheck.register($('another-field')); 317 </script> 318 (end code) 319 320 See also: 321 <FormCheck::dispose> 322 */ 323 register : function(el) { 324 el.validation = []; 325 el.getProperty("class").split(' ').each(function(classX) { 326 if(classX.match(/^validate(\[.+\])$/)) { 327 var validators = eval(classX.match(/^validate(\[.+\])$/)[1]); 328 for(var i = 0; i < validators.length; i++) { 329 el.validation.push(validators[i]); 330 if (validators[i].match(/^confirm\[/)) { 331 var field = eval(validators[i].match(/^.+(\[.+\])$/)[1].replace(/([A-Z0-9\._-]+)/i, "'$1'")); 332 if (this.form[field].validation.contains('required')){ 333 el.validation.push('required'); 334 } 335 336 } 337 } 338 this.addListener(el); 339 } 340 }, this); 341 }, 342 343 /* 344 Function: dispose 345 Allows you to remove a declared field from formCheck 346 347 Example: 348 (code) 349 <script type="text/javascript"> 350 window.addEvent('domready', function() { 351 formcheck = new FormCheck('form_id'); 352 }); 353 354 // ...some code... 355 356 formcheck.dispose($('obsolete-field')); 357 </script> 358 (end code) 359 360 See also: 361 <FormCheck::register> 362 */ 363 dispose : function(element) { 364 this.validations.erase(element); 365 }, 366 367 /* 368 Function: addListener 369 Private method 370 371 Add listener on fields 372 */ 373 addListener : function(el) { 374 this.validations.push(el); 375 el.errors = []; 376 377 if (this.options.display.indicateErrorsInit) { 378 this.validations.each(function(el) { 379 if(!this.manageError(el,'submit')) this.form.isValid = false; 380 }, this); 381 return true; 382 } 383 384 if (el.validation[0] == 'submit') { 385 el.addEvent('click', function(e){ 386 this.onSubmit(e); 387 }.bind(this)); 388 return true; 389 } 390 391 if (this.isChildType(el) == false) el.addEvent('blur', function() { 392 (function(){ 393 if(!this.fxRunning && (el.element || this.options.display.showErrors == 1) && (this.options.display.checkValueIfEmpty || el.value)) 394 this.manageError(el, 'blur') 395 }.bind(this)).delay(100); 396 }.bind(this)) 397 //We manage errors on radio 398 else if (this.isChildType(el) == true) { 399 //We get all radio from the same group and add a blur option 400 var nlButtonGroup = this.form.getElements('input[name="'+ el.getProperty("name") +'"]'); 401 nlButtonGroup.each(function(radio){ 402 radio.addEvent('blur', function(){ 403 (function(){ 404 if((el.element || this.options.display.showErrors == 1) && (this.options.display.checkValueIfEmpty || el.value)) this.manageError(el, 'click'); 405 }.bind(this)).delay(100); 406 }.bind(this)) 407 },this); 408 } 409 }, 410 411 /* 412 Function: validate 413 Private method 414 415 Dispatch check to other methods 416 */ 417 validate : function(el) { 418 el.errors = []; 419 el.isOk = true; 420 421 //skip validation and trim if specified 422 if (el.getParent('div.formline') && el.getParent('div.formline').getStyle('display') == 'none') return true; 423 if (!this.options.validateDisabled && el.get('disabled')) return true; 424 if (this.options.trimValue && el.value) el.value = el.value.trim(); 425 426 el.validation.each(function(rule) { 427 if(this.isChildType(el)) { 428 if (this.validateGroup(el) == false) { 429 el.isOk = false; 430 } 431 } else { 432 var ruleArgs = []; 433 434 if(rule.match(/^.+\[/)) { 435 var ruleMethod = rule.split('[')[0]; 436 ruleArgs = eval(rule.match(/^.+(\[.+\])$/)[1].replace(/([A-Z0-9\._-]+)/i, "'$1'")); 437 } else var ruleMethod = rule; 438 //console.log(ruleMethod); 439 //console.log(ruleArgs); 440 if (this.regex.contains(ruleMethod) && el.get('tag') != "select") { 441 //console.log('----'); 442 if (this.validateRegex(el, ruleMethod, ruleArgs) == false) { 443 el.isOk = false; 444 } 445 } 446 447 if (ruleMethod == 'confirm') { 448 if (this.validateConfirm(el, ruleArgs) == false) { 449 el.isOk = false; 450 } 451 } 452 if (ruleMethod == 'differs') { 453 if (this.validateDiffers(el, ruleArgs) == false) { 454 el.isOk = false; 455 } 456 } 457 if (el.get('tag') == "select" || (el.type == "checkbox" && ruleMethod == 'required')) { 458 if (this.simpleValidate(el) == false) { 459 el.isOk = false; 460 } 461 } 462 if(rule.match(/%[A-Z0-9\._-]+$/i) || (el.isOk && rule.match(/~[A-Z0-9\._-]+$/i))) { 463 if(eval(rule.slice(1)+'(el)') == false) { 464 el.isOk = false; 465 } 466 } 467 } 468 }, this); 469 470 if (el.isOk) return true; 471 else return false; 472 }, 473 474 /* 475 Function: simpleValidate 476 Private method 477 478 Perform simple check for select fields and checkboxes 479 */ 480 simpleValidate : function(el) { 481 if (el.get('tag') == 'select' && el.selectedIndex <= 0) { 482 el.errors.push([this.options.alerts.select,1]); 483 return false; 484 } else if (el.type == "checkbox" && el.checked == false) { 485 el.errors.push([this.options.alerts.checkbox,1]); 486 return false; 487 } 488 return true; 489 }, 490 491 /* 492 Function: validateRegex 493 Private method 494 495 Perform regex validations 496 */ 497 validateRegex : function(el, ruleMethod, ruleArgs) { 498 var msg = ""; 499 if (ruleArgs[1] && ruleMethod == 'length') { 500 if (ruleArgs[1] == -1) { 501 this.options.regexp.length = new RegExp("^[\\s\\S]{"+ ruleArgs[0] +",}$"); 502 msg = this.options.alerts.lengthmin.replace("%0",ruleArgs[0]); 503 } else if(ruleArgs[0] == ruleArgs[1]) { 504 this.options.regexp.length = new RegExp("^[\\s\\S]{"+ ruleArgs[0] +"}$"); 505 msg = this.options.alerts.length_fix.replace("%0",ruleArgs[0]); 506 } else { 507 this.options.regexp.length = new RegExp("^[\\s\\S]{"+ ruleArgs[0] +","+ ruleArgs[1] +"}$"); 508 msg = this.options.alerts.length_str.replace("%0",ruleArgs[0]).replace("%1",ruleArgs[1]); 509 } 510 } else if (ruleArgs[0] && ruleMethod == 'length') { 511 this.options.regexp.length = new RegExp("^.{0,"+ ruleArgs[0] +"}$"); 512 msg = this.options.alerts.lengthmax.replace("%0",ruleArgs[0]); 513 } else { 514 msg = this.options.alerts[ruleMethod]; 515 } 516 if (ruleArgs[1] && ruleMethod == 'digit') { 517 var regres = true; 518 if (!this.options.regexp.digit.test(el.value)) { 519 el.errors.push([this.options.alerts[ruleMethod],1]); 520 regres = false; 521 } 522 if (ruleArgs[1] == -1) { 523 if (el.value >= ruleArgs[0]) var valueres = true; else var valueres = false; 524 msg = this.options.alerts.digitmin.replace("%0",ruleArgs[0]); 525 } else { 526 if (el.value >= ruleArgs[0] && el.value <= ruleArgs[1]) var valueres = true; else var valueres = false; 527 msg = this.options.alerts.digitltd.replace("%0",ruleArgs[0]).replace("%1",ruleArgs[1]); 528 } 529 if (regres == false || valueres == false) { 530 el.errors.push([msg,1]); 531 return false; 532 } 533 } else if (this.options.regexp[ruleMethod].test(el.value) == false) { 534 var priority = (ruleMethod == 'required') ? 2 : 1; 535 el.errors.push([msg,priority]); 536 return false; 537 } 538 return true; 539 }, 540 541 /* 542 Function: validateConfirm 543 Private method 544 545 Perform confirm validations 546 */ 547 validateConfirm: function(el,ruleArgs) { 548 549 var confirm = ruleArgs[0]; 550 if(el.value != this.form[confirm].value){ 551 if (this.options.display.titlesInsteadNames) 552 var msg = this.options.alerts.confirm.replace("%0",this.form[confirm].getProperty('title'));
553 else 554 var msg = this.options.alerts.confirm.replace("%0",confirm); 555 el.errors.push([msg,1]); 556 return false; 557 } 558 return true; 559 }, 560 561 /* 562 Function: validateDiffers 563 Private method 564 565 Perform differs validations 566 */ 567 validateDiffers: function(el,ruleArgs) { 568 var differs = ruleArgs[0]; 569 if(el.value == this.form[differs].value){ 570 if (this.options.display.titlesInsteadNames) 571 var msg = this.options.alerts.differs.replace("%0",this.form[differs].getProperty('title')); 572 else 573 var msg = this.options.alerts.differs.replace("%0",differs); 574 el.errors.push([msg,1]); 575 return false; 576 } 577 return true; 578 }, 579 580 /* 581 Function: isChildType 582 Private method 583 584 Determine if the field is a group of radio or not. 585 */ 586 isChildType: function(el) { 587 return ($defined(el.type) && el.type == 'radio') ? true : false; 588 }, 589 590 /* 591 Function: validateGroup 592 Private method 593 594 Perform radios validations 595 */ 596 validateGroup : function(el) { 597 el.errors = []; 598 var nlButtonGroup = this.form[el.getProperty("name")]; 599 el.group = nlButtonGroup; 600 var cbCheckeds = false; 601 602 for(var i = 0; i < nlButtonGroup.length; i++) { 603 if(nlButtonGroup[i].checked) { 604 cbCheckeds = true; 605 } 606 } 607 if(cbCheckeds == false) { 608 el.errors.push([this.options.alerts.radios,1]); 609 return false; 610 } else { 611 return true; 612 } 613 }, 614 615 /* 616 Function: listErrorsAtTop 617 Private method 618 619 Display errors 620 */ 621 listErrorsAtTop : function(obj) { 622 if(!this.form.element) { 623 this.form.element = new Element('div', {'id' : 'errorlist', 'class' : this.options.errorClass}).injectTop(this.form); 624 } 625 if ($type(obj) == 'collection') { 626 new Element('p').set('html',"<span>" + obj[0].name + " : </span>" + obj[0].errors[0]).injectInside(this.form.element); 627 } else { 628 if ((obj.validation.contains('required') && obj.errors.length > 0) || (obj.errors.length >
628 0 && obj.value && obj.validation.contains('required') == false)) { 629 obj.errors.each(function(error) { 630 new Element('p').set('html',"<span>" + obj.name + " : </span>" + error[0]).injectInside(this.form.element); 631 }, this); 632 } 633 } 634 }, 635 636 /* 637 Function: manageError 638 Private method 639 640 Manage display of errors boxes 641 */ 642 manageError : function(el, method) { 643 var isValid = this.validate(el); 644 if ((!isValid && el.validation.flatten()[0].contains('confirm[')) || (!isValid && el.validation.contains('required')) || (!el.validation.contains('required') && el.value && !isValid)) { 645 if(this.options.display.listErrorsAtTop == true && method == 'submit') 646 this.listErrorsAtTop(el, method); 647 if (this.options.display.indicateErrors == 2 ||this.alreadyIndicated == false || el.name == this.alreadyIndicated.name) 648 { 649 if(!this.firstError) this.firstError = el; 650 651 this.alreadyIndicated = el; 652 653 if (this.options.display.keepFocusOnError && el.name == this.firstError.name) (function(){el.focus()}).delay(20); 654 this.addError(el); 655 return false; 656 } 657 } else if ((isValid || (!el.validation.contains('required') && !el.value)) && el.element) { 658 this.removeError(el); 659 return true; 660 } 661 return true; 662 }, 663 664 /* 665 Function: addError 666 Private method 667 668 Add error message 669 */ 670 addError : function(obj) { 671 if(!obj.element && this.options.display.indicateErrors != 0) { 672 if (this.options.display.errorsLocation == 1) { 673 var pos = (this.options.display.tipsPosition == 'left') ? obj.getCoordinates().left : obj.getCoordinates().right; 674 pos += this.options.display.tipsOffsetX; 675 if ( this.isChildType(obj) && this.options.display.tipsPosition == 'right'){ 676 pos = obj.getParent('div.inputform').getLast('span').getCoordinates().right - 40; 677 } 678 if ( obj.hasClass('unikcb') ){ 679 pos -= this.options.display.tipsOffsetX; 680 pos -= 45; 681 } 682 var options = { 683 'opacity' : 0, 684 'position' : 'absolute', 685 'float' : 'left', 686 'left' : pos 687 } 688 obj.element = new Element('div', {'class' : this.options.tipsClass, 'styles' : options}).injectInside(document.body); 689 this.addPositionEvent(obj); 690 } else if (this.options.display.errorsLocation == 2){ 691 obj.element = new Element('div', {'class' : this.options.errorClass, 'styles' : {'opacity' : 0}}).injectBefore(obj); 692 } else if (this.options.display.errorsLocation == 3){ 693 obj.element = new Element('div', {'class' : this.options.errorClass, 'styles' : {'opacity' : 0}}); 694 if ($type(obj.group) == 'object' || $type(obj.group) == 'collection') 695 obj.element.injectAfter(obj.group[obj.group.length-1]); 696 else 697 obj.element.injectAfter(obj); 698 } 699 } 700 if (obj.element && obj.element != true) { 701 obj.element.empty(); 702 if (this.options.display.errorsLocation == 1) { 703 var errors = []; 704 var priority_max = -1; 705 obj.errors.each(function(error) { 706 if ( error[1] > priority_max) priority_max = error[1]; 707 }); 708 obj.errors.each(function(error) { 709 if ( error[1] == priority_max) 710 errors.push(new Element('p').set('html', error[0])); 711 }); 712 var alt_err = obj.get('rel'); 713 if ( alt_err && alt_err.length > 0 ) 714 errors = [new Element('p').set('html', alt_err)]; 715 // si obj a un attribut spécial d'erreur remplacer les erreurs par celle ci 716 var tips = this.makeTips(errors).injectInside(obj.element); 717 if(this.options.display.closeTipsButton) { 718 tips.getElements('a.close').addEvent('mouseup', function(){ 719 this.removeError(obj); 720 }.bind(this)); 721 } 722 var pos = obj.getCoordinates().top - tips.getCoordinates().height + this.options.display.tipsOffsetY; 723 if ( this.isChildType(obj) && this.options.display.tipsPosition == 'right'){ 724 //pos -= this.options.display.tipsOffsetY; 725 } 726 if ( obj.hasClass('unikcb') ){ 727 pos -= 5; 728 } 729 obj.element.setStyle('top', pos); 730 } else { 731 obj.errors.each(function(error) { 732 new Element('p').set('html',error[0]).injectInside(obj.element); 733 }); 734 } 735 736 if (!this.options.display.fadeDuration || Browser.Engine.trident && Browser.Engine.version == 5 && this.options.display.errorsLocation < 2) { 737 obj.element.setStyle('opacity', 1); 738 } else { 739 obj.fx = new Fx.Tween(obj.element, { 740 'duration' : this.options.display.fadeDuration, 741 'ignore' : true, 742 'onStart' : function(){ 743 this.fxRunning = true; 744 }.bind(this), 745 'onComplete' : function() { 746 this.fxRunning = false; 747 if (obj.element && obj.element.getStyle('opacity').toInt() == 0) { 748 obj.element.destroy(); 749 obj.element = false; 750 } 751 }.bind(this) 752 }) 753 if(obj.element.getStyle('opacity').toInt() != 1) obj.fx.start('opacity', 1); 754 } 755 } 756 if (this.options.display.addClassErrorToField){ 757 if ( obj.getParent('div.formline') ) 758 obj.getParent('div.formline').addClass(this.options.divErrorClass); 759 if ( !this.isChildType(obj) ){ 760 obj.addClass(this.options.fieldErrorClass); 761 obj.element = obj.element || true; 762 } 763 } 764 765 }, 766 767 /* 768 Function: addPositionEvent 769 770 Update tips position after a browser resize 771 */ 772 addPositionEvent : function(obj) { 773 if(this.options.display.replaceTipsEffect) { 774 obj.event = function(){ 775 new Fx.Morph(obj.element, { 776 'duration' : this.options.display.fadeDuration 777 }).start({ 778 'left':[obj.element.getStyle('left'), obj.getCoordinates().right + this.options.display.tipsOffsetX], 779 'top':[obj.element.getStyle('top'), obj.getCoordinates().top - obj.element.getCoordinates().height + this.options.display.tipsOffsetY] 780 }); 781 }.bind(this); 782 783 } else { 784 obj.event = function(){ 785 obj.element.setStyles({ 786 'left':obj.getCoordinates().right + this.options.display.tipsOffsetX, 787 'top':obj.getCoordinates().top - obj.element.getCoordinates().height + this.options.display.tipsOffsetY 788 }); 789 }.bind(this) 790 } 791 window.addEvent('resize', obj.event); 792 }, 793 794 /* 795 Function: removeError 796 Private method 797 798 Remove the error display 799 */ 800 removeError : function(obj) { 801 this.alreadyIndicated = false; 802 obj.errors = []; 803 obj.isOK = true; 804 window.removeEvent('resize', obj.event); 805 if (this.options.display.errorsLocation >= 2 && obj.element) { 806 new Fx.Tween(obj.element, { 807 'duration': this.options.display.fadeDuration 808 }).start('height', 0); 809 } 810 if (!this.options.display.fadeDuration || Browser.Engine.trident && Browser.Engine.version == 5 && this.options.display.errorsLocation == 1 && obj.element) { 811 this.fxRunning = true; 812 obj.element.destroy(); 813 obj.element = false; 814 (function(){this.fxRunning = false}.bind(this)).delay(200); 815 } else if (obj.element && obj.element != true) { 816 obj.fx.start('opacity', 0); 817 } 818 819 if (this.options.display.addClassErrorToField){ 820 if ( obj.getParent('div.formline') ) 821 obj.getParent('div.formline').removeClass(this.options.divErrorClass); 822 if ( !this.isChildType(obj) ) 823 obj.removeClass(this.options.fieldErrorClass); 824 } 825 }, 826 827 /* 828 Function: focusOnError 829 Private method 830 831 Create set the focus to the first field with an error if needed 832 */ 833 focusOnError : function (obj) { 834 if (this.options.display.scrollToFirst && !this.alreadyFocused && !this.isScrolling) { 835 if (!this.options.display.indicateErrors || !this.options.display.errorsLocation) { 836 var dest = obj.getCoordinates().top-30; 837 } else if (this.alreadyIndicated.element) { 838 switch (this.options.display.errorsLocation){ 839 case 1 : 840 var dest = obj.element.getCoordinates().top; 841 break; 842 case 2 : 843 var dest = obj.element.getCoordinates().top-30; 844 break; 845 case 3 : 846 var dest = obj.getCoordinates().top-30; 847 break; 848 } 849 this.isScrolling = true; 850 } 851 var winh = window.getSize().y; 852 //if (window.getScroll().y != dest) { 853 if (dest < window.getScroll().y || dest > (winh + window.getScroll().y)) { 854 new Fx.Scroll(window, { 855 onComplete : function() { 856 this.isScrolling = false; 857 obj.focus(); 858 }.bind(this) 859 }).start(0,dest); 860 } else { 861 this.isScrolling = false; 862 obj.focus(); 863 } 864 this.alreadyFocused = true; 865 } 866 }, 867 868 /* 869 Function: fixIeStuffs 870 Private method 871 872 Fix png for IE6 873 */ 874 fixIeStuffs : function () { 875 if (Browser.Engine.trident4) { 876 //We fix png stuffs 877 var rpng = new RegExp('url\\(([\.a-zA-Z0-9_/:-]+\.png)\\)'); 878 var search = new RegExp('(.+)formcheck\.css'); 879 for (var i = 0; i < document.styleSheets.length; i++){ 880 if (document.styleSheets[i].href.match(/formcheck\.css$/)) { 881 var root = document.styleSheets[i].href.replace(search, '$1'); 882 var count = document.styleSheets[i].rules.length; 883 for (var j = 0; j < count; j++){ 884 var cssstyle = document.styleSheets[i].rules[j].style; 885 var bgimage = root + cssstyle.backgroundImage.replace(rpng, '$1'); 886 if (bgimage && bgimage.match(/\.png/i)){ 887 var scale = (cssstyle.backgroundRepeat == 'no-repeat') ? 'crop' : 'scale'; 888 cssstyle.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, src=\'' + bgimage + '\', sizingMethod=\''+ scale +'\')'; 889 cssstyle.backgroundImage = "none"; 890 } 891 } 892 } 893 } 894 } 895 }, 896 897 /* 898 Function: makeTips 899 Private method 900 901 Create tips boxes 902 */ 903 makeTips : function(txt) { 904 var table = new Element('table'); 905 table.cellPadding ='0'; 906 table.cellSpacing ='0'; 907 table.border ='0'; 908 909 var tbody = new Element('tbody').injectInside(table); 910 var tr1 = new Element('tr').injectInside(tbody); 911 new Element('td', {'class' : 'tl'}).injectInside(tr1); 912 new Element('td', {'class' : 't'}).injectInside(tr1); 913 new Element('td', {'class' : 'tr'}).injectInside(tr1); 914 var tr2 = new Element('tr').injectInside(tbody); 915 new Element('td', {'class' : 'l'}).injectInside(tr2); 916 var cont = new Element('td', {'class' : 'c'}).injectInside(tr2); 917 var errors = new Element('div', {'class' : 'err'}).injectInside(cont); 918 txt.each(function(error) { 919 error.injectInside(errors); 920 });
921 if (this.options.display.closeTipsButton) new Element('a',{'class' : 'close'}).injectInside(cont); 922 new Element('td', {'class' : 'r'}).injectInside(tr2); 923 var tr3 = new Element('tr').injectInside(tbody); 924 new Element('td', {'class' : 'bl'}).injectInside(tr3); 925 new Element('td', {'class' : 'b'}).injectInside(tr3); 926 new Element('td', {'class' : 'br'}).injectInside(tr3); 927 return table; 928 }, 929 930 /* 931 Function: reinitialize 932 Private method 933 934 Reinitialize form before submit check 935 */ 936 reinitialize: function() { 937 this.validations.each(function(el) { 938 if (el.element) { 939 el.errors = []; 940 el.isOK = true; 941 if(this.options.display.flashTips == 1) { 942 el.element.destroy(); 943 el.element = false; 944 } 945 } 946 }, this); 947 if (this.form.element) this.form.element.empty(); 948 this.alreadyFocused = false; 949 this.firstError = false; 950 this.elementToRemove = this.alreadyIndicated; 951 this.alreadyIndicated = false; 952 this.form.isValid = true; 953 }, 954 955 /* 956 Function: submitByAjax 957 Private method 958 959 Send the form by ajax, and replace the form with response 960 */ 961 962 submitByAjax: function() { 963 var url = this.form.getProperty('action'); 964 this.fireEvent('ajaxRequest'); 965 new Request({ 966 url: url, 967 method: this.form.getProperty('method'), 968 data : this.form.toQueryString(), 969 evalScripts: this.options.ajaxEvalScripts, 970 onFailure: function(instance){ 971 this.fireEvent('ajaxFailure', instance); 972 }.bind(this), 973 onSuccess: function(result){ 974 this.fireEvent('ajaxSuccess', result); 975 if(this.options.ajaxResponseDiv) $(this.options.ajaxResponseDiv).set('html',result); 976 }.bind(this) 977 }).send(); 978 }, 979 980 /* 981 Function: onSubmit 982 Private method 983 984 Perform check on submit action 985 */ 986 onSubmit: function(event) { 987 this.reinitialize(); 988 989 this.validations.each(function(el) { 990 var validation = this.manageError(el,'submit'); 991 if(!validation) this.form.isValid = false; 992 }, this); 993 994 if (this.form.isValid) { 995 if (this.options.submitByAjax) { 996 new Event(event).stop(); 997 this.submitByAjax(); 998 } 999 1000 if (this.options.customSubmit) { 1001 new Event(event).stop(); 1002 //this.fireEvent('formIsValid'); 1003 this.options.customSubmit.call(); 1004 } 1005 } else { 1006 new Event(event).stop(); 1007 if (this.elementToRemove && this.elementToRemove != this.firstError && this.options.display.indicateErrors == 1) { 1008 this.removeError(this.elementToRemove); 1009 } 1010 this.focusOnError(this.firstError) 1011 } 1012 } 1013}); 1014 1015 1016var formcheck_forms = []; 1017window.addEvent('domready', function(){ 1018 $$('form.formcheck').each(function(d){ 1019 formcheck_forms[d.id] = new FormCheck(d, { 1020 display : { 1021 errorsLocation : 1, 1022 indicateErrors : 2, 1023 showErrors : 1, 1024 addClassErrorToField : 1 1025 } 1026 }); 1027 }); 1028 if ( $('nlform') && formcheck_forms['nlform'] ){ 1029 formcheck_forms['nlform'].options.customSubmit = function(){ 1030 $('nlsubmit').setStyle('cursor','wait'); 1031 $('nlform').animate({'opacity':0.3},500,function(){ 1032 new Element('input', {'type':'hidden','name':'nllang','value':SITE_LANG}).inject($('nlform')); 1033 new Request({url:SITE_ROOT+'ajax/?page=nlinscform', onComplete: function(txt, xml){ 1034 if ( txt.trim() == '1' ){ // success 1035 $('nlform').animate({'opacity':'0'}, 500, function(){ 1036 $('nlform').setStyle('display','none'); 1037 $('nlformsuccess').setStyle('opacity','0'); 1038 $('nlformsuccess').setStyle('display','block'); 1039 $('nlformsuccess').animate({'opacity':'1'},500); 1040 }); 1041 } 1042 }}).post($('nlform')); 1043 }); 1044 return false; 1045 }; 1046 } 1047});
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.