// JavaScript Document // 401k Calculator // Author : Jesse Kyzar // Date : Aug. 6, 2004 function perform_calculations(formObj) { //clear out any old results reset_results(formObj); //validate form fields; proceed no further if errors are found if(validate_fields(formObj)) { //determine difference between both dates var date_diff = date_difference_in_days(formObj); //calculate dayes in period if(formObj.num_paychecks.value == 52) { var period_days = 7; } else { var period_days = Math.round(365 / formObj.num_paychecks.value); } //calculate compounding periods in first year var compounding_periods = Math.round(date_diff / period_days); //calculate first progressed age fields age_progression(formObj, period_days, compounding_periods, date_diff); //line 29; causes page to scroll to results location.href = "#scrollTo"; //show progress; remove later //var progress_check = "Date difference : " + date_diff + "\nDays in period : " + period_days + "\nCompounding periods in first year : " + compounding_periods; //alert(progress_check); } } function validate_fields(formObj) { //error tracking variables var errors = 0; var num_errors = 0; var messages = ""; //date variables var as_of_date = ""; var year_end_date = ""; //validate contribution : required; must be numeric; if(formObj.contribution.value.length == 0) { errors = errors | 1; num_errors++; messages = messages + num_errors + ". 401(k) contribution per paycheck is required.\n"; } else if(isNaN(formObj.contribution.value)) { errors = errors | 1; num_errors++; messages = messages + num_errors + ". 401(k) contribution per paycheck must be a number.\n"; } //validate emp_contribution : required; must be numeric; if(formObj.emp_contribution.value.length == 0) { errors = errors | 2; num_errors++; messages = messages + num_errors + ". 401(k) employer contribution match per paycheck is required.\n"; } else if(isNaN(formObj.emp_contribution.value)) { errors = errors | 2; num_errors++; messages = messages + num_errors + ". 401(k) employer contribution match per paycheck must be a number.\n"; } //validate num_paychecks : required; if(formObj.num_paychecks.value.length == 0) { errors = errors | 4; num_errors++; messages = messages + num_errors + ". Paychecks per year is required.\n"; } //validate return_rate : required; must be numeric; if(formObj.return_rate.value.length == 0) { errors = errors | 8; num_errors++; messages = messages + num_errors + ". Expected annual rate of return is required.\n"; } else if(isNaN(formObj.return_rate.value)) { errors = errors | 8; num_errors++; messages = messages + num_errors + ". Expected annual rate of return must be a number.\n"; } //validate investment_age : required; must be numeric; must be less than anticipated retirement age; if(formObj.investment_age.value.length == 0) { errors = errors | 16; num_errors++; messages = messages + num_errors + ". Age as of the end of this tax year is required.\n"; } else if(isNaN(formObj.investment_age.value)) { errors = errors | 16; num_errors++; messages = messages + num_errors + ". Age as of the end of this tax year must be a number.\n"; } //validate retirement_age : required; must be numeric; must be greater than age as of end of this tax year and less than 90; if(formObj.retirement_age.value.length == 0) { errors = errors | 32; num_errors++; messages = messages + num_errors + ". Anticipated retirement age is required.\n"; } else if(isNaN(formObj.retirement_age.value)) { errors = errors | 32; num_errors++; messages = messages + num_errors + ". Anticipated retirement age must be a number.\n"; } //validate investment_age and retirement_age; retirement_age must be greater; if((!isNaN(formObj.investment_age.value)) && (!isNaN(formObj.retirement_age.value)) && ((formObj.investment_age.value > formObj.retirement_age.value) || (formObj.retirement_age.value >= 90))) { errors = errors | 16; errors = errors | 32; num_errors++; messages = messages + num_errors + ". Anticipated retirement age must be greater than age as of the end of this tax year and less than 90.\n"; } //validate current_value : required; must be numeric; if(formObj.current_value.value.length == 0) { errors = errors | 64; num_errors++; messages = messages + num_errors + ". Current value of 401(k) is required.\n"; } else if(isNaN(formObj.current_value.value)) { errors = errors | 64; num_errors++; messages = messages + num_errors + ". Current value of 401(k) must be a number.\n"; } //validate as-of date : must be a valid date; must be less than year-end date; if(!validate_dates(formObj.aod_month.value, formObj.aod_day.value, formObj.aod_year.value)) { errors = errors | 128; num_errors++; messages = messages + num_errors + ". Date (the \"as of\" date for the current value) is invalid.\n"; } else { as_of_date = new Date(formObj.aod_month.value + " " + formObj.aod_day.value + ", " + formObj.aod_year.value); } //validate year-end date : must be a valid date; must be greater than as-of date; if(!validate_dates(formObj.eoy_month.value, formObj.eoy_day.value, formObj.eoy_year.value)) { errors = errors | 256; num_errors++; messages = messages + num_errors + ". The date of the year end is invalid.\n"; } else { year_end_date = new Date(formObj.eoy_month.value + " " + formObj.eoy_day.value + ", " + formObj.eoy_year.value); } //validate as-of and year end dates; year-end must be greater than as-of; if(as_of_date > year_end_date) { errors = errors | 128; errors = errors | 256; num_errors++; messages = messages + num_errors + ". The date of the year end must be later than date (the \"as of\" date for the current value).\n"; } //validate tax_rate : required; must be numeric; if(formObj.tax_rate.value.length == 0) { errors = errors | 512; num_errors++; messages = messages + num_errors + ". Marginal tax rate is required."; } else if(isNaN(formObj.tax_rate.value)) { errors = errors | 512; num_errors++; messages = messages + num_errors + ". Marginal tax rate must be a number.\n"; } if(num_errors == 1) { messages = "An error was found in the information you provided:\n" + messages; alert(messages); return false; } else if(num_errors > 1) { messages = num_errors + " errors were found in the information you provided:\n" + messages; alert(messages); return false; } else { return true; } } function date_difference_in_days(formObj) { var as_of_date = new Date(formObj.aod_month.value + " " + formObj.aod_day.value + ", " + formObj.aod_year.value); var year_end_date = new Date(formObj.eoy_month.value + " " + formObj.eoy_day.value + ", " + formObj.eoy_year.value); return Math.round((((((year_end_date - as_of_date) / 1000) /60) / 60) / 24)); } function validate_dates(checkMonth, checkDay, checkYear) { //make sure month and day are valid switch(checkMonth) { //take leap years into account case "February": if(checkDay == 29) { if((checkYear == 2004) || (checkYear == 2008) || (checkYear == 2012)) { return 1; } else { return 0; } } else if(checkDay > 29) { return 0; } else { return 1; } break; case "April": if(checkDay > 30) { return 0; } else { return 1; } break; case "June": if(checkDay > 30) { return 0; } else { return 1; } break; case "September": if(checkDay > 30) { return 0; } else { return 1; } break; case "November": if(checkDay > 30) { return 0; } else { return 1; } break; default : return 1; break; } } function age_progression(formObj, period_days, compounding_periods, date_diff) { //determine difference between retirement age and age as of the end of this tax year var age_diff = formObj.retirement_age.value - formObj.investment_age.value; //set results fields if(age_diff <= 5) { formObj.results_401k_age1.value = formObj.investment_age.value; formObj.results_savings_age1.value = formObj.investment_age.value; formObj.results_401k_value1.value = Math.round(((((date_diff / 365) * convert_percent(parseFloat(formObj.return_rate.value))) + 1) * parseFloat(formObj.current_value.value))) + Math.round(future_value((convert_percent(formObj.return_rate.value) / (365 / parseInt(period_days))), parseInt(compounding_periods), (parseFloat(formObj.contribution.value) + parseFloat(formObj.emp_contribution.value)))); formObj.results_savings_value1.value = Math.round(((((1 - convert_percent(formObj.tax_rate.value)) * convert_percent(formObj.return_rate.value)) * date_diff / 365) + 1) * parseFloat(formObj.current_value.value)) + Math.round(future_value(((1 - convert_percent(formObj.tax_rate.value)) * convert_percent(formObj.return_rate.value)) / (365 / period_days), compounding_periods, (parseFloat(formObj.contribution.value) + parseFloat(formObj.emp_contribution.value)))); formObj.results_401k_age2.value = formObj.retirement_age.value; formObj.results_savings_age2.value = formObj.retirement_age.value; formObj.results_401k_value2.value = Math.round(parseInt(formObj.results_401k_value1.value) * (Math.pow((1 + convert_percent(formObj.return_rate.value)), (parseInt(formObj.results_401k_age2.value) - parseInt(formObj.results_401k_age1.value))))) + Math.round(future_value(convert_percent(formObj.return_rate.value) / parseInt(formObj.num_paychecks.value), (parseInt(formObj.results_401k_age2.value) - parseInt(formObj.results_401k_age1.value)) * parseInt(formObj.num_paychecks.value), (parseFloat(formObj.contribution.value) + parseFloat(formObj.emp_contribution.value)))); formObj.results_savings_value2.value = Math.round(Math.pow((((1 - convert_percent(parseFloat(formObj.tax_rate.value))) * convert_percent(parseFloat(formObj.return_rate.value))) + 1), (parseInt(formObj.results_savings_age2.value) - parseInt(formObj.results_savings_age1.value))) * parseFloat(formObj.results_savings_value1.value)) + Math.round(future_value((convert_percent(parseFloat(formObj.return_rate.value)) * (1 - convert_percent(parseFloat(formObj.tax_rate.value)))) / parseInt(formObj.num_paychecks.value), (parseInt(formObj.results_savings_age2.value) - parseInt(formObj.results_savings_age1.value)) * parseInt(formObj.num_paychecks.value), (parseFloat(formObj.contribution.value) + parseFloat(formObj.emp_contribution.value)))); formObj.monthly_income_401k.value = payment(parseInt(formObj.results_401k_value2.value), convert_percent(parseFloat(formObj.return_rate.value)), ((90 - parseInt(formObj.retirement_age.value)) * 12)); formObj.monthly_income_savings.value = payment(parseInt(formObj.results_savings_value2.value), convert_percent(parseFloat(formObj.return_rate.value)), ((90 - parseInt(formObj.retirement_age.value)) * 12)); } else if((age_diff > 5) && (age_diff <= 15)) { formObj.results_401k_age1.value = formObj.investment_age.value; formObj.results_savings_age1.value = formObj.investment_age.value; formObj.results_401k_value1.value = Math.round(((((date_diff / 365) * convert_percent(parseFloat(formObj.return_rate.value))) + 1) * parseFloat(formObj.current_value.value))) + Math.round(future_value((convert_percent(formObj.return_rate.value) / (365 / parseInt(period_days))), parseInt(compounding_periods), (parseFloat(formObj.contribution.value) + parseFloat(formObj.emp_contribution.value)))); formObj.results_savings_value1.value = Math.round(((((1 - convert_percent(formObj.tax_rate.value)) * convert_percent(formObj.return_rate.value)) * date_diff / 365) + 1) * parseFloat(formObj.current_value.value)) + Math.round(future_value(((1 - convert_percent(formObj.tax_rate.value)) * convert_percent(formObj.return_rate.value)) / (365 / period_days), compounding_periods, (parseFloat(formObj.contribution.value) + parseFloat(formObj.emp_contribution.value)))); formObj.results_401k_age2.value = parseInt(formObj.investment_age.value) + 5; formObj.results_savings_age2.value = parseInt(formObj.investment_age.value) + 5; formObj.results_401k_value2.value = Math.round(parseInt(formObj.results_401k_value1.value) * (Math.pow((1 + convert_percent(formObj.return_rate.value)), (parseInt(formObj.results_401k_age2.value) - parseInt(formObj.results_401k_age1.value))))) + Math.round(future_value(convert_percent(formObj.return_rate.value) / parseInt(formObj.num_paychecks.value), (parseInt(formObj.results_401k_age2.value) - parseInt(formObj.results_401k_age1.value)) * parseInt(formObj.num_paychecks.value), (parseFloat(formObj.contribution.value) + parseFloat(formObj.emp_contribution.value)))); formObj.results_savings_value2.value = Math.round(Math.pow((((1 - convert_percent(parseFloat(formObj.tax_rate.value))) * convert_percent(parseFloat(formObj.return_rate.value))) + 1), (parseInt(formObj.results_savings_age2.value) - parseInt(formObj.results_savings_age1.value))) * parseFloat(formObj.results_savings_value1.value)) + Math.round(future_value((convert_percent(parseFloat(formObj.return_rate.value)) * (1 - convert_percent(parseFloat(formObj.tax_rate.value)))) / parseInt(formObj.num_paychecks.value), (parseInt(formObj.results_savings_age2.value) - parseInt(formObj.results_savings_age1.value)) * parseInt(formObj.num_paychecks.value), (parseFloat(formObj.contribution.value) + parseFloat(formObj.emp_contribution.value)))); formObj.results_401k_age3.value = formObj.retirement_age.value; formObj.results_savings_age3.value = formObj.retirement_age.value; formObj.results_401k_value3.value = Math.round(parseInt(formObj.results_401k_value2.value) * (Math.pow((1 + convert_percent(formObj.return_rate.value)), (parseInt(formObj.results_401k_age3.value) - parseInt(formObj.results_401k_age2.value))))) + Math.round(future_value(convert_percent(formObj.return_rate.value) / parseInt(formObj.num_paychecks.value), (parseInt(formObj.results_401k_age3.value) - parseInt(formObj.results_401k_age2.value)) * parseInt(formObj.num_paychecks.value), (parseFloat(formObj.contribution.value) + parseFloat(formObj.emp_contribution.value)))); formObj.results_savings_value3.value = Math.round(Math.pow((((1 - convert_percent(parseFloat(formObj.tax_rate.value))) * convert_percent(parseFloat(formObj.return_rate.value))) + 1), (parseInt(formObj.results_savings_age3.value) - parseInt(formObj.results_savings_age2.value))) * parseFloat(formObj.results_savings_value2.value)) + Math.round(future_value((convert_percent(parseFloat(formObj.return_rate.value)) * (1 - convert_percent(parseFloat(formObj.tax_rate.value)))) / parseInt(formObj.num_paychecks.value), (parseInt(formObj.results_savings_age3.value) - parseInt(formObj.results_savings_age2.value)) * parseInt(formObj.num_paychecks.value), (parseFloat(formObj.contribution.value) + parseFloat(formObj.emp_contribution.value)))); formObj.monthly_income_401k.value = payment(parseInt(formObj.results_401k_value3.value), convert_percent(parseFloat(formObj.return_rate.value)), ((90 - parseInt(formObj.retirement_age.value)) * 12)); formObj.monthly_income_savings.value = payment(parseInt(formObj.results_savings_value3.value), convert_percent(parseFloat(formObj.return_rate.value)), ((90 - parseInt(formObj.retirement_age.value)) * 12)); } else { formObj.results_401k_age1.value = formObj.investment_age.value; formObj.results_savings_age1.value = formObj.investment_age.value; formObj.results_401k_value1.value = Math.round(((((date_diff / 365) * convert_percent(parseFloat(formObj.return_rate.value))) + 1) * parseFloat(formObj.current_value.value))) + Math.round(future_value((convert_percent(formObj.return_rate.value) / (365 / parseInt(period_days))), parseInt(compounding_periods), (parseFloat(formObj.contribution.value) + parseFloat(formObj.emp_contribution.value)))); formObj.results_savings_value1.value = Math.round(((((1 - convert_percent(formObj.tax_rate.value)) * convert_percent(formObj.return_rate.value)) * date_diff / 365) + 1) * parseFloat(formObj.current_value.value)) + Math.round(future_value(((1 - convert_percent(formObj.tax_rate.value)) * convert_percent(formObj.return_rate.value)) / (365 / period_days), compounding_periods, (parseFloat(formObj.contribution.value) + parseFloat(formObj.emp_contribution.value)))); formObj.results_401k_age2.value = parseInt(formObj.investment_age.value) + 5; formObj.results_savings_age2.value = parseInt(formObj.investment_age.value) + 5; formObj.results_401k_value2.value = Math.round(parseInt(formObj.results_401k_value1.value) * (Math.pow((1 + convert_percent(formObj.return_rate.value)), (parseInt(formObj.results_401k_age2.value) - parseInt(formObj.results_401k_age1.value))))) + Math.round(future_value(convert_percent(formObj.return_rate.value) / parseInt(formObj.num_paychecks.value), (parseInt(formObj.results_401k_age2.value) - parseInt(formObj.results_401k_age1.value)) * parseInt(formObj.num_paychecks.value), (parseFloat(formObj.contribution.value) + parseFloat(formObj.emp_contribution.value)))); formObj.results_savings_value2.value = Math.round(Math.pow((((1 - convert_percent(parseFloat(formObj.tax_rate.value))) * convert_percent(parseFloat(formObj.return_rate.value))) + 1), (parseInt(formObj.results_savings_age2.value) - parseInt(formObj.results_savings_age1.value))) * parseFloat(formObj.results_savings_value1.value)) + Math.round(future_value((convert_percent(parseFloat(formObj.return_rate.value)) * (1 - convert_percent(parseFloat(formObj.tax_rate.value)))) / parseInt(formObj.num_paychecks.value), (parseInt(formObj.results_savings_age2.value) - parseInt(formObj.results_savings_age1.value)) * parseInt(formObj.num_paychecks.value), (parseFloat(formObj.contribution.value) + parseFloat(formObj.emp_contribution.value)))); formObj.results_401k_age3.value = parseInt(formObj.investment_age.value) + 15; formObj.results_savings_age3.value = parseInt(formObj.investment_age.value) + 15; formObj.results_401k_value3.value = Math.round(parseInt(formObj.results_401k_value2.value) * (Math.pow((1 + convert_percent(formObj.return_rate.value)), (parseInt(formObj.results_401k_age3.value) - parseInt(formObj.results_401k_age2.value))))) + Math.round(future_value(convert_percent(formObj.return_rate.value) / parseInt(formObj.num_paychecks.value), (parseInt(formObj.results_401k_age3.value) - parseInt(formObj.results_401k_age2.value)) * parseInt(formObj.num_paychecks.value), (parseFloat(formObj.contribution.value) + parseFloat(formObj.emp_contribution.value)))); formObj.results_savings_value3.value = Math.round(Math.pow((((1 - convert_percent(parseFloat(formObj.tax_rate.value))) * convert_percent(parseFloat(formObj.return_rate.value))) + 1), (parseInt(formObj.results_savings_age3.value) - parseInt(formObj.results_savings_age2.value))) * parseFloat(formObj.results_savings_value2.value)) + Math.round(future_value((convert_percent(parseFloat(formObj.return_rate.value)) * (1 - convert_percent(parseFloat(formObj.tax_rate.value)))) / parseInt(formObj.num_paychecks.value), (parseInt(formObj.results_savings_age3.value) - parseInt(formObj.results_savings_age2.value)) * parseInt(formObj.num_paychecks.value), (parseFloat(formObj.contribution.value) + parseFloat(formObj.emp_contribution.value)))); formObj.results_401k_age4.value = formObj.retirement_age.value; formObj.results_savings_age4.value = formObj.retirement_age.value; formObj.results_401k_value4.value = Math.round(parseInt(formObj.results_401k_value3.value) * (Math.pow((1 + convert_percent(formObj.return_rate.value)), (parseInt(formObj.results_401k_age4.value) - parseInt(formObj.results_401k_age3.value))))) + Math.round(future_value(convert_percent(formObj.return_rate.value) / parseInt(formObj.num_paychecks.value), (parseInt(formObj.results_401k_age4.value) - parseInt(formObj.results_401k_age3.value)) * parseInt(formObj.num_paychecks.value), (parseFloat(formObj.contribution.value) + parseFloat(formObj.emp_contribution.value)))); formObj.results_savings_value4.value = Math.round(Math.pow((((1 - convert_percent(parseFloat(formObj.tax_rate.value))) * convert_percent(parseFloat(formObj.return_rate.value))) + 1), (parseInt(formObj.results_savings_age4.value) - parseInt(formObj.results_savings_age3.value))) * parseFloat(formObj.results_savings_value3.value)) + Math.round(future_value((convert_percent(parseFloat(formObj.return_rate.value)) * (1 - convert_percent(parseFloat(formObj.tax_rate.value)))) / parseInt(formObj.num_paychecks.value), (parseInt(formObj.results_savings_age4.value) - parseInt(formObj.results_savings_age3.value)) * parseInt(formObj.num_paychecks.value), (parseFloat(formObj.contribution.value) + parseFloat(formObj.emp_contribution.value)))); formObj.monthly_income_401k.value = payment(parseInt(formObj.results_401k_value4.value), convert_percent(parseFloat(formObj.return_rate.value)), ((90 - parseInt(formObj.retirement_age.value)) * 12)); formObj.monthly_income_savings.value = payment(parseInt(formObj.results_savings_value4.value), convert_percent(parseFloat(formObj.return_rate.value)), ((90 - parseInt(formObj.retirement_age.value)) * 12)); } } function reset_results(formObj) { formObj.results_401k_age1.value = ""; formObj.results_401k_age2.value = ""; formObj.results_401k_age3.value = ""; formObj.results_401k_age4.value = ""; formObj.results_401k_value1.value = ""; formObj.results_401k_value2.value = ""; formObj.results_401k_value3.value = ""; formObj.results_401k_value4.value = ""; formObj.results_savings_age1.value = ""; formObj.results_savings_age2.value = ""; formObj.results_savings_age3.value = ""; formObj.results_savings_age4.value = ""; formObj.results_savings_value1.value = ""; formObj.results_savings_value2.value = ""; formObj.results_savings_value3.value = ""; formObj.results_savings_value4.value = ""; formObj.monthly_income_401k.value = ""; formObj.monthly_income_savings.value = ""; } function future_value(int_rate, per_term, payment) { return (((Math.pow((1 + int_rate), per_term) - 1) / int_rate) * payment); } function convert_percent(num) { //converts num to decimal return (parseFloat(num) * 0.01); } function payment(principal, interest, months) { return Math.round(((principal * interest)/ 12) / (1 - (Math.pow(1 / ((interest / 12) + 1), months)))); }