/*https://thewayofcode.wordpress.com/2012/01/18/custom-unobtrusive-jquery-validation-with-data-annotations-in-mvc-3/*/ /*METHODS*/ // Value is the element to be validated, params is the array of name/value pairs of the parameters extracted from the HTML, element is the HTML element that the validator is attached to $.validator.addMethod("datenotexceedprop", function (value, element, params) { //console.log("val: " + value + "/" + Date.parse(value) + "; params: " + $(params).val() + "/" + Date.parse($(params).val())); return value == "" || $(params).val() == "" || (Date.parse(value) <= Date.parse($(params).val())); }); $.validator.addMethod("dategreaterthanorequal", function (value, element, params) { //console.log("val: " + value + "/" + Date.parse(value) + "; params: " + $(params).val() + "/" + Date.parse($(params).val())); return $(params).val() == null || Date.parse($(params).val()) == NaN || (Date.parse(value) >= Date.parse($(params).val())); }); $.validator.addMethod("eitherofrequired", function (value, element, params) { //console.log("val: " + value + "; params: " + $(params).val()); if ($(params).length > 0) return !(!value && !$(params).val()); else return !!value; }); $.validator.addMethod('requiredif', function (value, element, parameters) { var id = '#' + parameters['dependentproperty']; // get the target value (as a string, // as that's what actual value will be) var targetvalue = parameters['targetvalue']; targetvalue = (targetvalue == null ? '' : targetvalue).toString(); // get the actual value of the target control // note - this probably needs to cater for more // control types, e.g. radios var control = $(id); var controltype = control.attr('type'); var actualvalue = controltype === 'checkbox' ? control.attr('checked').toString() : control.val(); // if the condition is true, reuse the existing // required field validator functionality if (targetvalue === actualvalue) return $.validator.methods.required.call( this, value, element, parameters); return true; } ); $.validator.addMethod('integer', function (value, element, params) { return value == "" || value == null || parseInt(value, 10) == value; }); $.validator.addMethod('validationgroup', function (value, element, params) { //console.log("val: " + value + "; dependantproperty: " + params.dependantproperty + "; eitherofvalidation: " + params.eitherofvalidation + "; depvalue: " + $("#" + params.dependantproperty).val()); if (params.currentgroup == params.group) { if (params.dependantproperty != "") { var obj = $("#" + params.dependantproperty); if (params.eitherofvalidation == "True") { if (obj.length > 0) return !(!value && !obj.val()); else return true;//if second field is not on the form, consider form valid //e.g. in expense-only projects, when margin is hidden, we allow empty expense } else { if (obj.length > 0) { var val = obj.val(); if ('false' === val.toLowerCase()) return true; } } } return value != ""; } else { return true; } }); $.validator.addMethod('projectedrevenue', function (value, element, params) { var hasactuals = params.hasactuals === '1', isrevenuegenerating = params.isrevenuegenerating === '1', actualstotal = parseFloat(params.actualstotal) || 0, projectedrevenue = parseFloat(value) || 0, uselmmargin = $('#' + params.uselmmarginproperty).is(':checked'), grossmargin = (parseFloat($('#' + params.grossmarginproperty).val()) / 100.0) || 0, lmmargin = (parseFloat($('#' + params.lmmarginproperty).val()) / 100.0) || 0; var margin = uselmmargin ? lmmargin : grossmargin; var minRevenue = actualstotal / (1 - margin); var isValid = !(hasactuals && isrevenuegenerating && projectedrevenue < minRevenue); if (!isValid) $.validator.messages.projectedrevenue = 'Projected Revenue can\'t be less than actual expences of a project ({0})'.replace('{0}', '$' + minRevenue.toFixed(2)); return isValid; }); $.validator.addMethod('tddirectcost', function (value, element, params) { var hasactuals = params.hasactuals === '1', isrevenuegenerating = params.isrevenuegenerating === '1', actualstotal = parseFloat(params.actualstotal) || 0, tddirectcosts = parseFloat(value) || 0; var isValid = !(hasactuals && isrevenuegenerating && tddirectcosts <= actualstotal); if (!isValid) $.validator.messages.tddirectcost = 'Projected Expence should be more than actual expences of a project ({0})'.replace('{0}', '$' + actualstotal.toFixed(2)); return isValid; }); $.validator.addMethod('dateforstartofchange', function (value, element, params) { var hasactuals = params.hasactuals === '1', actualsenddateMs = new Number(params.actualsenddate) || 0, currentdate = new Date(value) || null, scenarioStartDate = new Date($('#' + params.scenariostartdateproperty).val()) || null; scenarioEndDate = new Date($('#' + params.scenarioenddateproperty).val()) || null; var currentMs = Date.UTC(currentdate.getFullYear(), currentdate.getMonth(), currentdate.getDate()); var scenarioStartDateMs = Date.UTC(scenarioStartDate.getFullYear(), scenarioStartDate.getMonth(), scenarioStartDate.getDate()); var scenarioEndDateMs = Date.UTC(scenarioEndDate.getFullYear(), scenarioEndDate.getMonth(), scenarioEndDate.getDate()); if (currentMs < scenarioStartDateMs) { $.validator.messages.dateforstartofchange = 'Date for start of change can not be less than scenario start date ( ' + (scenarioStartDate.getMonth() + 1) + '/' + scenarioStartDate.getDate() + '/' + scenarioStartDate.getFullYear() + ' )'; return false; } if (currentMs > scenarioEndDateMs) { $.validator.messages.dateforstartofchange = 'Date for start of change can not be greater than scenario end date ( ' + (scenarioEndDate.getMonth() + 1) + '/' + scenarioEndDate.getDate() + '/' + scenarioEndDate.getFullYear() + ' )'; return false; } if (hasactuals && currentMs <= actualsenddateMs) { var actualsEndDate = new Date(actualsenddateMs); $.validator.messages.dateforstartofchange = 'Date for start of change should be bigger than actuals end date ( ' + (actualsEndDate.getMonth() + 1) + '/' + actualsEndDate.getDate() + '/' + actualsEndDate.getFullYear() + ' )'; return false; } return true; }); $.validator.addMethod('scenarioenddate', function (value, element, params) { var hasactuals = $(element).attr('data-val-scenarioenddate-hasactuals') === '1', projectdeadlineMs = parseFloat($(element).attr('data-val-scenarioenddate-projectdeadline')) || 0, actualsenddateMs = parseFloat($(element).attr('data-val-scenarioenddate-actualsenddate')) || 0, endDate = new Date(value) || null; if (!endDate) return true; var endDateMs = Date.UTC(endDate.getFullYear(), endDate.getMonth(), endDate.getDate()); var isValid = !(projectdeadlineMs > 0 && endDateMs > projectdeadlineMs); if (!isValid) { var deadline = new Date(projectdeadlineMs); $.validator.messages.scenarioenddate = 'Scenario End date exceeds project deadline ( ' + (deadline.getMonth() + 1) + '/' + deadline.getDate() + '/' + deadline.getFullYear() + ' )'; return false; } isValid = !(hasactuals && actualsenddateMs > 0 && endDateMs < actualsenddateMs); if (!isValid) { var aenddate = new Date(actualsenddateMs); $.validator.messages.scenarioenddate = 'You can\'t resize scenario prior to Actuals end date + 1 week ( ' + (aenddate.getMonth() + 1) + '/' + aenddate.getDate() + '/' + aenddate.getFullYear() + ' )' return false; } return true; }); $.validator.addMethod('scenariostartdate', function (value, element, params) { var hasactuals = $(element).attr('data-val-scenariostartdate-hasactuals') === '1', actualsstartdateMs = parseFloat($(element).attr('data-val-scenariostartdate-actualsstartdate')) || 0, startDate = new Date(value) || null; if (startDate == null || actualsstartdateMs <= 0) return true; var startDateMs = Date.UTC(startDate.getFullYear(), startDate.getMonth(), startDate.getDate()); var isValid = !(hasactuals && startDateMs > actualsstartdateMs); if (!isValid) { var actualsstartdate = new Date(actualsstartdateMs); $.validator.messages.scenariostartdate = 'You should set scenario start date prior or equal to actuals start date ( ' + (actualsstartdate.getMonth() + 1) + '/' + actualsstartdate.getDate() + '/' + actualsstartdate.getFullYear() + ' )'; } return isValid; }); // /*ADAPTERS*/ /* The adapter signature: adapterName is the name of the adapter, and matches the name of the rule in the HTML element. params is an array of parameter names that you're expecting in the HTML attributes, and is optional. If it is not provided, then it is presumed that the validator has no parameters. fn is a function which is called to adapt the HTML attribute values into jQuery Validate rules and messages. The function will receive a single parameter which is an options object with the following values in it: element The HTML element that the validator is attached to form The HTML form element message The message string extract from the HTML attribute params The array of name/value pairs of the parameters extracted from the HTML attributes rules The jQuery rules array for this HTML element. The adapter is expected to add item(s) to this rules array for the specific jQuery Validate validators that it wants to attach. The name is the name of the jQuery Validate rule, and the value is the parameter values for the jQuery Validate rule. messages The jQuery messages array for this HTML element. The adapter is expected to add item(s) to this messages array for the specific jQuery Validate validators that it wants to attach, if it wants a custom error message for this rule. The name is the name of the jQuery Validate rule, and the value is the custom message to be displayed when the rule is violated. */ $.validator.unobtrusive.adapters.add("datenotexceedprop", ["otherpropertyname"], function (options) { options.rules["datenotexceedprop"] = "#" + options.params.otherpropertyname; options.messages["datenotexceedprop"] = options.message; }); $.validator.unobtrusive.adapters.add("dategreaterthanorequal", ["otherpropertyname"], function (options) { options.rules["dategreaterthanorequal"] = "#" + options.params.otherpropertyname; options.messages["dategreaterthanorequal"] = options.message; }); $.validator.unobtrusive.adapters.add("eitherofrequired", ["otherpropertyname", "singleerrormessage"], function (options) { options.rules["eitherofrequired"] = "#" + options.params.otherpropertyname; if ($(options.rules["eitherofrequired"]).length > 0) options.messages["eitherofrequired"] = options.message; else options.messages["eitherofrequired"] = options.params.singleerrormessage; }); $.validator.unobtrusive.adapters.add('requiredif', ['dependentproperty', 'targetvalue'], function(options) { options.rules['requiredif'] = { dependentproperty: options.params['dependentproperty'], targetvalue: options.params['targetvalue'] }; options.messages['requiredif'] = options.message; }); $.validator.unobtrusive.adapters.add( 'validationgroup', ['group', 'dependantproperty', 'eitherofvalidation', 'currentgroup'], function (options) { options.rules['validationgroup'] = options.params; options.messages['validationgroup'] = options.message; } ); jQuery.validator.unobtrusive.adapters.add("integer", [], function (options) { options.rules['integer'] = {}; options.messages['integer'] = options.message; }); $.validator.unobtrusive.adapters.add('projectedrevenue', ['hasactuals', 'isrevenuegenerating', 'uselmmarginproperty', 'grossmarginproperty', 'lmmarginproperty', 'actualstotal'], function (options) { options.rules['projectedrevenue'] = options.params; } ); $.validator.unobtrusive.adapters.add('tddirectcost', ['hasactuals', 'isrevenuegenerating', 'actualstotal'], function (options) { options.rules['tddirectcost'] = options.params; } ); $.validator.unobtrusive.adapters.add('dateforstartofchange', ['hasactuals', 'actualsenddate', 'scenariostartdateproperty', 'scenarioenddateproperty'], function (options) { options.rules['dateforstartofchange'] = options.params; } ); $.validator.unobtrusive.adapters.add('scenarioenddate', ['hasactuals', 'projectdeadline', 'actualsenddate'], function (options) { options.rules['scenarioenddate'] = options.params; } ); $.validator.unobtrusive.adapters.add('scenariostartdate', ['hasactuals', 'actualsstartdate'], function (options) { options.rules['scenariostartdate'] = options.params; } );