From 43a82b487eae2fc761d65c458e67d931f422c241 Mon Sep 17 00:00:00 2001 From: Roland Gruber Date: Sun, 16 Oct 2011 12:06:00 +0000 Subject: [PATCH] client-side validation --- lam/copyright | 12 +- lam/lib/html.inc | 58 +- lam/lib/modules.inc | 7 +- lam/lib/modules/posixAccount.inc | 5 +- lam/lib/modules/shadowAccount.inc | 4 + lam/style/150_jquery-validationEngine.css | 165 ++ .../lib/140_jquery-validationEngine-2.2.1.js | 1429 +++++++++++++++++ .../lib/141_jquery-validationEngine-lang.php | 44 + lam/templates/main_header.php | 4 +- 9 files changed, 1703 insertions(+), 25 deletions(-) create mode 100644 lam/style/150_jquery-validationEngine.css create mode 100644 lam/templates/lib/140_jquery-validationEngine-2.2.1.js create mode 100644 lam/templates/lib/141_jquery-validationEngine-lang.php diff --git a/lam/copyright b/lam/copyright index 6f157989..02be7f2b 100644 --- a/lam/copyright +++ b/lam/copyright @@ -139,8 +139,10 @@ D: Programs and licenses with other licenses and/or authors than the main license and authors: -lib/fpdf.php A 2008 Olivier Plathey -lib/font/Vera* B 2003 Bitstream, Inc. -templates/lib/wz_tooltip.js C Walter Zorn -templates/lib/jquery*.js D 2010 John Resig, Paul Bakaus, Fred Heusschen -lib/3rdParty/phpseclib C Jim Wigginton +lib/fpdf.php A 2008 Olivier Plathey +lib/font/Vera* B 2003 Bitstream, Inc. +templates/lib/*wz_tooltip.js C Walter Zorn +lib/3rdParty/phpseclib C Jim Wigginton +templates/lib/*jquery*.js D 2010 John Resig, Paul Bakaus, Fred Heusschen +templates/lib/*jquery-validationEngine-*.js D 2010 Cedric Dugas and Olivier Refalo + diff --git a/lam/lib/html.inc b/lam/lib/html.inc index f5489e12..dd49f793 100644 --- a/lam/lib/html.inc +++ b/lam/lib/html.inc @@ -36,14 +36,20 @@ $Id$ */ abstract class htmlElement { - const OPTION_ALIGN = 0; - + /** align to top */ const ALIGN_TOP = 0; + /** align to left */ const ALIGN_LEFT = 1; + /** align to right */ const ALIGN_RIGHT = 2; + /** align to bottom */ const ALIGN_BOTTOM = 3; + /** align to center */ const ALIGN_CENTER = 4; + /** validation rule to allow only numbers ([0-9]+) */ + const VALIDATE_NUMERIC = 'numeric'; + /** alignment when inside a table */ public $alignment = null; /** colspan if inside a table */ @@ -351,6 +357,10 @@ class htmlInputField extends htmlElement { private $isEnabled = true; /** indicates that the value should be saved in obfuscated form */ private $obfuscate = false; + /** required field */ + protected $required = false; + /** validation rule */ + private $validationRule = null; /** * Constructor @@ -389,8 +399,20 @@ class htmlInputField extends htmlElement { $this->fieldValue = $values[$this->fieldName][0]; } } + $validators = array(); + if ($this->required) { + $validators[] = 'required'; + } + if ($this->validationRule != null) { + $validators[] = 'custom[' . $this->validationRule . ']'; + } // print input field + $class = ''; + if (sizeof($validators) > 0) { + $class = ' class="validate[' . implode(',', $validators) . ']"'; + } $name = ' name="' . $this->fieldName . '"'; + $id = ' id="inputField_' . $this->fieldName . '"'; $value = ''; if ($this->fieldValue != null) { $value = ' value="' . $this->fieldValue . '"'; @@ -410,7 +432,7 @@ class htmlInputField extends htmlElement { if (!$this->isEnabled) { $disabled = ' disabled'; } - echo ''; + echo ''; if ($this->obfuscate) { return array($this->fieldName => 'text_obfuscated'); } @@ -464,6 +486,25 @@ class htmlInputField extends htmlElement { $this->obfuscate = $obfuscate; } + /** + * Specifies if the input field is required. + * + * @param boolean $required required + */ + public function setRequired($required) { + $this->required = $required; + } + + /** + * Specifies the validation rule (e.g. htmlElement::VALIDATE_NUMERIC) for this field. + * This rule is checked on client side when the input field looses focus. + * + * @param boolean $rule rule name + */ + public function setValidationRule($rule) { + $this->validationRule = $rule; + } + } /** @@ -477,8 +518,6 @@ class htmlTableExtendedInputField extends htmlInputField { private $label; /** help ID */ private $helpID; - /** required field */ - private $required = false; /** * Constructor @@ -527,15 +566,6 @@ class htmlTableExtendedInputField extends htmlInputField { return $return; } - /** - * Specifies if this input field must be filled. - * - * @param boolean $required required or not - */ - public function setRequired($required) { - $this->required = $required; - } - } /** diff --git a/lam/lib/modules.inc b/lam/lib/modules.inc index 3b1cb612..412a9138 100644 --- a/lam/lib/modules.inc +++ b/lam/lib/modules.inc @@ -1159,7 +1159,12 @@ class accountContainer { */ private function printPageHeader() { include '../main_header.php'; - echo "
\n"; + echo ''; + echo "\n"; } /** diff --git a/lam/lib/modules/posixAccount.inc b/lam/lib/modules/posixAccount.inc index b21e4036..05aae6c3 100644 --- a/lam/lib/modules/posixAccount.inc +++ b/lam/lib/modules/posixAccount.inc @@ -1086,13 +1086,10 @@ class posixAccount extends baseModule implements passwordService { $return->addElement($uidInput, true); $commonName = ''; if (isset($this->attributes['cn'][0])) $commonName = $this->attributes['cn'][0]; - $cnInput = new htmlTableExtendedInputField(_("Common name"), 'cn', $commonName, 'cn'); - $cnInput->setRequired(true); - $return->addElement($cnInput, true); + $return->addElement(new htmlTableExtendedInputField(_("Common name"), 'cn', $commonName, 'cn'), true); $uidNumber = ''; if (isset($this->attributes['uidNumber'][0])) $uidNumber = $this->attributes['uidNumber'][0]; $uidNumberInput = new htmlTableExtendedInputField(_('UID number'), 'uidNumber', $uidNumber, 'uidNumber'); - $uidNumberInput->setRequired(true); $uidNumberInput->setFieldMaxLength(20); $return->addElement($uidNumberInput, true); $gecos = ''; diff --git a/lam/lib/modules/shadowAccount.inc b/lam/lib/modules/shadowAccount.inc index 00340338..be04f5ab 100644 --- a/lam/lib/modules/shadowAccount.inc +++ b/lam/lib/modules/shadowAccount.inc @@ -305,6 +305,7 @@ class shadowAccount extends baseModule implements passwordService { $pwdWarnInput = new htmlTableExtendedInputField(_('Password warning'), 'shadowWarning', $shWarning, 'shadowWarning'); $pwdWarnInput->setFieldMaxLength(4); $pwdWarnInput->setFieldSize(5); + $pwdWarnInput->setValidationRule(htmlElement::VALIDATE_NUMERIC); $return->addElement($pwdWarnInput, true); $shPwdExpiration = ''; @@ -312,6 +313,7 @@ class shadowAccount extends baseModule implements passwordService { $pwdExpInput = new htmlTableExtendedInputField(_('Password expiration'), 'shadowInactive', $shPwdExpiration, 'shadowInactive'); $pwdExpInput->setFieldMaxLength(4); $pwdExpInput->setFieldSize(5); + $pwdExpInput->setValidationRule(htmlElement::VALIDATE_NUMERIC); $return->addElement($pwdExpInput, true); $shMinAge = ''; @@ -319,6 +321,7 @@ class shadowAccount extends baseModule implements passwordService { $minAgeInput = new htmlTableExtendedInputField(_('Minimum password age'), 'shadowMin', $shMinAge, 'shadowMin'); $minAgeInput->setFieldMaxLength(5); $minAgeInput->setFieldSize(5); + $minAgeInput->setValidationRule(htmlElement::VALIDATE_NUMERIC); $return->addElement($minAgeInput, true); $shMaxAge = ''; @@ -326,6 +329,7 @@ class shadowAccount extends baseModule implements passwordService { $maxAgeInput = new htmlTableExtendedInputField(_('Maximum password age'), 'shadowMax', $shMaxAge, 'shadowMax'); $maxAgeInput->setFieldMaxLength(5); $maxAgeInput->setFieldSize(5); + $maxAgeInput->setValidationRule(htmlElement::VALIDATE_NUMERIC); $return->addElement($maxAgeInput, true); $expirationDate = "     -      "; diff --git a/lam/style/150_jquery-validationEngine.css b/lam/style/150_jquery-validationEngine.css new file mode 100644 index 00000000..c2a94f5a --- /dev/null +++ b/lam/style/150_jquery-validationEngine.css @@ -0,0 +1,165 @@ +/* +$Id$ + + This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/) + Copyright (C) 2010 Cedric Dugas and Olivier Refalo + 2011 Roland Gruber + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + + +.inputContainer { + position: relative; + float: left; +} + +.formError { + position: absolute; + top: 300px; + left: 300px; + display: block; + z-index: 5000; + cursor: pointer; +} + +.ajaxSubmit { + padding: 20px; + background: #55ea55; + border: 1px solid #999; + display: none +} + +.formError .formErrorContent { + width: 100%; + background: #ee4f01; + position:relative; + z-index:5001; + color: #fff; + font-family: tahoma; + font-size: 12px; + border: 2px solid #ddd; + box-shadow: 0 0 6px #000; + -moz-box-shadow: 0 0 6px #000; + -webkit-box-shadow: 0 0 6px #000; + padding: 4px 10px 4px 10px; + border-radius: 6px; + -moz-border-radius: 6px; + -webkit-border-radius: 6px; +} + +.greenPopup .formErrorContent { + background: #33be40; +} + +.blackPopup .formErrorContent { + background: #393939; + color: #FFF; +} + +.formError .formErrorArrow { + width: 15px; + margin: -2px 0 0 13px; + position:relative; + z-index: 5006; +} + +.formError .formErrorArrowBottom { + box-shadow: none; + -moz-box-shadow: none; + -webkit-box-shadow: none; + margin: 0px 0 0 12px; + top:2px; +} + +.formError .formErrorArrow div { + border-left: 2px solid #ddd; + border-right: 2px solid #ddd; + box-shadow: 0 2px 3px #444; + -moz-box-shadow: 0 2px 3px #444; + -webkit-box-shadow: 0 2px 3px #444; + font-size: 0px; + height: 1px; + background: #ee4f01; + margin: 0 auto; + line-height: 0; + font-size: 0; + display: block; +} + +.formError .formErrorArrowBottom div { + box-shadow: none; + -moz-box-shadow: none; + -webkit-box-shadow: none; +} + +.greenPopup .formErrorArrow div { + background: #33be40; +} + +.blackPopup .formErrorArrow div { + background: #393939; + color: #FFF; +} + +.formError .formErrorArrow .line10 { + width: 15px; + border: none; +} + +.formError .formErrorArrow .line9 { + width: 13px; + border: none; +} + +.formError .formErrorArrow .line8 { + width: 11px; +} + +.formError .formErrorArrow .line7 { + width: 9px; +} + +.formError .formErrorArrow .line6 { + width: 7px; +} + +.formError .formErrorArrow .line5 { + width: 5px; +} + +.formError .formErrorArrow .line4 { + width: 3px; +} + +.formError .formErrorArrow .line3 { + width: 1px; + border-left: 2px solid #ddd; + border-right: 2px solid #ddd; + border-bottom: 0 solid #ddd; +} + +.formError .formErrorArrow .line2 { + width: 3px; + border: none; + background: #ddd; +} + +.formError .formErrorArrow .line1 { + width: 1px; + border: none; + background: #ddd; +} diff --git a/lam/templates/lib/140_jquery-validationEngine-2.2.1.js b/lam/templates/lib/140_jquery-validationEngine-2.2.1.js new file mode 100644 index 00000000..352a0171 --- /dev/null +++ b/lam/templates/lib/140_jquery-validationEngine-2.2.1.js @@ -0,0 +1,1429 @@ +/* + * Inline Form Validation Engine 2.2, jQuery plugin + * + * Copyright(c) 2010, Cedric Dugas + * http://www.position-absolute.com + * + * 2.0 Rewrite by Olivier Refalo + * http://www.crionics.com + * + * Form validation engine allowing custom regex rules to be added. + * Licensed under the MIT License + */ +(function($) { + + var methods = { + + /** + * Kind of the constructor, called before any action + * @param {Map} user options + */ + init: function(options) { + var form = this; + if (!form.data('jqv') || form.data('jqv') == null ) { + methods._saveOptions(form, options); + + // bind all formError elements to close on click + $(".formError").live("click", function() { + $(this).fadeOut(150, function() { + + // remove prompt once invisible + $(this).remove(); + }); + }); + } + }, + /** + * Attachs jQuery.validationEngine to form.submit and field.blur events + * Takes an optional params: a list of options + * ie. jQuery("#formID1").validationEngine('attach', {promptPosition : "centerRight"}); + */ + attach: function(userOptions) { + + var form = this; + var options; + + if(userOptions) + options = methods._saveOptions(form, userOptions); + else + options = form.data('jqv'); + + var validateAttribute = (form.find("[data-validation-engine*=validate]")) ? "data-validation-engine" : "class"; + + if (!options.binded) { + if (options.bindMethod == "bind"){ + + // bind fields + form.find("[class*=validate]").not("[type=checkbox]").not("[type=radio]").not(".datepicker").bind(options.validationEventTrigger, methods._onFieldEvent); + form.find("[class*=validate][type=checkbox],[class*=validate][type=radio]").bind("click", methods._onFieldEvent); + + form.find("[class*=validate][class*=datepicker]").bind(options.validationEventTrigger,{"delay": 300}, methods._onFieldEvent); + + // bind form.submit + form.bind("submit", methods._onSubmitEvent); + } else if (options.bindMethod == "live") { + // bind fields with LIVE (for persistant state) + form.find("[class*=validate]").not("[type=checkbox]").not(".datepicker").live(options.validationEventTrigger, methods._onFieldEvent); + form.find("[class*=validate][type=checkbox]").live("click", methods._onFieldEvent); + + form.find("[class*=validate][class*=datepicker]").live(options.validationEventTrigger,{"delay": 300}, methods._onFieldEvent); + + // bind form.submit + form.live("submit", methods._onSubmitEvent); + } + + options.binded = true; + } + return this; + }, + /** + * Unregisters any bindings that may point to jQuery.validaitonEngine + */ + detach: function() { + var form = this; + var options = form.data('jqv'); + if (options.binded) { + + // unbind fields + form.find("[class*=validate]").not("[type=checkbox]").unbind(options.validationEventTrigger, methods._onFieldEvent); + form.find("[class*=validate][type=checkbox],[class*=validate][type=radio]").unbind("click", methods._onFieldEvent); + + // unbind form.submit + form.unbind("submit", methods.onAjaxFormComplete); + + + // unbind live fields (kill) + form.find("[class*=validate]").not("[type=checkbox]").die(options.validationEventTrigger, methods._onFieldEvent); + form.find("[class*=validate][type=checkbox]").die("click", methods._onFieldEvent); + // unbind form.submit + + + + + form.die("submit", methods.onAjaxFormComplete); + + form.removeData('jqv'); + } + }, + /** + * Validates the form fields, shows prompts accordingly. + * Note: There is no ajax form validation with this method, only field ajax validation are evaluated + * + * @return true if the form validates, false if it fails + */ + validate: function() { + return methods._validateFields(this); + }, + /** + * Validates one field, shows prompt accordingly. + * Note: There is no ajax form validation with this method, only field ajax validation are evaluated + * + * @return true if the form validates, false if it fails + */ + validateField: function(el) { + var options = $(this).data('jqv'); + return methods._validateField($(el), options); + }, + /** + * Validates the form fields, shows prompts accordingly. + * Note: this methods performs fields and form ajax validations(if setup) + * + * @return true if the form validates, false if it fails, undefined if ajax is used for form validation + */ + validateform: function() { + return methods._onSubmitEvent.call(this); + }, + /** + * Redraw prompts position, useful when you change the DOM state when validating + */ + updatePromptsPosition: function() { + var form = this.closest('form'); + var options = form.data('jqv'); + // No option, take default one + form.find('[class*=validate]').not(':hidden').not(":disabled").each(function(){ + var field = $(this); + + var prompt = methods._getPrompt(field); + var promptText = $(prompt).find(".formErrorContent").html(); + + if(prompt) methods._updatePrompt(field, $(prompt), promptText, undefined, false, options); + }) + }, + /** + * Displays a prompt on a element. + * Note that the element needs an id! + * + * @param {String} promptText html text to display type + * @param {String} type the type of bubble: 'pass' (green), 'load' (black) anything else (red) + * @param {String} possible values topLeft, topRight, bottomLeft, centerRight, bottomRight + */ + showPrompt: function(promptText, type, promptPosition, showArrow) { + + var form = this.closest('form'); + var options = form.data('jqv'); + // No option, take default one + if(!options) options = methods._saveOptions(this, options); + if(promptPosition) + options.promptPosition=promptPosition; + options.showArrow = showArrow==true; + + methods._showPrompt(this, promptText, type, false, options); + }, + /** + * Closes all error prompts on the page + */ + hidePrompt: function() { + var promptClass = "."+ methods._getClassName($(this).attr("id")) + "formError"; + $(promptClass).fadeTo("fast", 0.3, function() { + $(this).remove(); + }); + }, + /** + * Closes form error prompts, CAN be invidual + */ + hide: function() { + var closingtag; + if($(this).is("form")){ + closingtag = "parentForm"+$(this).attr('id'); + }else{ + closingtag = $(this).attr('id') +"formError"; + } + $('.'+closingtag).fadeTo("fast", 0.3, function() { + $(this).remove(); + }); + }, + /** + * Closes all error prompts on the page + */ + hideAll: function() { + $('.formError').fadeTo("fast", 0.3, function() { + $(this).remove(); + }); + }, + /** + * Typically called when user exists a field using tab or a mouse click, triggers a field + * validation + */ + _onFieldEvent: function(event) { + var field = $(this); + var form = field.closest('form'); + var options = form.data('jqv'); + // validate the current field + window.setTimeout(function() { + methods._validateField(field, options); + }, (event.data) ? event.data.delay : 0); + + }, + /** + * Called when the form is submited, shows prompts accordingly + * + * @param {jqObject} + * form + * @return false if form submission needs to be cancelled + */ + _onSubmitEvent: function() { + var form = $(this); + var options = form.data('jqv'); + + // validate each field (- skip field ajax validation, no necessary since we will perform an ajax form validation) + var r=methods._validateFields(form, true); + + if (r && options.ajaxFormValidation) { + methods._validateFormWithAjax(form, options); + return false; + } + + if(options.onValidationComplete) { + options.onValidationComplete(form, r); + return false; + } + return r; + }, + + /** + * Return true if the ajax field validations passed so far + * @param {Object} options + * @return true, is all ajax validation passed so far (remember ajax is async) + */ + _checkAjaxStatus: function(options) { + var status = true; + $.each(options.ajaxValidCache, function(key, value) { + if (!value) { + status = false; + // break the each + return false; + } + }); + return status; + }, + /** + * Validates form fields, shows prompts accordingly + * + * @param {jqObject} + * form + * @param {skipAjaxFieldValidation} + * boolean - when set to true, ajax field validation is skipped, typically used when the submit button is clicked + * + * @return true if form is valid, false if not, undefined if ajax form validation is done + */ + _validateFields: function(form, skipAjaxValidation) { + var options = form.data('jqv'); + + // this variable is set to true if an error is found + var errorFound = false; + + // Trigger hook, start validation + form.trigger("jqv.form.validating"); + // first, evaluate status of non ajax fields + form.find('[class*=validate]').not(':hidden').not(":disabled").each( function() { + var field = $(this); + errorFound |= methods._validateField(field, options, skipAjaxValidation); + }); + // second, check to see if all ajax calls completed ok + // errorFound |= !methods._checkAjaxStatus(options); + + // thrird, check status and scroll the container accordingly + form.trigger("jqv.form.result", [errorFound]); + + if (errorFound) { + + if (options.scroll) { + + // get the position of the first error, there should be at least one, no need to check this + //var destination = form.find(".formError:not('.greenPopup'):first").offset().top; + + // look for the visually top prompt + var destination = Number.MAX_VALUE; + var fixleft = 0; + var lst = $(".formError:not('.greenPopup')"); + + for (var i = 0; i < lst.length; i++) { + var d = $(lst[i]).offset().top; + if (d < destination){ + destination = d; + fixleft = $(lst[i]).offset().left; + } + } + + if (!options.isOverflown) + $("html:not(:animated),body:not(:animated)").animate({ + scrollTop: destination, + scrollLeft: fixleft + }, 1100); + else { + var overflowDIV = $(options.overflownDIV); + var scrollContainerScroll = overflowDIV.scrollTop(); + var scrollContainerPos = -parseInt(overflowDIV.offset().top); + + destination += scrollContainerScroll + scrollContainerPos - 5; + var scrollContainer = $(options.overflownDIV + ":not(:animated)"); + + scrollContainer.animate({ + scrollTop: destination + }, 1100); + + $("html:not(:animated),body:not(:animated)").animate({ + scrollTop: overflowDIV.offset().top, + scrollLeft: fixleft + }, 1100); + } + } + return false; + } + return true; + }, + /** + * This method is called to perform an ajax form validation. + * During this process all the (field, value) pairs are sent to the server which returns a list of invalid fields or true + * + * @param {jqObject} form + * @param {Map} options + */ + _validateFormWithAjax: function(form, options) { + + var data = form.serialize(); + var url = (options.ajaxFormValidationURL) ? options.ajaxFormValidationURL : form.attr("action"); + $.ajax({ + type: "GET", + url: url, + cache: false, + dataType: "json", + data: data, + form: form, + methods: methods, + options: options, + beforeSend: function() { + return options.onBeforeAjaxFormValidation(form, options); + }, + error: function(data, transport) { + methods._ajaxError(data, transport); + }, + success: function(json) { + + if (json !== true) { + + // getting to this case doesn't necessary means that the form is invalid + // the server may return green or closing prompt actions + // this flag helps figuring it out + var errorInForm=false; + for (var i = 0; i < json.length; i++) { + var value = json[i]; + + var errorFieldId = value[0]; + var errorField = $($("#" + errorFieldId)[0]); + + // make sure we found the element + if (errorField.length == 1) { + + // promptText or selector + var msg = value[2]; + // if the field is valid + if (value[1] == true) { + + if (msg == "" || !msg){ + // if for some reason, status==true and error="", just close the prompt + methods._closePrompt(errorField); + } else { + // the field is valid, but we are displaying a green prompt + if (options.allrules[msg]) { + var txt = options.allrules[msg].alertTextOk; + if (txt) + msg = txt; + } + methods._showPrompt(errorField, msg, "pass", false, options, true); + } + + } else { + // the field is invalid, show the red error prompt + errorInForm|=true; + if (options.allrules[msg]) { + var txt = options.allrules[msg].alertText; + if (txt) + msg = txt; + } + methods._showPrompt(errorField, msg, "", false, options, true); + } + } + } + options.onAjaxFormComplete(!errorInForm, form, json, options); + } else + options.onAjaxFormComplete(true, form, "", options); + } + }); + + }, + /** + * Validates field, shows prompts accordingly + * + * @param {jqObject} + * field + * @param {Array[String]} + * field's validation rules + * @param {Map} + * user options + * @return true if field is valid + */ + _validateField: function(field, options, skipAjaxValidation) { + if (!field.attr("id")) + $.error("jQueryValidate: an ID attribute is required for this field: " + field.attr("name") + " class:" + + field.attr("class")); + + var rulesParsing = field.attr('class'); + var getRules = /validate\[(.*)\]/.exec(rulesParsing); + if (!getRules) + return false; + var str = getRules[1]; + var rules = str.split(/\[|,|\]/); + + // true if we ran the ajax validation, tells the logic to stop messing with prompts + var isAjaxValidator = false; + var fieldName = field.attr("name"); + var promptText = ""; + var required = false; + options.isError = false; + options.showArrow = true; + + for (var i = 0; i < rules.length; i++) { + + var errorMsg = undefined; + switch (rules[i]) { + + case "required": + required = true; + errorMsg = methods._required(field, rules, i, options); + break; + case "custom": + errorMsg = methods._customRegex(field, rules, i, options); + break; + case "groupRequired": + // Check is its the first of group, if not, reload validation with first field + // AND continue normal validation on present field + var classGroup = "[class*=" +rules[i + 1] +"]"; + var firstOfGroup = field.closest("form").find(classGroup).eq(0); + if(firstOfGroup[0] != field[0]){ + methods._validateField(firstOfGroup, options, skipAjaxValidation) + options.showArrow = true; + continue; + }; + errorMsg = methods._groupRequired(field, rules, i, options); + if(errorMsg) required = true; + options.showArrow = false; + break; + case "ajax": + // ajax has its own prompts handling technique + if(!skipAjaxValidation){ + methods._ajax(field, rules, i, options); + isAjaxValidator = true; + } + break; + case "minSize": + errorMsg = methods._minSize(field, rules, i, options); + break; + case "maxSize": + errorMsg = methods._maxSize(field, rules, i, options); + break; + case "min": + errorMsg = methods._min(field, rules, i, options); + break; + case "max": + errorMsg = methods._max(field, rules, i, options); + break; + case "past": + errorMsg = methods._past(field, rules, i, options); + break; + case "future": + errorMsg = methods._future(field, rules, i, options); + break; + case "dateRange": + var classGroup = "[class*=" + rules[i + 1] + "]"; + var firstOfGroup = field.closest("form").find(classGroup).eq(0); + var secondOfGroup = field.closest("form").find(classGroup).eq(1); + /* + if (firstOfGroup[0] != field[0]) { + methods._validateField(firstOfGroup, options, skipAjaxValidation) + options.showArrow = true; + continue; + }; + */ + //if one entry out of the pair has value then proceed to run through validation + if (firstOfGroup[0].value || secondOfGroup[0].value) { + errorMsg = methods._dateRange(firstOfGroup, secondOfGroup, rules, i, options); + } + if (errorMsg) required = true; + options.showArrow = false; + break; + + case "dateTimeRange": + var classGroup = "[class*=" + rules[i + 1] + "]"; + var firstOfGroup = field.closest("form").find(classGroup).eq(0); + var secondOfGroup = field.closest("form").find(classGroup).eq(1); + /* + if (firstOfGroup[0] != field[0]) { + methods._validateField(firstOfGroup, options, skipAjaxValidation) + options.showArrow = true; + continue; + }; + */ + //if one entry out of the pair has value then proceed to run through validation + if (firstOfGroup[0].value || secondOfGroup[0].value) { + errorMsg = methods._dateTimeRange(firstOfGroup, secondOfGroup, rules, i, options); + } + if (errorMsg) required = true; + options.showArrow = false; + break; + case "maxCheckbox": + errorMsg = methods._maxCheckbox(field, rules, i, options); + field = $($("input[name='" + fieldName + "']")); + break; + case "minCheckbox": + errorMsg = methods._minCheckbox(field, rules, i, options); + field = $($("input[name='" + fieldName + "']")); + break; + case "equals": + errorMsg = methods._equals(field, rules, i, options); + break; + case "funcCall": + errorMsg = methods._funcCall(field, rules, i, options); + break; + + default: + //$.error("jQueryValidator rule not found"+rules[i]); + } + if (errorMsg !== undefined) { + promptText += errorMsg + "
"; + options.isError = true; + + } + + } + // If the rules required is not added, an empty field is not validated + if(!required){ + if(field.val() == "") options.isError = false; + } + + // Hack for radio/checkbox group button, the validation go into the + // first radio/checkbox of the group + var fieldType = field.attr("type"); + + if ((fieldType == "radio" || fieldType == "checkbox") && $("input[name='" + fieldName + "']").size() > 1) { + field = $($("input[name='" + fieldName + "'][type!=hidden]:first")); + options.showArrow = false; + } + if (fieldType == "text" && $("input[name='" + fieldName + "']").size() > 1) { + field = $($("input[name='" + fieldName + "'][type!=hidden]:first")); + options.showArrow = false; + } + + if (options.isError){ + + methods._showPrompt(field, promptText, "", false, options); + }else{ + if (!isAjaxValidator) methods._closePrompt(field); + } + field.trigger("jqv.field.result", [field, options.isError, promptText]); + return options.isError; + }, + /** + * Required validation + * + * @param {jqObject} field + * @param {Array[String]} rules + * @param {int} i rules index + * @param {Map} + * user options + * @return an error string if validation failed + */ + _required: function(field, rules, i, options) { + switch (field.attr("type")) { + case "text": + case "password": + case "textarea": + case "file": + default: + if (!field.val()) + return options.allrules[rules[i]].alertText; + break; + case "radio": + case "checkbox": + var name = field.attr("name"); + if ($("input[name='" + name + "']:checked").size() == 0) { + if ($("input[name='" + name + "']").size() == 1) + return options.allrules[rules[i]].alertTextCheckboxe; + else + return options.allrules[rules[i]].alertTextCheckboxMultiple; + } + break; + // required for