/* * Really easy field validation with Prototype * http://tetlaw.id.au/view/blog/really-easy-field-validation-with-prototype * Andrew Tetlaw * Version 1.5.3 (2006-07-15) * * Copyright (c) 2006 Andrew Tetlaw * http://www.opensource.org/licenses/mit-license.php */ Validator = Class.create(); Validator.prototype = { initialize : function(className, error, test, options) { this.options = Object.extend({}, options || {}); this._test = test ? test : function(v,elm){ return true }; this.error = error ? error : 'Validation failed.'; this.className = className; }, test : function(v, elm) { return this._test(v,elm); } } var Validation = Class.create(); Validation.prototype = { initialize : function(form, options){ this.options = Object.extend({ onSubmit : true, stopOnFirst : false, immediate : false, focusOnError : true, useTitles : false, onFormValidate : function(result, form) {}, onElementValidate : function(result, elm) {} }, options || {}); this.form = $(form); if(this.options.onSubmit) Event.observe(this.form,'submit',this.onSubmit.bind(this),false); if(this.options.immediate) { var useTitles = this.options.useTitles; var callback = this.options.onElementValidate; Form.getElements(this.form).each(function(input) { // Thanks Mike! Event.observe(input, 'blur', function(ev) { Validation.validate(Event.element(ev),{useTitle : useTitles, onElementValidate : callback}); }); }); } }, onSubmit : function(ev){ if(!this.validate()) Event.stop(ev); }, validate : function() { var result = false; var useTitles = this.options.useTitles; var callback = this.options.onElementValidate; if(this.options.stopOnFirst) { result = Form.getElements(this.form).all(function(elm) { return Validation.validate(elm,{useTitle : useTitles, onElementValidate : callback}); }); } else { result = Form.getElements(this.form).collect(function(elm) { return Validation.validate(elm,{useTitle : useTitles, onElementValidate : callback}); }).all(); } if(!result && this.options.focusOnError) { Form.getElements(this.form).findAll(function(elm){return $(elm).hasClassName('validation-failed')}).first().focus() } this.options.onFormValidate(result, this.form); return result; }, reset : function() { Form.getElements(this.form).each(Validation.reset); } } Object.extend(Validation, { validate : function(elm, options){ options = Object.extend({ useTitle : false, onElementValidate : function(result, elm) {} }, options || {}); elm = $(elm); var cn = elm.classNames(); return result = cn.all(function(value) { var test = Validation.test(value,elm,options.useTitle); options.onElementValidate(test, elm); return test; }); }, test : function(name, elm, useTitle) { // alert('Running test:' + name + ' | ' + elm.id); var v = Validation.get(name); var prop = '__advice'+name.camelize(); if(Validation.isVisible(elm) && !v.test($F(elm), elm)) { var advice = Validation.getAdvice(name, elm); var errorMsg = useTitle ? ((elm && elm.title) ? elm.title : v.error) : v.error; if(elm.type.toLowerCase() == 'radio') advice_id = 'advice-' + elm.name; else advice_id = 'advice-' + elm.id; advice = $(advice_id) var fade_in = advice.innerHTML == ''; if(fade_in) advice.style.display = 'none'; advice.innerHTML = errorMsg; // if(typeof Effect == 'undefined') { advice.style.display = 'block';} // else {new Effect.Appear(advice, {duration : 0.5 }); } if(fade_in) new Effect.Appear(advice, {duration : 0.5 }); elm[prop] = true; elm.removeClassName('validation-passed'); elm.addClassName('validation-failed'); return false; } else { // alert('Passed ' + name + ' | ' + elm.type); var advice = Validation.getAdvice(name, elm); //if(typeof advice != 'undefined') advice.hide(); /*if(typeof advice != 'undefined') { */ //alert('Passed ' + name + ' | ' + elm.type); if(elm.type.toLowerCase() == 'radio') advice_id = 'advice-' + elm.name; else advice_id = 'advice-' + elm.id; advice = $(advice_id) if(advice) advice.innerHTML = ''; //} elm[prop] = ''; elm.removeClassName('validation-failed'); elm.addClassName('validation-passed'); return true; } }, isVisible : function(elm) { while(elm.tagName != 'BODY') { if(!$(elm).visible()) return false; elm = elm.parentNode; } return true; }, getAdvice : function(name, elm) { return Try.these( function(){ return $('advice-' + Validation.getElmID(elm)) } ); }, getElmID : function(elm) { return elm.id ? elm.id : elm.name; }, reset : function(elm) { elm = $(elm); var cn = elm.classNames(); cn.each(function(value) { var prop = '__advice'+value.camelize(); if(elm[prop]) { var advice = Validation.getAdvice(value, elm); advice.hide(); elm[prop] = ''; } elm.removeClassName('validation-failed'); elm.removeClassName('validation-passed'); }); }, add : function(className, error, test, options) { var nv = {}; nv[className] = new Validator(className, error, test, options); Object.extend(Validation.methods, nv); }, addAllThese : function(validators) { var nv = {}; $A(validators).each(function(value) { nv[value[0]] = new Validator(value[0], value[1], value[2], (value.length > 3 ? value[3] : {})); }); Object.extend(Validation.methods, nv); }, get : function(name) { return Validation.methods[name] ? Validation.methods[name] : new Validator(); }, methods : {} }); Validation.add('IsEmpty', '', function(v) { return ((v == null) || (v.length == 0)); // || /^\s+$/.test(v)); }); Validation.addAllThese([ ['required', "Required", function(v) { return !Validation.get('IsEmpty').test(v); }], ['validate-number', 'Please enter a valid number in this field.', function(v) { return Validation.get('IsEmpty').test(v) || (!isNaN(v) && !/^\s+$/.test(v)); }], ['validate-phone', "Invalid phone number", function(v) { return Validation.get('IsEmpty').test(v) || /^[0-9\s\-+]+$/.test(v); }], ['validate-digits', 'Please use numbers only in this field. please avoid spaces or other characters such as dots or commas.', function(v) { return Validation.get('IsEmpty').test(v) || !/[^\d]/.test(v); }], ['validate-alpha', 'Please use letters only (a-z) in this field.', function (v) { return Validation.get('IsEmpty').test(v) || /^[a-zA-Z]+$/.test(v) }], ['validate-alphanum', 'Please use only letters (a-z) or numbers (0-9) only in this field. No spaces or other characters are allowed.', function(v) { return Validation.get('IsEmpty').test(v) || !/\W/.test(v) }], ['validate-date', 'Please enter a valid date.', function(v) { var test = new Date(v); return Validation.get('IsEmpty').test(v) || !isNaN(test); }], ['validate-email', "Invalid e-mail address", function (v) { return Validation.get('IsEmpty').test(v) || /\w{1,}[@][\w\-]{1,}([.]([\w\-]{1,})){1,3}$/.test(v) }], ['validate-select', "Required", function(v){ return (v != ' '); }], ['validate-url', 'Please enter a valid URL.', function (v) { return Validation.get('IsEmpty').test(v) || /^(http|https|ftp):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(:(\d+))?\/?/i.test(v) }], ['validate-date-au', 'Please use this date format: dd/mm/yyyy. For example 17/03/2006 for the 17th of March, 2006.', function(v) { if(Validation.get('IsEmpty').test(v)) return true; var regex = /^(\d{2})\/(\d{2})\/(\d{4})$/; if(!regex.test(v)) return false; var d = new Date(v.replace(regex, '$2/$1/$3')); return ( parseInt(RegExp.$2, 10) == (1+d.getMonth()) ) && (parseInt(RegExp.$1, 10) == d.getDate()) && (parseInt(RegExp.$3, 10) == d.getFullYear() ); }], ['validate-currency-dollar', 'Please enter a valid $ amount. For example $100.00 .', function(v) { // [$]1[##][,###]+[.##] // [$]1###+[.##] // [$]0.## // [$].## return Validation.get('IsEmpty').test(v) || /^\$?\-?([1-9]{1}[0-9]{0,2}(\,[0-9]{3})*(\.[0-9]{0,2})?|[1-9]{1}\d*(\.[0-9]{0,2})?|0(\.[0-9]{0,2})?|(\.[0-9]{1,2})?)$/.test(v) }], ['validate-one-required', "Required", function (v,elm) { var p = elm.parentNode; var options = p.getElementsByTagName('INPUT'); return $A(options).any(function(elm) { return $F(elm); }); }] ]);