/*! * jQuery bValidator plugin * * http://code.google.com/p/bvalidator/ * * Copyright (c) 2012 Bojan Mauser * * Joomla! 2.5 Ajax Mod by Jaroslaw Paluch, SoftDeco, (c) 2013 * * Released under the MIT license * http://www.opensource.org/licenses/mit-license.php * * $Id: jquery.bvalidator.js 101 2012-09-30 23:51:36Z bmauser $ */ (function($){ // constructor $.fn.bValidator = function(overrideOptions, instanceName){ return this.each(function(){ new bValidator($(this), overrideOptions, instanceName); }); }; // bValidator class bValidator = function(mainElement, overrideOptions, instanceName){ // default options var options = { singleError: false, // validate all inputs at once offset: {x:-23, y:-4}, // offset position for error message tooltip position: {x:'right', y:'top'}, // error message placement x:left|center|right y:top|center|bottom template: '
{message}
', // template for error message templateCloseIcon: '
{message}
x
', // template for error message container when showCloseIcon option is true showCloseIcon: true, // put close icon on error message showErrMsgSpeed: 'normal', // message's fade-in speed 'fast', 'normal', 'slow' or number of milliseconds scrollToError: true, // scroll to first error // css class names classNamePrefix: 'bvalidator_', // prefix for css class names closeIconClass: 'close_icon', // close error message icon class errMsgClass: 'errmsg', // error message class errorClass: 'invalid', // input field class name in case of validation error validClass: '', // input field class name in case of valid value lang: 'en', // default language for error messages errorMessageAttr: 'data-bvalidator-msg',// name of the attribute for overridden error message validateActionsAttr: 'data-bvalidator', // name of the attribute which stores info what validation actions to do forceValidAttr: 'data-bvalidator-forcevalid', // name of the attribute which which makes validator to act like the field is valid modifyActionsAttr: 'data-bvalidator-modifier', paramsDelimiter: ':', // delimiter for validator action options inside [] actionsDelimiter: ',', // delimiter for validator actions // when to validate validateOn: null, // null, 'change', 'blur', 'keyup' errorValidateOn: 'keyup', // null, 'change', 'blur', 'keyup' // callback functions onBeforeValidate: null, onAfterValidate: null, onValidateFail: null, onValidateSuccess: null, onBeforeElementValidation: null, onAfterElementValidation: null, onBeforeAllValidations: null, onAfterAllValidations: null, validateOnSubmit: true, // should validation occur on form submit if validator is bind to a form stopSubmitPropagation: true, // should submit event be stopped on error if validator is bind to a form noMsgIfExistsForInstance: [], validateTillInvalid: false, autoModifiers: { 'digit': ['trim'], 'number': ['trim'], 'email': ['trim'], 'url': ['trim'], 'date': ['trim'], 'ip4': ['trim'], 'ip6': ['trim'] }, ajaxAnswerValid: 'ok', ajaxDelay: 300, ajaxOptions: {cache: false}, ajaxParamName: 'bValue', // default messages errorMessages: { en: { 'default': 'Please correct this value.', 'equalto': 'Please enter the same value again.', 'differs': 'Please enter a different value.', 'minlength': 'The length must be at least {0} characters', 'maxlength': 'The length must be at max {0} characters', 'rangelength':'The length must be between {0} and {1}', 'min': 'Please enter a number greater than or equal to {0}.', 'max': 'Please enter a number less than or equal to {0}.', 'between': 'Please enter a number between {0} and {1}.', 'required': 'This field is required.', 'alpha': 'Please enter alphabetic characters only.', 'alphanum': 'Please enter alphanumeric characters only.', 'digit': 'Please enter only digits.', 'number': 'Please enter a valid number.', 'email': 'Please enter a valid email address.', 'image': 'This field should only contain image types', 'url': 'Please enter a valid URL.', 'ip4': 'Please enter a valid IPv4 address.', 'ip6': 'Please enter a valid IPv6 address.', 'date': 'Please enter a valid date in format {0}.' } } }, _ajaxValidation = function(element, instanceName, ajaxUrl, sync, option, task){ var ajax_data = element.data("ajaxData.bV" + instanceName); if(!ajax_data){ ajax_data = {}; element.data("ajaxData.bV" + instanceName, ajax_data); } else{ clearTimeout(ajax_data.timeOut); } // value to validate ajax_data.val = element.val(); // do not do ajax if value is already validated if(ajax_data.lastValidated === ajax_data.val) return validator.ajax(ajax_data.lastResponse); var ajaxOptions = $.extend({}, options.ajaxOptions); if(typeof ajaxOptions.data != 'object') ajaxOptions.data = {} ajaxOptions.url = ajaxUrl; ajaxOptions.type = 'POST'; if(sync){ var ret = false; ajaxOptions.async = false; ajaxOptions.data[options.ajaxParamName] = ajax_data.val; if (option != undefined) { ajaxOptions.data["option"] = option; } if (task != undefined) { ajaxOptions.data["task"] = task; } $.ajax(ajaxOptions).done(function(ajaxResponse){ ajax_data.lastValidated = ajax_data.val; ajax_data.lastResponse = ajaxResponse; ret = validator.ajax(ajaxResponse) }); return ret; } else{ ajax_data.timeOut = setTimeout(function() { var val = element.val(); // only check if the value has not changed if(ajax_data.val == val){ ajaxOptions.async = true; ajaxOptions.data[options.ajaxParamName] = val; if (option != undefined) { ajaxOptions.data["option"] = option; } if (task != undefined) { ajaxOptions.data["task"] = task; } $.ajax(ajaxOptions).done(function(ajaxResponse){ ajax_data.lastValidated = val; ajax_data.lastResponse = ajaxResponse; instance.validate(false, element, undefined, ajaxResponse) }); } }, options.ajaxDelay); } return; }, // returns all inputs _getElementsForValidation = function(element){ // skip hidden and input fields witch we do not want to validate return element.is(':input') ? element : element.find(':input[' + options.validateActionsAttr + '], :input[' + options.modifyActionsAttr + ']').not(":button, :image, :reset, :submit, :hidden, :disabled"); }, // binds validateOn event _bindValidateOn = function(elements){ elements.bind(options.validateOn + '.bV' + instanceName, {'bVInstance': instance}, function(event){ event.data.bVInstance.validate(false, $(this)); }); }, // checks does message from validator instance exists on element _isMsgFromInstanceExists = function(element, instance_names){ for(var i=0; i').css('position','absolute'); element.data("errMsg.bV" + instanceName, msg_container); msg_container.insertAfter(element); var messagesHtml = ''; for(var i=0; i\n'; if(options.showCloseIcon) messagesHtml = options.templateCloseIcon.replace('{message}', messagesHtml).replace('{closeIconClass}', options.classNamePrefix+options.closeIconClass); // make tooltip from template var tooltip = $(options.template.replace('{errMsgClass}', options.classNamePrefix+options.errMsgClass).replace('{message}', messagesHtml)); tooltip.appendTo(msg_container); // bind close tootlip function tooltip.find('.' + options.classNamePrefix+options.closeIconClass).click(function(e){ e.preventDefault(); $(this).closest('.'+ options.classNamePrefix+options.errMsgClass).css('visibility', 'hidden'); }); var pos = _getErrMsgPosition(element, tooltip); tooltip.css({visibility: 'visible', position: 'absolute', top: pos.top, left: pos.left}).fadeIn(options.showErrMsgSpeed); if(options.scrollToError){ // get most top tolltip var tot = tooltip.offset().top; if(scroll_to === null || tot < scroll_to) scroll_to = tot; } }, // removes message from DOM _removeMsg = function(element){ var existingMsg = element.data("errMsg.bV" + instanceName) if(existingMsg){ existingMsg.remove(); element.data("errMsg.bV" + instanceName, null); } }, // calculates message position _getErrMsgPosition = function(input, tooltip){ var tooltipContainer = input.data("errMsg.bV" + instanceName), top = - ((tooltipContainer.offset().top - input.offset().top) + tooltip.outerHeight() - options.offset.y), left = (input.offset().left + input.outerWidth()) - tooltipContainer.offset().left + options.offset.x, x = options.position.x, y = options.position.y; // adjust Y if(y == 'center' || y == 'bottom'){ var height = tooltip.outerHeight() + input.outerHeight(); if (y == 'center') {top += height / 2;} if (y == 'bottom') {top += height;} } // adjust X if(x == 'center' || x == 'left'){ var width = input.outerWidth(); if (x == 'center') {left -= width / 2;} if (x == 'left') {left -= width;} } return {top: top, left: left}; }, // calls callback function _callBack = function(type, param1, param2, param3){ if($.isFunction(options[type])){ return options[type](param1, param2, param3); } }, // returns checkboxes in a group _chkboxGroup = function(chkbox){ var name = chkbox.attr('name'); if(name && /^[^\[\]]+\[.*\]$/.test(name)){ return $('input:checkbox').filter(function(){ var r = new RegExp(name.match(/^[^\[\]]+/)[0] + '\\[.*\\]$'); return this.name.match(r); }); } return chkbox; }, // gets element value _getValue = function(element){ var ret = {}; // checkbox if(element.is('input:checkbox')){ ret['value'] = element.attr('name') ? ret['selectedInGroup'] = _chkboxGroup(element).filter(':checked').length : element.attr('checked'); } else if(element.is('input:radio')){ ret['value'] = element.attr('name') ? ret['value'] = $('input:radio[name="' + element.attr('name') + '"]:checked').length : element.val(); } else if(element.is('select')){ ret['selectedInGroup'] = $("option:selected", element).length; ret['value'] = element.val(); } else if(element.is(':input')){ ret['value'] = element.val(); } return ret; }, // parses bValidator attributes _parseAttr = function(attrVal){ // value of validateActionsAttr input attribute var action_str = $.trim(attrVal).replace(new RegExp('\\s*\\' + options.actionsDelimiter + '\\s*', 'g'), options.actionsDelimiter); if(!action_str) return null; return action_str.split(options.actionsDelimiter); }, // parses validator action and parameters _parseAction = function(actionStr){ var ap = $.trim(actionStr).match(/^(.*?)\[(.*?)\]/); if(ap && ap.length == 3){ return { name: ap[1], params: ap[2].split(options.paramsDelimiter) } } else{ return { name: actionStr, params:[] } } }, // applys modifier _applyModifier = function(action, el){ var oldVal, newVal = _callModifier(action, el); if(typeof newVal !== 'undefined'){ oldVal = $(el).val(); if(oldVal != newVal) $(el).val(newVal); } }, // calls modifier _callModifier = function(action, el){ var apply_params = [$(el).val()].concat(action.params); if(typeof modifier[action.name] == 'function') return modifier[action.name].apply(el, apply_params); else if(typeof window[action.name] == 'function') return window[action.name].apply(el, apply_params); else if(window.console.warn) window.console.warn('[bValidator] unknown modifier: ' + action.name); }, // calls validator _callValidator = function(action, el, value){ if(typeof validator[action.name] == 'function'){ return validator[action.name].apply(el, [value].concat(action.params)); // add input value to beginning of action.params } // call custom user defined function if(typeof window[action.name] == 'function'){ return window[action.name].apply(el, [value.value].concat(action.params)); } if(window.console.warn) window.console.warn('[bValidator] unknown validator: ' + action.name); }, // object with validator actions validator = { equalto: function(v, elementId){ return v.value == $('#' + elementId).val(); }, differs: function(v, elementId){ return v.value != $('#' + elementId).val(); }, minlength: function(v, minlength){ return (v.value.length >= parseInt(minlength)) }, maxlength: function(v, maxlength){ return (v.value.length <= parseInt(maxlength)) }, rangelength: function(v, minlength, maxlength){ return (v.value.length >= parseInt(minlength) && v.value.length <= parseInt(maxlength)) }, min: function(v, min){ if(v.selectedInGroup) return v.selectedInGroup >= parseFloat(min) else{ if(!validator.number(v)) return false; return (parseFloat(v.value) >= parseFloat(min)) } }, max: function(v, max){ if(v.selectedInGroup) return v.selectedInGroup <= parseFloat(max) else{ if(!validator.number(v)) return false; return (parseFloat(v.value) <= parseFloat(max)) } }, between: function(v, min, max){ if(v.selectedInGroup) return (v.selectedInGroup >= parseFloat(min) && v.selectedInGroup <= parseFloat(max)) if(!validator.number(v)) return false; var va = parseFloat(v.value); return (va >= parseFloat(min) && va <= parseFloat(max)) }, required: function(v){ if(!v.value || !$.trim(v.value)) return false return true }, alpha: function(v){ return validator.regex(v, /^[a-z ._\-]+$/i); }, alphanum: function(v){ return validator.regex(v, /^[a-z\d ._\-]+$/i); }, digit: function(v){ return validator.regex(v, /^\d+$/); }, number: function(v){ return validator.regex(v, /^[-+]?\d+(\.\d+)?$/); }, email: function(v){ return validator.regex(v, /^([a-zA-Z\d_\.\-\+%])+\@(([a-zA-Z\d\-])+\.)+([a-zA-Z\d]{2,4})+$/); }, image: function(v){ return validator.regex(v, /\.(jpg|jpeg|png|gif|bmp)$/i); }, url: function(v){ return validator.regex(v, /^\b(https?|ftp):\/\/([-A-Z0-9.]+)(\/[-A-Z0-9+&@#\/%=~_|!:,.;]*)?(\?[A-Z0-9+&@#\/%=~_|!:,.;]*)?$/i); }, regex: function(v, regex, mod){ if(typeof regex === "string") regex = new RegExp(regex, mod); return regex.test(v.value); }, ip4: function(v){ return validator.regex(v, /^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/); }, ip6: function(v){ return validator.regex(v, /^(?:(?:(?:[A-F\d]{1,4}:){5}[A-F\d]{1,4}|(?:[A-F\d]{1,4}:){4}:[A-F\d]{1,4}|(?:[A-F\d]{1,4}:){3}(?::[A-F\d]{1,4}){1,2}|(?:[A-F\d]{1,4}:){2}(?::[A-F\d]{1,4}){1,3}|[A-F\d]{1,4}:(?::[A-F\d]{1,4}){1,4}|(?:[A-F\d]{1,4}:){1,5}|:(?::[A-F\d]{1,4}){1,5}|:):(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)|(?:[A-F\d]{1,4}:){7}[A-F\d]{1,4}|(?:[A-F\d]{1,4}:){6}:[A-F\d]{1,4}|(?:[A-F\d]{1,4}:){5}(?::[A-F\d]{1,4}){1,2}|(?:[A-F\d]{1,4}:){4}(?::[A-F\d]{1,4}){1,3}|(?:[A-F\d]{1,4}:){3}(?::[A-F\d]{1,4}){1,4}|(?:[A-F\d]{1,4}:){2}(?::[A-F\d]{1,4}){1,5}|[A-F\d]{1,4}:(?::[A-F\d]{1,4}){1,6}|(?:[A-F\d]{1,4}:){1,7}:|:(?::[A-F\d]{1,4}){1,7})$/i); }, date: function(v, format){ // format can be any combination of mm,dd,yyyy with separator between. Example: 'mm.dd.yyyy' or 'yyyy-mm-dd' if(v.value.length == 10 && format.length == 10){ var s = format.match(/[^mdy]+/g); if(s.length == 2 && s[0].length == 1 && s[0] == s[1]){ var d = v.value.split(s[0]), f = format.split(s[0]); for(var i=0; i<3; i++){ if(f[i] == 'dd') var day = d[i]; else if(f[i] == 'mm') var month = d[i]; else if(f[i] == 'yyyy') var year = d[i]; } var dobj = new Date(year, month-1, day) if ((dobj.getMonth()+1!=month) || (dobj.getDate()!=day) || (dobj.getFullYear()!=year)) return false return true } } return false; }, extension: function(){ var v = arguments[0], r = ''; if(!arguments[1]) return false for(var i=1; i scroll_to || $(window).scrollTop()+$(window).height() < scroll_to)){ var ua = navigator.userAgent.toLowerCase(); $(ua.indexOf('chrome')>-1 || ua.indexOf('safari')>-1 ? 'body' : 'html').animate({scrollTop: scroll_to - 10}, {duration: 'slow'}); } return ret; } // returns options object this.getOptions = function(){ return options; } // returns validator object this.getValidators = this.getActions = function(){ return validator; } // returns modifier object this.getModifiers = function(){ return modifier; } // checks validity this.isValid = function(elements){ return this.validate(true, elements, 1, undefined, true); } // deletes message this.removeMsg = this.removeErrMsg = function(element){ _removeMsg(element); } // shows message this.showMsg = function(element, message){ if(element.length){ if(typeof(message)=='string') message = [message]; _showMsg(element, message); } } // returns all inputs this.getInputs = function(){ return _getElementsForValidation(mainElement); } // binds validateOn event this.bindValidateOn = function(element){ _bindValidateOn(element); } // resets validation this.reset = function(){ elements = _getElementsForValidation(mainElement); if (options.validateOn) _bindValidateOn(elements); elements.each(function(){ _removeMsg($(this)); $(this).unbind('.bVerror' + instanceName); $(this).removeClass(options.classNamePrefix+options.errorClass); $(this).removeClass(options.classNamePrefix+options.validClass); $(this).removeData('ajaxData.bV' + instanceName); $(this).removeData('errMsg.bV' + instanceName); $(this).removeData('checked.bV' + instanceName); }); } this.destroy = function(){ if (mainElement.is('form')) mainElement.unbind('.bV' + instanceName); this.reset(); mainElement.removeData("bValidator"); mainElement.removeData("bValidators"); } } })(jQuery);