/** Global function to format text messages * @param string sKey - key of message * @param string aParam - params to substitute * @return string formatted message. */ function formatMes(sKey, aParam) { // aValMes - global array var sStr = aValMes[sKey]; for(var m = 0; m < aParam.length; m++) sStr = sStr.replace('%s'+m, aParam[m]); return sStr; }; /** Factory of FormElements * Instantiate proper FormElement object depending on Html object * @param object oForm html form */ function FormElementFactory(oForm) { this.aHtmlEl = oForm.elements; }; /** * @access public * @param string sName name of html element in form * @return object HtmlElement */ FormElementFactory.prototype.create = function(sName) { var oHtmlEl = this.aHtmlEl[sName]; if (!oHtmlEl) { alert('Error: Invalid html element! Name:'+sName); return null; } return this._make(oHtmlEl); }; FormElementFactory.prototype._make = function(oHtmlEl) { //alert('oHtmlEl = '+oHtmlEl); if (oHtmlEl[0]) //array { var oFormEl = new FormElement_Group(); for(var i=0; i parseFloat(oEl.getValue())); if (oEl.aChilds.length && this.aChilds.length) { var n = Math.min(this.aChilds.length, oEl.aChilds.length); for (var i=0; i oEl.aChilds.length); } if (!oEl.aChilds.length && this.aChilds.length) return this._isArrMoreStr(this.getValue(), oEl.getValue()); if (oEl.aChilds.length && !this.aChilds.length) return this._isArrMoreStr(oEl.getValue(), this.getValue()); alert('Unespected Error'); }; /** compare array and string * @access private * @return boolean is array more string */ FormElement.prototype._isArrMoreStr = function (aArr, sVal) { if (aArr.length > 1) return true; if (aArr.length == 1) return (parseFloat(aArr[0]) > parseFloat(sVal)); return false; }; FormElement.prototype.isEqual = function (oEl) { return (this.getValue().toString() == oEl.getValue().toString()); }; FormElement.prototype.getCount = function() { return 1; }; // return true or false FormElement.prototype.setFocus = function() { try { if (this.oHtml.parentNode.tagName.toLowerCase() == 'select') this.oHtml.parentNode.focus(); else this.oHtml.focus(); } catch (e) { //alert(e); return false; } return true; }; // @coauthor Nikolay Severikov FormElement.prototype.markAsValid = function() { if ('option' == this.oHtml.tagName.toLowerCase()) this.oHtml = this.oHtml.parentNode; var sClass = this.oHtml.className; if ('val_error' == sClass.substr(sClass.length-9, 9)) this.oHtml.className = sClass.substr(0, sClass.length-10); return true; }; // @coauthor Nikolay Severikov FormElement.prototype.markAsInvalid = function() { if ('option' == this.oHtml.tagName.toLowerCase()) this.oHtml = this.oHtml.parentNode; var sClass = this.oHtml.className; if ('val_error' != sClass.substr(sClass.length-9, 9)) this.oHtml.className = sClass + ' val_error'; if (this.oHtml.className == ' val_error') this.oHtml.className = 'val_error'; return true; }; //============================================================================// /** Derived class of group of elements * */ function FormElement_Group() { this.aChilds = []; this.oHtml = null; }; FormElement_Group.prototype = new FormElement(); FormElement_Group.prototype.add = function(oEl) { this.aChilds[this.aChilds.length] = oEl; return true; }; FormElement_Group.prototype.getValue = function() { var a=[]; for (var i = 0; i < this.aChilds.length; i++) if (this.aChilds[i].getCount()) a[a.length] = this.aChilds[i].getValue(); return a; }; FormElement_Group.prototype.getCount = function() { var n = 0; for (var i = 0; i < this.aChilds.length; i++) n += this.aChilds[i].getCount(); return n; }; // return true or false FormElement_Group.prototype.setFocus = function() { for (var i = 0; i < this.aChilds.length; i++) if(this.aChilds[i].setFocus()) return true; return false; }; FormElement_Group.prototype.markAsValid = function() { for (var i = 0; i < this.aChilds.length; i++) this.aChilds[i].markAsValid(); return true; }; FormElement_Group.prototype.markAsInvalid = function() { for (var i = 0; i < this.aChilds.length; i++) this.aChilds[i].markAsInvalid(); return true; }; //============================================================================// /** Derived class of single cbox/radio element * */ function FormElement_Cbox(oHtml) { this.oHtml = oHtml; this.aChilds = []; }; FormElement_Cbox.prototype = new FormElement(); FormElement_Cbox.prototype.getValue = function() { if (this.getCount()) return this.trim(this.oHtml.value); return ''; }; FormElement_Cbox.prototype.getCount = function() { return (this.oHtml.checked || this.oHtml.selected) ? 1 : 0; }; //============================================================= /** * Class used to validate html form against validation rules * @param object oForm - html form to validate */ function Validator(oForm) { this.aFields = {}; // hash 'field_name' => 'has error' this.aErrors = []; // array of errors (strings) this.oFactory = new FormElementFactory(oForm); }; /** * Checks is form valid * @access public * @param array aSchemes - array of validation schemas of FormElements * @param array aRules - array of validation rules bentween 2 FormElements * @param array aCallbacks - array of callback functions for advansed validation * @return boolean */ Validator.prototype.isValid = function(aSchemes, aRules, aCallbacks) { this.aErrors = []; //clear errors //check each field for (var i = 0; i < aSchemes.length; i++) { var oEl = this.oFactory.create(aSchemes[i].field); if (oEl.oHtml != null) { if (oEl.oHtml.disabled == true) continue; } var bInvalid = this._checkField(oEl, aSchemes[i]); //remember validation result this.aFields[aSchemes[i].field] = bInvalid; } // check each rule for (var i = 0; i < aRules.length; i++) { var oEl1 = this.oFactory.create(aRules[i][0]); var oEl2 = this.oFactory.create(aRules[i][1]); var bInvalid = this._checkRule(oEl1, oEl2, aRules[i][2]); if (bInvalid) { // add error messages if any this.aErrors[this.aErrors.length] = aRules[i][3]; //remember validation result for each member this.aFields[aRules[i][0]] = true; this.aFields[aRules[i][1]] = true; } } // process callback functions for (var i = 0; i < aCallbacks.length; i++) aCallbacks[i](this); return (this.aErrors.length == 0); }; /** * Outputs validation errors in
or make alert, * sets focus to first field with error, * marks fields with errors using CSS, * @access public * @param string sDiv id of
element for output * @return false */ Validator.prototype.outputErrors = function(sDiv) { //output errors var sOut = ''; var oDiv = document.getElementById(sDiv); if (oDiv) { for (var i = 0; i < this.aErrors.length; i++) sOut += ''+this.aErrors[i]+'
'; oDiv.innerHTML = sOut; } else alert(this.aErrors.join("\n")); // no div in document var bFocusIsSet = false; for (var sName = 0; sName < this.aFields.length; sName++) { var oEl = this.oFactory.create(sName); var bHasErr = this.aFields[sName]; if (bHasErr) { oEl.markAsInvalid(); if (!bFocusIsSet) bFocusIsSet = oEl.setFocus(); } else oEl.markAsValid(); } return false; }; /** * Checks is single HtmlElement valid, store error messages * @access private * @param object oEl * @param object oScheme * @return boolean has any erros occurs */ Validator.prototype._checkField = function(oEl, oScheme) { var mVal = oEl.getValue(); //optional param - skip any validation if field is empty if (oScheme.optional && !mVal.length) return false; var aErr = []; // check size if ('undefined' != typeof(oScheme.minsize) && oScheme.minsize > oEl.getCount()) aErr[aErr.length] = formatMes('minsize', [oScheme.title, oScheme.minsize, oEl.getCount()]); if ('undefined' != typeof(oScheme.maxsize) && oScheme.maxsize < oEl.getCount()) aErr[aErr.length] = formatMes('maxsize', [oScheme.title, oScheme.maxsize, oEl.getCount()]); // convert string to array if (oEl.getCount() < 2) { mVal = []; mVal[0] = oEl.getValue(); } // validation for (var i=0; i sVal) aErr[aErr.length] = formatMes('min', [oScheme.title, oScheme.min, sVal]); if ('undefined' != typeof(oScheme.max) && oScheme.max < sVal) aErr[aErr.length] = formatMes('max', [oScheme.title, oScheme.max, sVal]); if ('undefined' != typeof(oScheme.mineq) && oScheme.mineq >= sVal) aErr[aErr.length] = formatMes('mineq', [oScheme.title, oScheme.mineq, sVal]); if ('undefined' != typeof(oScheme.maxeq) && oScheme.maxeq <= sVal) aErr[aErr.length] = formatMes('maxeq', [oScheme.title, oScheme.maxeq, sVal]); if (oScheme.callback) { var bValid = oScheme.callback(sVal); if (!bValid) { aErr[aErr.length] = formatMes('pattern', [oScheme.title]); } } sVal = sVal.toString(); if (oScheme.minlen && oScheme.minlen > sVal.length) aErr[aErr.length] = formatMes('minlen', [oScheme.title, oScheme.minlen, sVal.length]); if (oScheme.maxlen && oScheme.maxlen < sVal.length) aErr[aErr.length] = formatMes('maxlen', [oScheme.title, oScheme.maxlen, sVal.length]); if (oScheme.pattern && sVal.search(oScheme.pattern) == -1) aErr[aErr.length] = formatMes('pattern', [oScheme.title]); if (aErr.length) // break if first field with error found break; } // store error messages for field var sMes = oScheme.message ? oScheme.message : ''; if (sMes && aErr.length) // if given custom error message store only it this.aErrors[this.aErrors.length] = sMes; else this.aErrors = this.aErrors.concat(aErr); return aErr.length; }; /** Checks are 2 HtmlElement elements valid in the same time * @access private * @param object oLeft element * @param object oRight element * @param string sOperation // ==, <, < ... * @return boolean has any errors occurs */ Validator.prototype._checkRule = function(oLeft, oRight, sOperation) { var aErr = []; var bHasErr = false; switch (sOperation) { case '==' : bHasErr = !oLeft.isEqual(oRight); break; case '<=' : bHasErr = ( oLeft.getCount() && oRight.getCount() && oLeft.isMore(oRight) ); break; case '<' : bHasErr = ( oLeft.getCount() && oRight.getCount() && (oLeft.isMore(oRight) || oLeft.isEqual(oRight)) ); break; case '>=' : bHasErr = ( oLeft.getCount() && oRight.getCount() && !oLeft.isMore(oRight) && !oLeft.isEqual(oRight) ); break; case '>' : bHasErr = ( oLeft.getCount() && oRight.getCount() && !oLeft.isMore(oRight) ); break; case '!=' : bHasErr = oLeft.isEqual(oRight); break; case 'req' : bHasErr = (oLeft.getValue() && !oRight.getValue()); break; case 'more' : bHasErr = (oLeft.getCount() < oRight.getCount()); break; case 'less' : bHasErr = (oLeft.getCount() > oRight.getCount()); break; default: alert('Validator: unknown rule. Operation='+sOperation); }//switch return bHasErr; }; /** * Checks HTML form * @param object oForm html form * @param array aSchemes * @param array aRules * @param array aCallbacks * @return string sDiv for error output */ function validator_isValid(oForm, aSchemes, aRules, aCallbacks, sDiv) { var oVal = new Validator(oForm); var bValid = oVal.isValid(aSchemes, aRules, aCallbacks); if (!bValid) { oVal.outputErrors(sDiv); } else { if ( document.getElementById('formhide1') != null ) { document.getElementById('formhide1').style.display='none'; document.getElementById('formhide2').style.display=''; } } return bValid; }; /** * Checks if ip is in valid range * @param string sIp * @return bool result */ function validator_isIp(sIp) { return (sIp != '0.0.0.0' && sIp != '255.255.255.255') }; // end of validator