/*
  CalcMyMortgage.com Mortgage Calculator - 1.0

  (c) Dan Egbert (dan at dan-egbert.com)
  http://www.dan-egbert.com
  This is an uncompressed version of mortgage-interest-calculator-cc.js
*/
var mcalc = new mcalculator(); 

$(document).ready(function(){ 
	mcalc.fillDefaults();
	processCalculator();
	calculateDownPayment();
	calculateInsurance();
	calculatePTaxes();
	if (window.location.hash.slice(0, 10) != '#mortgage;' && $.cookie('pmi') == null) { //don't auto-calculate the pmi if it is provided by hash or cookie
		calculatePMI();
	}
	
	// Format currency while typing
	$('#principal').blur(function() {
		$('#principal').html(null);
		$(this).formatCurrency({ colorize: true, negativeFormat: '-%s%n', roundToDecimalPlace: 0 });
	})
	.keyup(function(e) {
		var e = window.event || e;
		var keyUnicode = e.charCode || e.keyCode;
		if (e !== undefined) {
			switch (keyUnicode) {
				case 27: this.value = ''; break; // Esc
				case 37: break; // cursor left
				case 38: break; // cursor up
				case 39: break; // cursor right
				case 40: break; // cursor down
				case 78: break; // N (Opera 9.63+ maps the "." from the number key section to the "N" key too!) (See: http://unixpapa.com/js/key.html search for ". Del")
				case 110: break; // . number block (Opera 9.63+ maps the "." from the number block to the "N" key (78) !!!)
				case 190: break; // .
				case 65: break; // a (so Ctrl+a can be used)
				case 17: break; // ctrl
				case 16: break; // shift 
				case 9: break; // tab
				default: $(this).formatCurrency({ colorize: true, negativeFormat: '-%s%n', roundToDecimalPlace: -1, eventOnDecimalsEntered: true });
			}
		}
	})
	$('#pmi').blur(function() {
		$('#principal').html(null);
		$(this).formatCurrency({ colorize: true, negativeFormat: '-%s%n', roundToDecimalPlace: 0 });
	})
	.keyup(function(e) {
		var e = window.event || e;
		var keyUnicode = e.charCode || e.keyCode;
		if (e !== undefined) {
			switch (keyUnicode) {
				case 27: this.value = ''; break; // Esc
				case 37: break; // cursor left
				case 38: break; // cursor up
				case 39: break; // cursor right
				case 40: break; // cursor down
				case 78: break; // N (Opera 9.63+ maps the "." from the number key section to the "N" key too!) (See: http://unixpapa.com/js/key.html search for ". Del")
				case 110: break; // . number block (Opera 9.63+ maps the "." from the number block to the "N" key (78) !!!)
				case 190: break; // .
				case 65: break; // a (so Ctrl+a can be used)
				case 17: break; // ctrl
				case 16: break; // shift 
				case 9: break; // tab
				default: $(this).formatCurrency({ colorize: true, negativeFormat: '-%s%n', roundToDecimalPlace: -1, eventOnDecimalsEntered: true });
			}
		}
	})
	
	$('#insurance').blur(function() {
		$('#insurance').html(null);
		$(this).formatCurrency({ colorize: true, negativeFormat: '-%s%n', roundToDecimalPlace: 0 });
	})
	.keyup(function(e) {
		var e = window.event || e;
		var keyUnicode = e.charCode || e.keyCode;
		if (e !== undefined) {
			switch (keyUnicode) {
				case 27: this.value = ''; break; // Esc
				case 37: break; // cursor left
				case 38: break; // cursor up
				case 39: break; // cursor right
				case 40: break; // cursor down
				case 78: break; // N (Opera 9.63+ maps the "." from the number key section to the "N" key too!) (See: http://unixpapa.com/js/key.html search for ". Del")
				case 110: break; // . number block (Opera 9.63+ maps the "." from the number block to the "N" key (78) !!!)
				case 190: break; // .
				case 65: break; // a (so Ctrl+a can be used)
				case 17: break; // ctrl
				case 16: break; // shift 
				case 9: break; // tab
				default: $(this).formatCurrency({ colorize: true, negativeFormat: '-%s%n', roundToDecimalPlace: -1, eventOnDecimalsEntered: true });
			}
		}
	})
	
	$('#ptaxes').blur(function() {
		$('#ptaxes').html(null);
		$(this).formatCurrency({ colorize: true, negativeFormat: '-%s%n', roundToDecimalPlace: 0 });
	})
	.keyup(function(e) {
		var e = window.event || e;
		var keyUnicode = e.charCode || e.keyCode;
		if (e !== undefined) {
			switch (keyUnicode) {
				case 27: this.value = ''; break; // Esc
				case 37: break; // cursor left
				case 38: break; // cursor up
				case 39: break; // cursor right
				case 40: break; // cursor down
				case 78: break; // N (Opera 9.63+ maps the "." from the number key section to the "N" key too!) (See: http://unixpapa.com/js/key.html search for ". Del")
				case 110: break; // . number block (Opera 9.63+ maps the "." from the number block to the "N" key (78) !!!)
				case 190: break; // .
				case 65: break; // a (so Ctrl+a can be used)
				case 17: break; // ctrl
				case 16: break; // shift 
				case 9: break; // tab
				default: $(this).formatCurrency({ colorize: true, negativeFormat: '-%s%n', roundToDecimalPlace: -1, eventOnDecimalsEntered: true });
			}
		}
	})
	
});

function processCalculator() {
	//enter values into mcalc object
    mcalc.principal		=  parseFloat($('#principal').asNumber(), 10); // $
    mcalc.cashdown		=  $('#cashdown').val(); // %
    mcalc.interest		=  parseFloat($('#interest').val(), 10); // %
    mcalc.term			=  parseInt($('#term').val(), 10);     // years
    mcalc.ptaxes		=  parseFloat($('#ptaxes').asNumber(), 10);   // %
    mcalc.insurance		=  parseFloat($('#insurance').asNumber(), 10);    // %
    mcalc.pmi			=  parseFloat($('#pmi').asNumber(), 10);    // $

	mcalc.calculate();
	
	//show results
	//$('#monthly_payment_span').hide().css("fontSize", "0px").text(Math.round(mcalc.monthlyTotal)).formatCurrency().show().animate({ fontSize: "32px" }, 750);
	$('#monthly_payment_span').hide().text(Math.round(mcalc.monthlyTotal)).formatCurrency().fadeIn(1500);
	$('#monthly_principal_td').text(mcalc.monthlySubtotal).formatCurrency();
	$('#monthly_taxes_td').text(mcalc.monthlyPropertyTax).formatCurrency();
	$('#monthly_insurance_td').text(mcalc.monthlyInsurance).formatCurrency();
	$('#monthly_pmi_td').text(mcalc.pmi).formatCurrency();
	//$('#total_payment_span').hide().css("fontSize", "0px").text(Math.round(mcalc.termTotal)).formatCurrency().show().animate({ fontSize: "32px" }, 750);
	$('#total_payment_span').hide().text(Math.round(mcalc.termTotal)).formatCurrency().fadeIn(1500);
	$('#total_principal_td').text(mcalc.termPrincipal).formatCurrency();
	$('#total_interest_td').text(mcalc.termInterestTotal).formatCurrency();
	$('#total_tax_td').text(mcalc.termTaxTotal).formatCurrency();
	$('#total_insurance_td').text(mcalc.termInsuranceTotal).formatCurrency();
	$('#total_pmi_td').text(mcalc.termPmiTotal).formatCurrency();
	$('#total_payment_description').hide().text("Total of "+mcalc.monthlyPeriods+" Payments").fadeIn();
	$('#payoff_date_td').text(formatDate(mcalc.payOffDate, "MMM, y")).fadeIn();
	mcalc.refreshChart();
	
	//save info in cookies
	$.cookie('principal', mcalc.principal, { expires: 360 });
	$.cookie('cashdown', mcalc.cashdown, { expires: 360 });
	$.cookie('interest', mcalc.interest, { expires: 360 });
	$.cookie('term', mcalc.term, { expires: 360 });
	$.cookie('ptaxes', mcalc.ptaxes, { expires: 360 });
	$.cookie('insurance', mcalc.insurance, { expires: 360 });
	$.cookie('pmi', mcalc.pmi, { expires: 360 });
	
	//update hash in address bar so it can be bookmarked or emailed
	var hash = '#mortgage;'+mcalc.principal+'-'+mcalc.cashdown+'-'+mcalc.interest+'-'+mcalc.term+'-'+mcalc.insurance+'-'+mcalc.ptaxes+'-'+mcalc.pmi;
	window.location.hash = hash;
}

function calculateDownPayment() {
	principal = parseFloat($('#principal').asNumber(), 10);
	cashdownPercentage = parseFloat($('#cashdown').val(), 10);
	var cashdownValue = parseFloat((cashdownPercentage / 100) * principal, 10);
	$('#cashdownValue').hide().text(cashdownValue).formatCurrency({roundToDecimalPlace:0}).fadeIn();
}

function calculateInsurance() {
	principal = parseFloat($('#principal').asNumber(), 10);
	insuranceCost = parseFloat($('#insurance').asNumber(), 10);
	var insurancePercentage = parseFloat(insuranceCost / principal * 100, 10).toFixed(2);
	$('#insuranceValue').hide().text(insurancePercentage+"%").fadeIn();
}

function calculatePTaxes() {
	principal = parseFloat($('#principal').asNumber(), 10);
	ptaxesCost = parseFloat($('#ptaxes').asNumber(), 10);
	var ptaxesPercentage = parseFloat(ptaxesCost / principal * 100, 10).toFixed(2);
	$('#ptaxesValue').hide().text(ptaxesPercentage+"%").fadeIn();
}

function calculatePMI() {
	cashdown = $('#cashdown').val();
	if (isNaN(cashdown)) {
		return 0;
	}
	if (cashdown >= 20) {
		pmi = 0;
		$('#pmi').attr("disabled", true);
	} else {
		if (cashdown <= 5) { factor=1500; }
		else if (cashdown <= 10) { factor=2300;}
		else if (cashdown < 20) { factor=3700;}
		pmi = parseInt(this.principal/factor+0.5);
		$('#pmi').removeAttr("disabled");
	}
	
	$('#pmi').val(pmi).formatCurrency({roundToDecimalPlace:0});
}

// define the Calculator Class
function mcalculator() {
	//default values
    this.principal		= '$250,000'; // $
    this.cashdown		= '10'; // %
    this.interest		= parseFloat('5.50', 10); // %
    this.term			= parseInt(30, 10);     // years
    this.ptaxes			= parseFloat('3000', 10);   // $
    this.insurance		= parseFloat('1250', 10);    // $
    this.pmi			= '$109';    // $
	
	mcalculator.prototype.fillDefaults = function() {
		//first checks for default values in the bookmark hash ({in the URL after #), then in the cookies. If neither are provided, it uses hard coded defaults above
		if (window.location.hash.slice(0, 10) == '#mortgage;') { //check the bookmark hash
			var h = window.location.hash.slice(10).split('-');
			p 	= h[0];
			c 	= h[1];
			i 	= h[2];
			t 	= h[3];
			ins = h[4];
			pt	= h[5];
			pmi	= h[6];
		} else if ($.cookie('principal') != null) { //check for cookies
			p 	= $.cookie('principal');
			c 	= $.cookie('cashdown');
			i 	= $.cookie('interest');
			t 	= $.cookie('term');
			ins = $.cookie('insurance');
			pt	= $.cookie('ptaxes');
			pmi	= $.cookie('pmi');
			
		} else { //use defaults
			p 	= this.principal;
			c 	= this.cashdown;
			i 	= this.interest;
			t 	= this.term;
			ins = this.insurance;
			pt	= this.ptaxes;
			pmi	= this.pmi;
		}
				
		$('#principal').val(p).formatCurrency({roundToDecimalPlace:0});
		$('#cashdown').val(c);
		$('#interest').val(i);
		$('#term').val(t);
		$('#insurance').val(ins).formatCurrency({roundToDecimalPlace:0});
		$('#ptaxes').val(pt).formatCurrency({roundToDecimalPlace:0});
		$('#pmi').val(pmi).formatCurrency({roundToDecimalPlace:0});
	}
	
	mcalculator.prototype.calculate = function() {
		this.monthlyInterest		= this.interest / 100 / 12;
		this.monthlyPeriods			= this.term*12;
		this.monthlyPropertyTax		= this.ptaxes / 12;
		this.monthlyInsurance		= this.insurance / 12;
		
		var p = this.principal - (this.principal * this.cashdown/100);
		this.loanAmount				= p;
	
		this.monthlySubtotal		= parseFloat((p * Math.pow(1 + this.monthlyInterest, this.monthlyPeriods) * this.monthlyInterest) / (Math.pow(1 + this.monthlyInterest, this.monthlyPeriods) -1), 10);
		this.monthlyTotal  		 	= parseFloat(this.monthlySubtotal + this.monthlyPropertyTax + this.monthlyInsurance + this.pmi, 10);
		this.termTotal				= parseFloat(this.monthlyTotal * 12 * this.term, 10);
		this.termPrincipal			= p;
		this.termInterestTotal		= parseFloat(this.monthlySubtotal * this.monthlyPeriods - this.termPrincipal, 10);
		this.termTaxTotal			= parseFloat(this.monthlyPropertyTax * this.monthlyPeriods);
		this.termInsuranceTotal		= parseFloat(this.monthlyInsurance * this.monthlyPeriods);
		//figure out PMI
		this.generateAmortData();
		for (var x = 0; x < this.amortTableData.length;x++) {
			var r = this.amortTableData[x];
			var percentPayedOff = (this.principal - r.remainingBalance) / this.principal;
			if (percentPayedOff > .20) {
				var monthsTillNoPMI = r.month;
				break;
			}
		}
		//var monthsTillNoPMI 		= (this.termPrincipal * (0.2 - (this.cashdown/100))) / (0.2 * this.monthlySubtotal); //very rough calculation to stop counting PMI after 20% is payed
		this.termPmiTotal		 	= parseFloat(this.pmi * monthsTillNoPMI, 10);
		this.payOffDate = new Date();
		this.payOffDate.setYear(this.payOffDate.getFullYear() + this.term);
    }	
    
	mcalculator.prototype.refreshChart = function() {
		//update balance chart
		var total	= (this.termTaxTotal+this.termInsuranceTotal+this.termPmiTotal)+this.termInterestTotal+this.termPrincipal;
		var principalPercent		= this.termPrincipal / total;
		var interestPercent			= this.termInterestTotal / total;
		var otherPercent			= (this.termTaxTotal+this.termInsuranceTotal+this.termPmiTotal) / total;
		var newBalanaceImageSrc		= "http://chart.apis.google.com/chart?chs=290x120&cht=p3&chco=FF1F1F,8989FF,FFD631&chma=0,0,0,0|0,0&chl=Principal%7CInterest%7COther&chf=bg,s,FFFFFF&&chd=t:"+principalPercent+","+interestPercent+","+otherPercent;
		$("#balanceChart").hide().attr("src", newBalanaceImageSrc).fadeIn();
		
		//update amortization chart
		this.generateAmortData();
		var o = {i:[], p:[]};
		var p = this.termPrincipal;
		var term = this.term;
		for (var x = 0; x < this.amortTableData.length;x++) {
			var r = this.amortTableData[x];
			
			o.p.push(Math.round((r.principal * 12 / p) * 100 * 10));
			o.i.push(Math.round((r.interest  * 12 / p) * 100 * 10));
			x = x + 12;
		}
		var xRange = $.map($.range(0, term+5, term/5), function(i){ return (new Date()).getFullYear() + i; }).join('|');
		var yRange = $.range(0, (Math.round(p/100000)*100000)+1, 100000).join('|');
		var chd		= $.format('t:{0:s}|{1:s}', o.p.join(','), o.i.join(','));
		var chxl	= $.format('0:|{0:s}|1:|{1:s}', xRange, yRange);
		var newAmortImageSrc = "http://chart.apis.google.com/chart?chs=270x160&cht=lc&chco=FF1F1F,FFD631&chma=0,0,0,20|80,20&chdl=Principal|Interest&chxt=x,y&chg=20,50,1,5&chf=c,lg,45,FFFFFF,0,76A4FB,0.75|bg,s,FFFFFF&chm=D,FF1F1F,0,0,2|D,FFD631,1,0,2&chdlp=b&chd="+chd+"&chxl="+chxl;
		$("#amortChart").hide().attr("src", newAmortImageSrc).fadeIn();
    }	
    
    //generates amortization data and displays popup window with it
	mcalculator.prototype.amortize = function() {
		/* 
			M: selected month
			Y: selected year
	
			A: monthly payment
			P: loan amount
			i: interest
			n: loan period
			b: baloon start month 
	
			j : current month
			LP: remaining loan amount
			PP: loan amount paid this month
			II: interest paid this month
			Nn: months remaining
			sd: start date of loan
			py: payments per year
	
		*/
		var width = 600;
		var height = 550;
		var left = parseInt((screen.availWidth/2) - (width/2));
		var top = parseInt((screen.availHeight/2) - (height/2));
		var as = window.open('', 'amortizationWindow' ,'scrollbars=yes,top='+top+',left='+left+',width='+width+',height='+height);
		as.focus();
		var A,P,i,n,b;
		var j,LP,PP,II,Nn,sd,py;
		var txt = '';
		var d = new Date();
		M = Number(d.getMonth());
		Y = Number(d.getFullYear());
		A = Number(this.monthlySubtotal);
		P = Number(this.loanAmount);
		i = Number(this.interest);
		n = Number(this.term);
		LP = P;
		n = n*12;	
		Nn = n;
		sd = new Date(Y,M,0,0,0,0,0);
		py = 12;
		txt+='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"\n 	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\n <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">\n <head>\n <title>Amortization Table</title>\n <link rel="stylesheet" type="text/css" href="css/style-amortization.css" /> </head> <body>';
		txt+='<div class="mainContainer" id="mainContainer" style="text-align:center;"><h2 class="mainTitle" id="mainTitle">Mortgage Amortization</h2><div>';
		txt+='<table id="gradient-style" summary="Amortization Table">';
		txt+='<thead>';
		txt+='<tr><th scope="col">Month</th><th scope="col">Date</th><th scope="col">Payment</th><th scope="col">Interest</th><th scope="col">Principal</th><th scope="col">Balance</th></tr>';
		txt+='</thead>';
		txt+='<tfoot><tr><td colspan="6"><input type="button" value="Close the Window" onClick="self.close()" /><input type="button" value="Print this page" onclick="window.print();return false;" /></td></tr></tfoot>';
		txt+='<tbody>';
		var m;	
		for(j=1;j<=Nn;j++){
			sd.setMonth(sd.getMonth()+1);
			II=LP*i/py/100;
			LP+=II;
			PP=A-II;
			LP-=A;
			m = sd.getMonth( );
			if (!m) {
				m = 12;
			}
			var amortTableRow = new Array();
			amortTableRow["principal"]	= PP;
			amortTableRow["interest"]	= II;
			this.amortTableData.push(amortTableRow);
			txt+='<tr>';
			txt+='<td>'+j+"</td>";
			txt+='<td>'+(m)+'/'+(sd.getFullYear( ))+'</td>';
			txt+='<td>'+convertVariableToMoney(A)+'</td>';
			txt+='<td>'+convertVariableToMoney(II)+'</td>';
			txt+='<td>'+convertVariableToMoney(PP)+'</td>';
			txt+='<td>'+convertVariableToMoney(LP)+'</td></tr>';
			as.document.write(txt + "\n");
			txt="";
		}
		txt+='</tbody>';
		txt+='</table>';
		txt+='</div></body></html>';
		as.document.write(txt);
		as.document.close();
	}

	
	mcalculator.prototype.generateAmortData = function() { //duplicate of the amortize function but just generates the data
		/* 
			M: selected month
			Y: selected year
	
			A: monthly payment
			P: loan amount
			i: interest
			n: loan period
			b: baloon start month 
	
			j : current month
			LP: remaining loan amount
			PP: loan amount paid this month
			II: interest paid this month
			Nn: months remaining
			sd: start date of loan
			py: payments per year
	
		*/
		this.amortTableData = new Array();
		var A,P,i,n,b;
		var j,LP,PP,II,Nn,sd,py;
		var txt = '';
		var d = new Date();
		M = Number(d.getMonth());
		Y = Number(d.getFullYear());
		A = Number(this.monthlySubtotal);
		P = Number(this.loanAmount);
		i = Number(this.interest);
		n = Number(this.term);
		LP = P;
		n = n*12;	
		Nn = n;
		sd = new Date(Y,M,0,0,0,0,0);
		py = 12;
		var m;	
		for(j=1;j<=Nn;j++){
			sd.setMonth(sd.getMonth()+1);
			II=LP*i/py/100;
			LP+=II;
			PP=A-II;
			LP-=A;
			m = sd.getMonth( );
			if (!m) {
				m = 12;
			}
			var amortTableRow = new Array();
			amortTableRow["month"]	= j;
			amortTableRow["remainingBalance"] = LP;
			amortTableRow["principal"]	= PP;
			amortTableRow["interest"]	= II;
			this.amortTableData.push(amortTableRow);
		}
	}
    
    
}

function convertVariableToMoney(variable) {
	if (variable==null) {
		return variable;
	}
	else {
		variable = Math.round(variable * 100) / 100;
		variable = commify(variable);
		return variable;
	}
}

function commify(variable) {
	/*
		Commify function not copyright Steven Alyari.  Instead:

		This JavaScript takes an input number and adds commas in the proper places.
		As needed by its original purpose, also adds a dollar.
		Copyright 1998, David Turley <dturley@pobox.com>
		Feel free to use and build on this code as long as you include this notice.
		Last Modified March 3, 1998 

		Newly revised by Steven Alyari: 2004
	*/
	var Num = String(variable);
	var newNum = "";
	var newNum2 = "";
	var count = 0;

	//check for decimal number
	if (Num.indexOf('.') != -1){ //number ends with a decimal point
	if (Num.indexOf('.') == Num.length-1){
		Num += "00";
	}
	if (Num.indexOf('.') == Num.length-2){ //number ends with a single digit
		Num += "0";
	}
	var a = Num.split(".");
	Num = a[0]; //the part we will commify
	var end = a[1] //the decimal place we will ignore and add back later
	}
	else {var end = "00";}

	//this loop actually adds the commas
	for (var k = Num.length-1; k >= 0; k--){
		var oneChar = Num.charAt(k);
		if (count == 3){
			newNum += ",";
			newNum += oneChar;
			count = 1;
			continue;
		}
		else {
			newNum += oneChar;
			count ++;
		}
	} //but now the string is reversed!
	//re-reverse the string
	for (var k = newNum.length-1; k >= 0; k--){
		var oneChar = newNum.charAt(k);
		newNum2 += oneChar;
	}

	// add dollar sign and decimal ending from above
	newNum2 = "$" + newNum2 + "." + end;
	return newNum2;	
}


//  This file is part of the jQuery formatCurrency Plugin.
//
//    The jQuery formatCurrency Plugin 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 3 of the License, or
//    (at your option) any later version.

//    The jQuery formatCurrency Plugin 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 
//    the jQuery formatCurrency Plugin.  If not, see <http://www.gnu.org/licenses/>.

(function($) {

	$.formatCurrency = {};

	$.formatCurrency.regions = [];

	// default Region is en
	$.formatCurrency.regions[''] = {
		symbol: '$',
		positiveFormat: '%s%n',
		negativeFormat: '(%s%n)',
		decimalSymbol: '.',
		digitGroupSymbol: ',',
		groupDigits: true
	};

	$.fn.formatCurrency = function(destination, settings) {

		if (arguments.length == 1 && typeof destination !== "string") {
			settings = destination;
			destination = false;
		}

		// initialize defaults
		var defaults = {
			name: "formatCurrency",
			colorize: false,
			region: '',
			global: true,
			roundToDecimalPlace: 2, // roundToDecimalPlace: -1; for no rounding; 0 to round to the dollar; 1 for one digit cents; 2 for two digit cents; 3 for three digit cents; ...
			eventOnDecimalsEntered: false
		};
		// initialize default region
		defaults = $.extend(defaults, $.formatCurrency.regions['']);
		// override defaults with settings passed in
		settings = $.extend(defaults, settings);

		// check for region setting
		if (settings.region.length > 0) {
			settings = $.extend(settings, getRegionOrCulture(settings.region));
		}
		settings.regex = generateRegex(settings);

		return this.each(function() {
			$this = $(this);

			// get number
			var num = '0';
			num = $this[$this.is('input, select, textarea') ? 'val' : 'html']();

			//identify '(123)' as a negative number
			if (num.search('\\(') >= 0) {
				num = '-' + num;
			}

			if (num === '') {
				return;
			}

			// if the number is valid use it, otherwise clean it
			if (isNaN(num)) {
				// clean number
				num = num.replace(settings.regex, '');
				
				if (num === '') {
					return;
				}
				
				if (settings.decimalSymbol != '.') {
					num = num.replace(settings.decimalSymbol, '.');  // reset to US decimal for arithmetic
				}
				if (isNaN(num)) {
					num = '0';
				}
			}
			
			// evalutate number input
			var numParts = String(num).split('.');
			var isPositive = (num == Math.abs(num));
			var hasDecimals = (numParts.length > 1);
			var decimals = (hasDecimals ? numParts[1].toString() : '0');
			var originalDecimals = decimals;
			
			// format number
			num = Math.abs(numParts[0]);
			if (settings.roundToDecimalPlace >= 0) {
				decimals = parseFloat('1.' + decimals); // prepend "0."; (IE does NOT round 0.50.toFixed(0) up, but (1+0.50).toFixed(0)-1
				decimals = decimals.toFixed(settings.roundToDecimalPlace); // round
				if (decimals.substring(0, 1) == '2') {
					num = Number(num) + 1;
				}
				decimals = decimals.substring(2); // remove "0."
			}
			num = String(num);

			if (settings.groupDigits) {
				for (var i = 0; i < Math.floor((num.length - (1 + i)) / 3); i++) {
					num = num.substring(0, num.length - (4 * i + 3)) + settings.digitGroupSymbol + num.substring(num.length - (4 * i + 3));
				}
			}

			if ((hasDecimals && settings.roundToDecimalPlace == -1) || settings.roundToDecimalPlace > 0) {
				num += settings.decimalSymbol + decimals;
			}

			// format symbol/negative
			var format = isPositive ? settings.positiveFormat : settings.negativeFormat;
			var money = format.replace(/%s/g, settings.symbol);
			money = money.replace(/%n/g, num);

			// setup destination
			var $destination = $([]);
			if (!destination) {
				$destination = $this;
			} else {
				$destination = $(destination);
			}
			// set destination
			$destination[$destination.is('input, select, textarea') ? 'val' : 'html'](money);

			if (hasDecimals && settings.eventOnDecimalsEntered) {
				$destination.trigger('decimalsEntered', originalDecimals);
			}

			// colorize
			if (settings.colorize) {
				$destination.css('color', isPositive ? 'black' : 'red');
			}
		});
	};

	// Remove all non numbers from text
	$.fn.toNumber = function(settings) {
		var defaults = $.extend({
			name: "toNumber",
			region: '',
			global: true
		}, $.formatCurrency.regions['']);

		settings = jQuery.extend(defaults, settings);
		if (settings.region.length > 0) {
			settings = $.extend(settings, getRegionOrCulture(settings.region));
		}
		settings.regex = generateRegex(settings);

		return this.each(function() {
			var method = $(this).is('input, select, textarea') ? 'val' : 'html';
			$(this)[method]($(this)[method]().replace('(', '(-').replace(settings.regex, ''));
		});
	};

	// returns the value from the first element as a number
	$.fn.asNumber = function(settings) {
		var defaults = $.extend({
			name: "asNumber",
			region: '',
			parse: true,
			parseType: 'Float',
			global: true
		}, $.formatCurrency.regions['']);
		settings = jQuery.extend(defaults, settings);
		if (settings.region.length > 0) {
			settings = $.extend(settings, getRegionOrCulture(settings.region));
		}
		settings.regex = generateRegex(settings);
		settings.parseType = validateParseType(settings.parseType);

		var method = $(this).is('input, select, textarea') ? 'val' : 'html';
		var num = $(this)[method]();
		num = num ? num : "";
		num = num.replace('(', '(-');
		num = num.replace(settings.regex, '');
		if (!settings.parse) {
			return num;
		}

		if (num.length == 0) {
			num = '0';
		}

		if (settings.decimalSymbol != '.') {
			num = num.replace(settings.decimalSymbol, '.');  // reset to US decimal for arthmetic
		}

		return window['parse' + settings.parseType](num);
	};

	function getRegionOrCulture(region) {
		var regionInfo = $.formatCurrency.regions[region];
		if (regionInfo) {
			return regionInfo;
		}
		else {
			if (/(\w+)-(\w+)/g.test(region)) {
				var culture = region.replace(/(\w+)-(\w+)/g, "$1");
				return $.formatCurrency.regions[culture];
			}
		}
		// fallback to extend(null) (i.e. nothing)
		return null;
	}

	function validateParseType(parseType) {
		switch (parseType.toLowerCase()) {
			case 'int':
				return 'Int';
			case 'float':
				return 'Float';
			default:
				throw 'invalid parseType';
		}
	}
	
	function generateRegex(settings) {
		if (settings.symbol === '') {
			return new RegExp("[^\\d" + settings.decimalSymbol + "-]", "g");
		}
		else {
			var symbol = settings.symbol.replace('$', '\\$').replace('.', '\\.');		
			return new RegExp(symbol + "|[^\\d" + settings.decimalSymbol + "-]", "g");
		}	
	}

})(jQuery);

// ===================================================================
// Author: Matt Kruse <matt@mattkruse.com>
// WWW: http://www.mattkruse.com/
//
// NOTICE: You may use this code for any purpose, commercial or
// private, without any further permission from the author. You may
// remove this notice from your final code if you wish, however it is
// appreciated by the author if at least my web site address is kept.
//
// You may *NOT* re-distribute this code in any way except through its
// use. That means, you can include it in your product, or your web
// site, or any other form where the code is actually being used. You
// may not put the plain javascript up on your site for download or
// include it in your javascript libraries for download. 
// If you wish to share this code with others, please just point them
// to the URL instead.
// Please DO NOT link directly to my .js files from your site. Copy
// the files to your server and use them there. Thank you.
// ===================================================================

// HISTORY
// ------------------------------------------------------------------
// May 17, 2003: Fixed bug in parseDate() for dates <1970
// March 11, 2003: Added parseDate() function
// March 11, 2003: Added "NNN" formatting option. Doesn't match up
//                 perfectly with SimpleDateFormat formats, but 
//                 backwards-compatability was required.

// ------------------------------------------------------------------
// These functions use the same 'format' strings as the 
// java.text.SimpleDateFormat class, with minor exceptions.
// The format string consists of the following abbreviations:
// 
// Field        | Full Form          | Short Form
// -------------+--------------------+-----------------------
// Year         | yyyy (4 digits)    | yy (2 digits), y (2 or 4 digits)
// Month        | MMM (name or abbr.)| MM (2 digits), M (1 or 2 digits)
//              | NNN (abbr.)        |
// Day of Month | dd (2 digits)      | d (1 or 2 digits)
// Day of Week  | EE (name)          | E (abbr)
// Hour (1-12)  | hh (2 digits)      | h (1 or 2 digits)
// Hour (0-23)  | HH (2 digits)      | H (1 or 2 digits)
// Hour (0-11)  | KK (2 digits)      | K (1 or 2 digits)
// Hour (1-24)  | kk (2 digits)      | k (1 or 2 digits)
// Minute       | mm (2 digits)      | m (1 or 2 digits)
// Second       | ss (2 digits)      | s (1 or 2 digits)
// AM/PM        | a                  |
//
// NOTE THE DIFFERENCE BETWEEN MM and mm! Month=MM, not mm!
// Examples:
//  "MMM d, y" matches: January 01, 2000
//                      Dec 1, 1900
//                      Nov 20, 00
//  "M/d/yy"   matches: 01/20/00
//                      9/2/00
//  "MMM dd, yyyy hh:mm:ssa" matches: "January 01, 2000 12:30:45AM"
// ------------------------------------------------------------------

var MONTH_NAMES=new Array('January','February','March','April','May','June','July','August','September','October','November','December','Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
var DAY_NAMES=new Array('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sun','Mon','Tue','Wed','Thu','Fri','Sat');
function LZ(x) {return(x<0||x>9?"":"0")+x}

// ------------------------------------------------------------------
// isDate ( date_string, format_string )
// Returns true if date string matches format of format string and
// is a valid date. Else returns false.
// It is recommended that you trim whitespace around the value before
// passing it to this function, as whitespace is NOT ignored!
// ------------------------------------------------------------------
function isDate(val,format) {
	var date=getDateFromFormat(val,format);
	if (date==0) { return false; }
	return true;
	}

// -------------------------------------------------------------------
// compareDates(date1,date1format,date2,date2format)
//   Compare two date strings to see which is greater.
//   Returns:
//   1 if date1 is greater than date2
//   0 if date2 is greater than date1 of if they are the same
//  -1 if either of the dates is in an invalid format
// -------------------------------------------------------------------
function compareDates(date1,dateformat1,date2,dateformat2) {
	var d1=getDateFromFormat(date1,dateformat1);
	var d2=getDateFromFormat(date2,dateformat2);
	if (d1==0 || d2==0) {
		return -1;
		}
	else if (d1 > d2) {
		return 1;
		}
	return 0;
	}

// ------------------------------------------------------------------
// formatDate (date_object, format)
// Returns a date in the output format specified.
// The format string uses the same abbreviations as in getDateFromFormat()
// ------------------------------------------------------------------
function formatDate(date,format) {
	format=format+"";
	var result="";
	var i_format=0;
	var c="";
	var token="";
	var y=date.getYear()+"";
	var M=date.getMonth()+1;
	var d=date.getDate();
	var E=date.getDay();
	var H=date.getHours();
	var m=date.getMinutes();
	var s=date.getSeconds();
	var yyyy,yy,MMM,MM,dd,hh,h,mm,ss,ampm,HH,H,KK,K,kk,k;
	// Convert real date parts into formatted versions
	var value=new Object();
	if (y.length < 4) {y=""+(y-0+1900);}
	value["y"]=""+y;
	value["yyyy"]=y;
	value["yy"]=y.substring(2,4);
	value["M"]=M;
	value["MM"]=LZ(M);
	value["MMM"]=MONTH_NAMES[M-1];
	value["NNN"]=MONTH_NAMES[M+11];
	value["d"]=d;
	value["dd"]=LZ(d);
	value["E"]=DAY_NAMES[E+7];
	value["EE"]=DAY_NAMES[E];
	value["H"]=H;
	value["HH"]=LZ(H);
	if (H==0){value["h"]=12;}
	else if (H>12){value["h"]=H-12;}
	else {value["h"]=H;}
	value["hh"]=LZ(value["h"]);
	if (H>11){value["K"]=H-12;} else {value["K"]=H;}
	value["k"]=H+1;
	value["KK"]=LZ(value["K"]);
	value["kk"]=LZ(value["k"]);
	if (H > 11) { value["a"]="PM"; }
	else { value["a"]="AM"; }
	value["m"]=m;
	value["mm"]=LZ(m);
	value["s"]=s;
	value["ss"]=LZ(s);
	while (i_format < format.length) {
		c=format.charAt(i_format);
		token="";
		while ((format.charAt(i_format)==c) && (i_format < format.length)) {
			token += format.charAt(i_format++);
			}
		if (value[token] != null) { result=result + value[token]; }
		else { result=result + token; }
		}
	return result;
	}
	
// ------------------------------------------------------------------
// Utility functions for parsing in getDateFromFormat()
// ------------------------------------------------------------------
function _isInteger(val) {
	var digits="1234567890";
	for (var i=0; i < val.length; i++) {
		if (digits.indexOf(val.charAt(i))==-1) { return false; }
		}
	return true;
	}
function _getInt(str,i,minlength,maxlength) {
	for (var x=maxlength; x>=minlength; x--) {
		var token=str.substring(i,i+x);
		if (token.length < minlength) { return null; }
		if (_isInteger(token)) { return token; }
		}
	return null;
	}
	
// ------------------------------------------------------------------
// getDateFromFormat( date_string , format_string )
//
// This function takes a date string and a format string. It matches
// If the date string matches the format string, it returns the 
// getTime() of the date. If it does not match, it returns 0.
// ------------------------------------------------------------------
function getDateFromFormat(val,format) {
	val=val+"";
	format=format+"";
	var i_val=0;
	var i_format=0;
	var c="";
	var token="";
	var token2="";
	var x,y;
	var now=new Date();
	var year=now.getYear();
	var month=now.getMonth()+1;
	var date=1;
	var hh=now.getHours();
	var mm=now.getMinutes();
	var ss=now.getSeconds();
	var ampm="";
	
	while (i_format < format.length) {
		// Get next token from format string
		c=format.charAt(i_format);
		token="";
		while ((format.charAt(i_format)==c) && (i_format < format.length)) {
			token += format.charAt(i_format++);
			}
		// Extract contents of value based on format token
		if (token=="yyyy" || token=="yy" || token=="y") {
			if (token=="yyyy") { x=4;y=4; }
			if (token=="yy")   { x=2;y=2; }
			if (token=="y")    { x=2;y=4; }
			year=_getInt(val,i_val,x,y);
			if (year==null) { return 0; }
			i_val += year.length;
			if (year.length==2) {
				if (year > 70) { year=1900+(year-0); }
				else { year=2000+(year-0); }
				}
			}
		else if (token=="MMM"||token=="NNN"){
			month=0;
			for (var i=0; i<MONTH_NAMES.length; i++) {
				var month_name=MONTH_NAMES[i];
				if (val.substring(i_val,i_val+month_name.length).toLowerCase()==month_name.toLowerCase()) {
					if (token=="MMM"||(token=="NNN"&&i>11)) {
						month=i+1;
						if (month>12) { month -= 12; }
						i_val += month_name.length;
						break;
						}
					}
				}
			if ((month < 1)||(month>12)){return 0;}
			}
		else if (token=="EE"||token=="E"){
			for (var i=0; i<DAY_NAMES.length; i++) {
				var day_name=DAY_NAMES[i];
				if (val.substring(i_val,i_val+day_name.length).toLowerCase()==day_name.toLowerCase()) {
					i_val += day_name.length;
					break;
					}
				}
			}
		else if (token=="MM"||token=="M") {
			month=_getInt(val,i_val,token.length,2);
			if(month==null||(month<1)||(month>12)){return 0;}
			i_val+=month.length;}
		else if (token=="dd"||token=="d") {
			date=_getInt(val,i_val,token.length,2);
			if(date==null||(date<1)||(date>31)){return 0;}
			i_val+=date.length;}
		else if (token=="hh"||token=="h") {
			hh=_getInt(val,i_val,token.length,2);
			if(hh==null||(hh<1)||(hh>12)){return 0;}
			i_val+=hh.length;}
		else if (token=="HH"||token=="H") {
			hh=_getInt(val,i_val,token.length,2);
			if(hh==null||(hh<0)||(hh>23)){return 0;}
			i_val+=hh.length;}
		else if (token=="KK"||token=="K") {
			hh=_getInt(val,i_val,token.length,2);
			if(hh==null||(hh<0)||(hh>11)){return 0;}
			i_val+=hh.length;}
		else if (token=="kk"||token=="k") {
			hh=_getInt(val,i_val,token.length,2);
			if(hh==null||(hh<1)||(hh>24)){return 0;}
			i_val+=hh.length;hh--;}
		else if (token=="mm"||token=="m") {
			mm=_getInt(val,i_val,token.length,2);
			if(mm==null||(mm<0)||(mm>59)){return 0;}
			i_val+=mm.length;}
		else if (token=="ss"||token=="s") {
			ss=_getInt(val,i_val,token.length,2);
			if(ss==null||(ss<0)||(ss>59)){return 0;}
			i_val+=ss.length;}
		else if (token=="a") {
			if (val.substring(i_val,i_val+2).toLowerCase()=="am") {ampm="AM";}
			else if (val.substring(i_val,i_val+2).toLowerCase()=="pm") {ampm="PM";}
			else {return 0;}
			i_val+=2;}
		else {
			if (val.substring(i_val,i_val+token.length)!=token) {return 0;}
			else {i_val+=token.length;}
			}
		}
	// If there are any trailing characters left in the value, it doesn't match
	if (i_val != val.length) { return 0; }
	// Is date valid for month?
	if (month==2) {
		// Check for leap year
		if ( ( (year%4==0)&&(year%100 != 0) ) || (year%400==0) ) { // leap year
			if (date > 29){ return 0; }
			}
		else { if (date > 28) { return 0; } }
		}
	if ((month==4)||(month==6)||(month==9)||(month==11)) {
		if (date > 30) { return 0; }
		}
	// Correct hours value
	if (hh<12 && ampm=="PM") { hh=hh-0+12; }
	else if (hh>11 && ampm=="AM") { hh-=12; }
	var newdate=new Date(year,month-1,date,hh,mm,ss);
	return newdate.getTime();
	}

// ------------------------------------------------------------------
// parseDate( date_string [, prefer_euro_format] )
//
// This function takes a date string and tries to match it to a
// number of possible date formats to get the value. It will try to
// match against the following international formats, in this order:
// y-M-d   MMM d, y   MMM d,y   y-MMM-d   d-MMM-y  MMM d
// M/d/y   M-d-y      M.d.y     MMM-d     M/d      M-d
// d/M/y   d-M-y      d.M.y     d-MMM     d/M      d-M
// A second argument may be passed to instruct the method to search
// for formats like d/M/y (european format) before M/d/y (American).
// Returns a Date object or null if no patterns match.
// ------------------------------------------------------------------
function parseDate(val) {
	var preferEuro=(arguments.length==2)?arguments[1]:false;
	generalFormats=new Array('y-M-d','MMM d, y','MMM d,y','y-MMM-d','d-MMM-y','MMM d');
	monthFirst=new Array('M/d/y','M-d-y','M.d.y','MMM-d','M/d','M-d');
	dateFirst =new Array('d/M/y','d-M-y','d.M.y','d-MMM','d/M','d-M');
	var checkList=new Array('generalFormats',preferEuro?'dateFirst':'monthFirst',preferEuro?'monthFirst':'dateFirst');
	var d=null;
	for (var i=0; i<checkList.length; i++) {
		var l=window[checkList[i]];
		for (var j=0; j<l.length; j++) {
			d=getDateFromFormat(val,l[j]);
			if (d!=0) { return new Date(d); }
			}
		}
	return null;
	}
	
/**
 * Cookie plugin
 *
 * Copyright (c) 2006 Klaus Hartl (stilbuero.de)
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 *
 */

/**
 * Create a cookie with the given name and value and other optional parameters.
 *
 * @example $.cookie('the_cookie', 'the_value');
 * @desc Set the value of a cookie.
 * @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true });
 * @desc Create a cookie with all available options.
 * @example $.cookie('the_cookie', 'the_value');
 * @desc Create a session cookie.
 * @example $.cookie('the_cookie', null);
 * @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain
 *       used when the cookie was set.
 *
 * @param String name The name of the cookie.
 * @param String value The value of the cookie.
 * @param Object options An object literal containing key/value pairs to provide optional cookie attributes.
 * @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object.
 *                             If a negative value is specified (e.g. a date in the past), the cookie will be deleted.
 *                             If set to null or omitted, the cookie will be a session cookie and will not be retained
 *                             when the the browser exits.
 * @option String path The value of the path atribute of the cookie (default: path of page that created the cookie).
 * @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie).
 * @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will
 *                        require a secure protocol (like HTTPS).
 * @type undefined
 *
 * @name $.cookie
 * @cat Plugins/Cookie
 * @author Klaus Hartl/klaus.hartl@stilbuero.de
 */

/**
 * Get the value of a cookie with the given name.
 *
 * @example $.cookie('the_cookie');
 * @desc Get the value of a cookie.
 *
 * @param String name The name of the cookie.
 * @return The value of the cookie.
 * @type String
 *
 * @name $.cookie
 * @cat Plugins/Cookie
 * @author Klaus Hartl/klaus.hartl@stilbuero.de
 */
jQuery.cookie = function(name, value, options) {
    if (typeof value != 'undefined') { // name and value given, set cookie
        options = options || {};
        if (value === null) {
            value = '';
            options.expires = -1;
        }
        var expires = '';
        if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
            var date;
            if (typeof options.expires == 'number') {
                date = new Date();
                date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
            } else {
                date = options.expires;
            }
            expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
        }
        // CAUTION: Needed to parenthesize options.path and options.domain
        // in the following expressions, otherwise they evaluate to undefined
        // in the packed version for some reason...
        var path = options.path ? '; path=' + (options.path) : '';
        var domain = options.domain ? '; domain=' + (options.domain) : '';
        var secure = options.secure ? '; secure' : '';
        document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
    } else { // only name given, get cookie
        var cookieValue = null;
        if (document.cookie && document.cookie != '') {
            var cookies = document.cookie.split(';');
            for (var i = 0; i < cookies.length; i++) {
                var cookie = jQuery.trim(cookies[i]);
                // Does this cookie string begin with the name we want?
                if (cookie.substring(0, name.length + 1) == (name + '=')) {
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                }
            }
        }
        return cookieValue;
    }
};

