/**
 * @author Luke Cuthbertson <luke.cuthbertons@heathwallace.com>
 * @version 0.6
 * 
 * @projectDescription nwmoneysense budget planner
 */

if(!HW){ var HW = {}}
 
(function($){

	$(function(){
		HW.BudgetPlanner.init();
	}); 

	HW.Util = {
		/**
		 * Formats supplied argument as '0.00'. If value is empty the default is used
		 * if one was supplied. If value is null or no default value supplied an empty
		 * string is returned
		 * @param {Object} val Any object that will evaluate to a float
		 * @param {Object} [def] default value to use if val is empty. Should evaluate to a String.
		 */
		format: function(val, def){
			if (val) {
				var chars = parseFloat(val).toFixed(2).split('');
				val = val.toString();
				for(var i = chars.length - 6; i > ((val.charAt(0) == '-')?1:0); i = i - 3){
					chars.splice(i, 0, ',');
				}
				return chars.join('');
			};
			return (val == null || !def) ? '' : def;
		},
		
		toNumber: function(val){
			//try{return val.toString().replace(/,(?=\d\d\d)/g,'');}catch(e){return null;}
			try{
				return parseFloat(val.toString().replace(/,/g,''));
			}catch(e){return 0;}
		}
	}
	
	/**
	 * Container for BudgetPlanner application. Orchastrates global opperations.
	 */
	HW.BudgetPlanner = {
		
		/*Settings*/
		
		lastConverted: 'weekly', //this radio will be checked by default on first use
		
		defaultValue: '0.00', //of text inputs
		
		validationEndPoint: 'index_AjaxValidation.aspx', //'errors.txt',  //set this to point to validation script
		
		dismissErrorEndPoint: 'Index_AjaxIgnoreList.aspx',
		
		/*end settings*/
		
		animating: false, //internal flag
		
		blocks: [],
		
		init: function(scope){
			
			var obj = this;
			var scope = scope || document;
			function submitHandler(allow){
				if (!allow) {
					return false;
				}
			};
			//stop enter key press submitting form unless it's the submit button
			$('form[name=form1] input:not(#btnCalc)').keypress(function(e){
				return (e.keyCode != 13);
			});
			//find and create blocks
			$('.sector', scope).each(function(i){
				obj.blocks.push(new HW.BudgetPlanner.Block($(this)));
			});
			//this.doGrandTotal();
			$('#btnTotal').hide();
			$(document).click(function(){HW.BudgetPlanner.hideIcons();});
			
			//////////////////////////////////////////////////////////////
			//UNCOMMENT THIS TO ENABLE THE JS VERSION OF THE OVERLAY	//
			//VALIDATE IS THE 2ND TO LAST MEMBER OF THIS OBJECT			//
			//////////////////////////////////////////////////////////////
			/*
			$('#btnCalc').click(function(){
				return obj.validate($(this).parents('form'));
			})
			*/
			
			
			var xpButtonsState = 'expand';
			$('#btnExpandCollapse').click(function(){
				if (xpButtonsState == 'expand') {
					obj.openAllBlocks();
					xpButtonsState = 'collapse';
					$(this).attr('src', '../Images/imgInputs/all-close.gif');
				}
				else{
					obj.closeAllBlocks();
					xpButtonsState = 'expand';
					$(this).attr('src', '../Images/imgInputs/all-expand.gif');
				}
				return false;
			})

		},
		
		/**
		 * Removes converter widgets from the document
		 * @param {HW.BudgetPlanner.Block.Input} [ignore] if supplied the converter for this input is not removed
		 */
		clearConverters: function(ignore){
			for (var i = 0; i < this.blocks.length; i++) {
				for (var j = 0; j < this.blocks[i].inputs.length; j++) {
					if (this.blocks[i].inputs[j] != ignore && this.blocks[i].inputs[j].converterWidget) {
						this.blocks[i].inputs[j].converterWidget.destroy();
					};
				};
			}; 
		},
			
		hideIcons: function(){
			for (var i = 0; i < this.blocks.length; i++) {
				for (var j = 0; j < this.blocks[i].inputs.length; j++) {
					if (this.blocks[i].inputs[j].icon) {
						this.blocks[i].inputs[j].icon.hide();
					}
				};
			};
		},
		
		/**
		 * Finds the sum of the values of the first input in each block. The first input must
		 * always hold the subtotal for each block. Sets the element with id SpendTotal to sum.
		 */
		doGrandTotal: function(){
			var sum = 0;
			//start at 1 - do not include income in spending total
			for(var i = 1; i < this.blocks.length; i++){
				sum += this.blocks[i].inputs[0].getFloat();
			};
			$('#SpendTotal').html(this.Util.format(sum));
			$('#lblMonthlyBalance').html(this.Util.format(HW.Util.toNumber($('#ctrlIncome_txtSectionHeaderValue').val())-sum,true));
		},
		
		openAllBlocks: function(){
			for (var i = 0; i < this.blocks.length; i++) {
				if(!this.blocks[i].state){
					this.blocks[i].changeState();
				}
			}
		},
		
		closeAllBlocks: function(){
			for (var i = 0; i < this.blocks.length; i++) {
				if(this.blocks[i].state){
					this.blocks[i].changeState();
				}
			}
		},	
		
		indexOf: function(block){
			for(var i = 0; i < this.blocks.length; i++){
				if(this.blocks[i] == block){
					return i;	
				}
			}
			return -1;
		},
		
		validate: function(form){
			for (var i = 0; i < this.blocks.length; i++) {
				if (this.blocks[i].inputs[0].getVal() == this.defaultValue) {
					$.ajaxportal('./confirm_overlay.html', function(){
						var overlay = this;
						$('#btnContine').click(function(){
							WebForm_DoPostBackWithOptions(new WebForm_PostBackOptions("btnContine", "", true, "", "", false, false));
							//form.submit();
						})
						$('#btnRevise').click(function(){
							overlay.trigger('overlay.close');
							return false;
						});
					})
					return false;
				}
			}
			return true;
		},
		
		Util: {
			/**
			 * checks if val is a valid number string and then calls HW.Util.format
			 * @param {String} val number string
			 * @param {Boolean} neg allow values < 0
			 * @see HW.Util.format
			 */
			format: function(val,neg){
				if(isNaN(HW.Util.toNumber(val)) || (parseFloat(HW.Util.toNumber(val)) < 0 && !neg)){
					return HW.BudgetPlanner.defaultValue;
				};
				
				return HW.Util.format(HW.Util.toNumber(val), HW.BudgetPlanner.defaultValue);
			}
		}
		
	}
	

	/**
	 * Passing an HTMLInput[type=text] element to this constructor endows it with the default behaviours 
	 * for this application. These are in summary:
	 * Empty fields are prefilled with default value if default provided,
	 * Giving focus clears default values,
	 * Giving focus will select all for non-default values,
	 * Supplied values are formated if formating function supplied.
	 * Also provides a number of methods for manipulating the input.
	 * @param {jQuery} elem jQuery containing HTMLInput
	 * @param {Object} [dv] default value
	 * @param {Function} [format] function to format user input
	 */
	HW.TextBox = function(elem, dv, format){
		var obj = this;
		
		this.element = elem;
		this.defaultVal = (dv) ? dv : '';
		this.format = format || function(v){return v;};
		if (!this.element || this.element.length == 0 || this.element.attr('tagName').toLowerCase() != 'input' || this.element.attr('type') != 'text') {
			throw new TypeError("HW.TextBox adds behaviours to HTML <input> elements of type 'text'. You supplied "+this.element[0]+' '+this.element.attr('tagName')+"[type="+this.element.attr('type')+"]");
		};
		var activated = false;
		this.isActivated = function(){
			return (activated == true);
		};
		this.activate = function(){
			activated = true;
		};
		this.deactivate = function(){
			activated = false;
		};
		if(this.isEmpty()){
			//this.setVal(this.defaultVal);
		}
		else{
			//if this is a page refresh activate 
			//any fields with values already entered
			if (this.getVal() != this.defaultVal) {
				//this.setVal(this.getVal());
				//if (this.getVal() != this.defaultVal) {
					//this.validate();
					this.activate();
				//}
			};
			
		};
		//FF requires us to clear the selection when we are done because of its strange
		//handling of programatic selections. The text stays selected even when the field 
		//is blurred, though it does not appear so until the input is refocused. This 
		//can cause unexpected effects when the app tries to reselect the text that is 
		//already selected.		
		this.element.mousedown(function(){
			if(obj.element.get(0).setSelectionRange){
				obj.setSelected(0, 0);
			};			
		})
		//a click will select all unless the user has manually selected something while the mouse was down
		//the second click will place the cursor
		var dubsees = 0;
		this.element.mouseup(function(){
			if (obj.isActivated()) {
				if (obj.getSelected() == '' && dubsees < 1) {
					obj.selectAll();
				}
				dubsees++;
			}			
		});
		//default value removed otherwise contents is selected
		this.element.focus(function(){
			if (!obj.isActivated()) {
				obj.clear();
				obj.activate();
			};
		});	
		
		this.element.blur(function(){
			//if the input is blank OR if the input is not blank and does not contain the default value but	validating it results in the default value
			//then deactivate the field again
			if (obj.getVal() == '' || obj.getVal() && obj.getVal() != obj.defaultVal && HW.BudgetPlanner.Util.format(obj.getVal()) == obj.defaultVal) {
				obj.deactivate();
			};
			//format user input
			obj.setVal(obj.getVal());
			dubsees=0;
		});		
		
	}
	
	HW.TextBox.prototype = {
		
		setVal: function(value){
			this.element.val(HW.BudgetPlanner.Util.format(value));
		},
		getVal: function(){
			return this.element.val();
		},
		/**
		 * parses the input text to a Float before returning it
		 * @return {Float}
		 */
		getFloat: function(){
			return parseFloat(HW.Util.toNumber(this.getVal()));
		},
		clear: function(){
			this.setVal(null);
		},
		isEmpty: function(){
			return (this.getVal() == '');
		},
		/**
		 * Programatically give focus. This will select the current value, unlike
		 * manualy giving focus which clears the field if it contains a default value.
		 */
		focus: function(){
			var val = this.getVal();
			this.element.focus();
			this.setVal(val);
			this.selectAll();
		},
		blur: function(){
			this.element.blur();
		},
		/**
		 * select text - abstracts away browser specific text slection code
		 * @param {Integer} start index of character to start selection on
		 * @param {Integer} end index of character to end selection on
		 * @return {Boolean} true if browser supports text selection. otherwise false
		 */		
		setSelected: function(start, end){
			try {
				if (this.element.get(0).setSelectionRange) {//FF
					this.element.get(0).setSelectionRange(start, end);
					return true;
				}
				else {
					if (this.element.get(0).createTextRange) {//IE
						var range = this.element.get(0).createTextRange();
						range.moveStart("character", start);
						range.moveEnd("character", this.element.get(0).value.length - end);//0 index from end
						range.select();
						return true;
					}
					else {
						return false;
					};
				};
			}
			catch(e){
				return false;
			}
		},
		/**
		 * get selected text
		 * @return {String} the currently selected text
		 */
		getSelected: function(){
			var selectedText = '';
			if (document.selection){//IE
				var sel = document.selection.createRange();
				selectedText = sel.text;
			}
			else if (typeof this.element.get(0).selectionStart != 'undefined'){//FF
				var startPos = this.element.get(0).selectionStart;
				var endPos = this.element.get(0).selectionEnd;
				selectedText = this.element.get(0).value.substring(startPos, endPos);
				if(!selectedText){ selectedText == '' }; 
			};
			return selectedText;
		},		
		selectAll: function(){
			this.setSelected(0, this.getVal().length);
		}
	}

	/**
	 * Creates an object that defines an expendature or income categary block. e.g. 'Your Home'
	 * @param {jQuery} jQuery containing wrapper HTML element that encloses a block. Has the class 'sector'.
	 * @constructor
	 */
	HW.BudgetPlanner.Block = function(wrapper){
		var obj = this;
		
		this.wrapper = wrapper;
		this.toggle = $('.button', wrapper);
		this.inputs = [];
		this.total = $('.total .col01 span', wrapper); //span containing total replaced by input when block closed
		
		//create input object for each form element
		$('input[type=text], select', wrapper).each(function(i){
			obj.inputs.push(new HW.BudgetPlanner.Block.Input($(this), obj, (i)?false:true));
		});
		
		//this.doTotal();//set starting total
		
		//set starting state
		if(this.wrapper.hasClass('open')){
			this.state = 'open';
		};
		
		this.toggle.css('cursor', 'pointer');
		this.toggle.click(function(e){
			obj.changeState();
			return false;
		});
	}
	
	HW.BudgetPlanner.Block.prototype = {
		
		//true is open
		state: false,
		
		changeState: function(validate){
			var obj = this;
			var validate = validate || true;
			if(!this.state){
				//synchronise text and textbox values
				this.setTotal(obj.getTotal());
			}
			else{
				if (validate) {
					this.inputs[0].validate();
				}
			}
			//set state and switch classes
			this.setState(!this.state);
			setTimeout(function(){
				obj.wrapper.toggleClass('closed');
				obj.wrapper.toggleClass('open');
			}, 0);
		},
		
		setState: function(state){
			this.state = state;
		},
		/**
		 * @return {String} the value of the total textbox
		 */
		getTotal: function(){
			return this.inputs[0].getVal();
		},
		/**
		 * sets the total text and total textbox to the supplied value
		 * @param {Object} val must evaluate to Float
		 */
		setTotal: function(val){
				this.inputs[0].setVal(val);
				if (this.inputs[0].getVal() != HW.BudgetPlanner.defaultValue) {
					this.inputs[0].activate();
				}
				this.total.html('£ '+HW.BudgetPlanner.Util.format(val));
		},
		/**
		 * @return {Number} the sum of the inputs not including total input
		 */
		sum: function(){
			var sum = 0;
			for(var i = 1; i < this.inputs.length; i++){
				if (!isNaN(HW.Util.toNumber(this.inputs[i].getVal()))) {
					sum += +HW.Util.toNumber(this.inputs[i].getVal());
				};
			};
			return sum;
		},
		/**
		 * sets the block subtotal to sum and sets grand total
		 */
		doTotal: function(stopGrandTotal){
			this.setTotal(HW.BudgetPlanner.Util.format(this.sum()));
			
				//HW.BudgetPlanner.doGrandTotal();

		},
		
		/**
		 * this manages the behaviour applied when a new value is entered in the total field.
		 * If less than current total, all fields cleared and value is applied to 'other'. Otherwise
		 * the diffence is added to the 'other' field.
		 */
		setDif: function(){
			//this.inputs[this.inputs.length-1].setVal(this.inputs[this.inputs.length-1].getFloat() + (this.getTotal() - this.sum()));
			if (HW.Util.toNumber(this.getTotal()) < this.sum()) {
				for (var i = 1; i < this.inputs.length - 1; i++) {
					this.inputs[i].setVal(0);
					this.inputs[this.inputs.length - 1].setVal(this.getTotal());
				}
			}
			else{
				this.inputs[this.inputs.length-1].setVal(this.inputs[this.inputs.length-1].getFloat() + (HW.Util.toNumber(this.getTotal()) - this.sum()));
			}
		},
		
		indexOf: function(input){
			for (var i = 0; i < this.inputs.length; i++) {
				if(this.inputs[i] == input){
					return i;
				};
			};
			return -1;
		},
		
		getNextVisibleInput: function(input){
			if (this.state && this.indexOf(input) + 1 < this.inputs.length) {
				return this.inputs[this.indexOf(input) + 1];
			}
			else {
				if(HW.BudgetPlanner.indexOf(this) + 1 < HW.BudgetPlanner.blocks.length){
					if (HW.BudgetPlanner.blocks[HW.BudgetPlanner.indexOf(this) + 1].state) {
						return HW.BudgetPlanner.blocks[HW.BudgetPlanner.indexOf(this) + 1].inputs[1];
					}
					else {
						return HW.BudgetPlanner.blocks[HW.BudgetPlanner.indexOf(this) + 1].inputs[0];
					}
				}
				else{
					return $('#btnCalc');
				}
			}
		}

	}
	
	/**
	 * Defines a container object for each input element in a block
	 * @param {jQuery} elem jQuery containg HTML form element
	 * @param {HW.BudgetPlanner.Block} block parent block
	 * @param {Object} isTotal is this an input field or a total field that will contain the sum of the other inputs
	 * @inherits HW.BudgetPlanner.TextInput
	 * @constructor
	 */
	HW.BudgetPlanner.Block.Input = function(elem, block, isTotal){
		var obj = this;
		this.element = elem;
		this.block = block;
		this.isTotal = isTotal;
		this.converterWidget;
		this.errorsUrl = HW.BudgetPlanner.validationEndPoint;
		this.dismissUrl = HW.BudgetPlanner.dismissErrorEndPoint;
		this.errObjs = {
			GenericError: {
				element: null, id: "GenericError", show: true
			},
			SpecificError: {
				element: null, id: "SpecificError", show: true
			}
		}
		
		//this.genError = {element: null, id: "GenericError", show: true};
		//this.specError = {element: null, id: "SpecificError", show: true};
		//this.GenericError = this.genError;
		//this.SpecificError = this.specError;
		this.errorsCache = {};
		this.timer;
		
		var errorState;//this holds a reference to the error element currently displayed
		this.setErrorState = function(arg){
			errorState = arg;
		};
		this.getErrorState = function(){
			return errorState;
		};
		
		//we must grab and initialise any preexisting error messages generated on the server side
		var error = this.initError();
		if (error.length > 0) {
			var errorType = error.attr('className').replace('error ', '');//extract error type from class name
			this.errObjs[errorType].element = error;
			this.setErrorState(this.errObjs[errorType].element);
		}
		
		//if this is a text input - this.calc will act a switch from now on to test this
		if (this.element.attr('tagName').toLowerCase() == 'input' && this.element.attr('type') == 'text') {
			//call our supper construcor only if this is a textbox
			HW.TextBox.call(this, this.element, HW.BudgetPlanner.defaultValue, HW.BudgetPlanner.Util.format);
			this.icon = new HW.BudgetPlanner.Block.Input.Icon(this);
		};

		this.element.click(function(e){
			e.stopPropagation();
		});
		this.element.change(function(){
			//total inputs must first set the 'other' field before doing total because doTotal()
			//overwrites the total input with the sum of other fields.
			if (obj.isTotal) {
				obj.block.setDif();
			}
			else {
				obj.block.doTotal();
			}
			HW.BudgetPlanner.doGrandTotal();
		});	
		this.element.blur(function(){
			//we are delaying this incase to blur the field user clicks 'correct' input
			//that click must act first because it should block the showing of further errors
			obj.timer = setTimeout(function(){obj.validate();}, 200);
		})	
		this.element.focus(function(){
			HW.BudgetPlanner.clearConverters(obj);
		});
		//client wants enter key to tab to nex input
		this.element.keypress(function(e){
			if (e.keyCode == 13) {
				obj.blur();
				obj.element.change();
				obj.block.getNextVisibleInput(obj).focus();
			}
		})
	}

	//inherit from HW.TextBox. N.B. extend does not deep copy
	HW.BudgetPlanner.Block.Input.prototype = $.extend({}, HW.TextBox.prototype);
	//override/add methods
	HW.BudgetPlanner.Block.Input.prototype = $.extend(HW.BudgetPlanner.Block.Input.prototype, {

		/**
		 * method to initialise the 'correct' <input> in the error message.
		 * If you do not pass the error object it will try to find a preexisting error message
		 * @param {Object} errorObj
		 */
		initError: function(errorObj){
			var obj = this;
			
			var errorElem = (errorObj) ? errorObj.element : null;
			errorElem = errorElem || this.element.parent().find('.error');
			errorElem.find('input[alt=Correct]').click(function(){
					var errorType = errorElem.attr('className').replace('error ', '');//extract error name from class
					$.get(obj.errorsUrl, {DatabaseId:obj.element.attr('DatabaseId'),value:obj.getVal(),isValid:1}, function(res){
						if (res == 'True') {
							obj.errObjs[errorType].show = false;
						};
					});
					clearTimeout(obj.timer);
					obj.removeError();
					obj.block.getNextVisibleInput(obj).focus();
					return false;
				});	
			return errorElem;			
		},

		/**
		 * Sends an AJAX request with the value to be validated and Boolean response as string
		 * This is not 100% elegant but is restricted by the way the backend is implemented. 
		 */
		validate: function(){
			var obj = this;
			function addRemoveErrors(errObj){
				if (errObj == obj.errObjs.GenericError && obj.errObjs.GenericError.show || errObj == obj.errObjs.SpecificError && obj.errObjs.SpecificError.show) {//user has not dismissed errors
					if (typeof obj.errorsCache[obj.getVal()] == 'undefined') {//not cached
						$.get(obj.errorsUrl, {DatabaseId:obj.element.attr('DatabaseId'),value:obj.getVal()}, function(res){
							if (res != 'noerror'){//} && !res.match('Error in the page')) { //these are the responses required if the field is valid
								errObj.element = $('<span class="error" style="display: none;"><span>' + res + ' <input type="image" title="click to dismiss error" alt="Correct" src="/Images/imgInputs/correct.gif"/>?</span></span>');
								obj.element.parent().append(errObj.element);
								obj.initError(errObj);						
								obj.displayError(errObj.element);
							}
							else{
								obj.removeError();
							};
							obj.errorsCache[obj.getVal()] = res; //cach response against value
						}, 'text');
					}
					else{
						if(obj.errorsCache[obj.getVal()] != 'noerror'){//cached with error
							obj.displayError(errObj.element);
						}
						else{
							obj.removeError();
						};
					};
				}
				else{
					obj.removeError();
				};
			}			
		
			if (this.errObjs.SpecificError.show) {
				addRemoveErrors(obj.errObjs.SpecificError);
			}
		},
		
		/**
		 * displays the error message held in the HTMLElement error. If the error 
		 * does not exist, no action is taken
		 * @param {jQuery} error HTMLElement holding error message
		 */
		displayError: function(error){
			var obj = this;
			
			if(!error){ throw new TypeError('error {jQuery} is null or not an object'); };

			//the timeout here gives us a chance to stop the error message being displayed if
			//the calculator icon is clicked (the onclick event fires) after dispayError has
			//been called. Because it is often a callback on an ajax request the icon is clicked
			//before displayError has been called. In this case the pauseErrors flag allows
			//the showing of errors to be temporarily suspended.
			if(this.pauseErrors){ this.pauseErrors = false; return;}
			//avoid collisions if the user goes click happy.
			clearTimeout(this.timer);
			this.timer = setTimeout(function(){
				/**
				 * Animates the error message er into view
				 * @param {jQuery} er HTMLElement holding error message
				 */
				function animIn(er){
					obj.setErrorState(er);
					if (er.length > 0) {
						er.children().css({visibility: 'hidden', display: 'block'});
						HW.BudgetPlanner.animating = true;
						er.slideDown(function(){
							er.children().css({visibility: 'visible', display: 'none'})
							er.children().fadeIn(function(){
								HW.BudgetPlanner.animating = false;
								er.css({height: ''});
							});
						});
					}
				};

				//if same error as already showing do nothing
				if(obj.getErrorState() && obj.getErrorState().attr('className') == error.attr('className')){
					return;
				};
				//else if another error is showing, hide it and show new error once current 
				//error is hidden, by using animIn as a callback
				if(obj.getErrorState()){
					obj.removeError(function(){animIn(error)});
					return;
				};
	
				//if no error is currently displayed just show new error
				animIn(error);
			
			}, 100);
			
		},
		
		/**
		 * removes the currently displayed error (held in errorSate) if there is one
		 * @param {Function} [callback] callback executed once error has finished animating out
		 * @return {Boolean} false if no error was found else true
		 */
		removeError: function(callback){
			var obj = this; 
			var error = this.getErrorState();
			if (error) {
				this.setErrorState(null);
				HW.BudgetPlanner.animating = true;
				error.children().fadeOut(function(){
					error.children().css({visibility: 'hidden', display: 'block'})
					error.slideUp(function(){
						error.css({display: 'none'});
						HW.BudgetPlanner.animating = false;
						if (callback) {callback();}
					});
				});
				return true;
			};
			return false;
		}

	});
	
	HW.BudgetPlanner.Block.Input.Icon = function(input){
			var obj = this;
			
			this.input = input;
			
			this.element = this.input.element.after(this.html).next('*').children('a');

			this.input.element.blur(function(){
				if (obj.input.converterWidget) {
					obj.input.converterWidget.delayDestroy();
				};
				obj.fadeOut();
			});		
			this.input.element.focus(function(){
					//this means that the converter will stay open if the input is refocused
					//remove the if if you don't want it to
					if (obj.input.converterWidget) {
						obj.input.converterWidget.stopDestroy();
					}else{
						obj.fadeIn();
					};
			});
			this.element.focus(function(){
				clearTimeout(obj.timer);
			});
			this.element.blur(function(){
					obj.fadeOut();
			});
			function open(e){
				if (obj.input.timer) {
					clearTimeout(obj.input.timer);
					obj.input.timer = null;
				}
				else{
					obj.input.pauseErrors = true;
				}
				obj.fadeOut(function(){
					obj.input.converterWidget = new HW.BudgetPlanner.Block.Converter(obj.input);
				});
				return false;
			}
			this.element.click(open);	
			this.element.keypress(function(e){
				if(e.which == 32){
					open(e);
					return false;	
				}
			});
	}
	
	HW.BudgetPlanner.Block.Input.Icon.prototype = {
		
		html: '<span class="calc"><a href="#" title="conversion calculator">&nbsp;</a></span>',
		
		hide: function(){
			this.element.parent().css({visibility: 'hidden'});
		},
		fadeIn: function(callback){
			var obj = this;
			
			HW.BudgetPlanner.hideIcons();
			this.element.parent().css({visibility: 'visible'});
			
			this.timer = setTimeout(function(){
				//Because IE is so *!=+!$*"? it is best just to not show the calculator until
				//any running animations have completed.
				if (!HW.BudgetPlanner.animating) {
						obj.element.parent().fadeIn((jQuery.support.opacity) ? 200 : 0, callback);
				}
				else {
					setTimeout(arguments.callee, 200);
				}
			}, 200);
		},
		fadeOut: function(callback){
			var obj = this;
			this.timer = setTimeout(function(){
				obj.element.parent().fadeOut((jQuery.support.opacity) ? 100 : 0, callback);
			}, (jQuery.support.opacity) ? 100 : 0);
		}
		
	}
	
	/**
	 * Instantiation generates a converter widget for the Input input
	 * @param {HW.BudgetPlanner.Input} input input object with which this converter is associated.
	 */
	HW.BudgetPlanner.Block.Converter = function(input){
		this.input = input;
		this.converter;
		this.timer;
		this.create();
	}
	
	HW.BudgetPlanner.Block.Converter.prototype = { 
		
		timers: [],
		
		html:	'<div class="converterWrap">\
					<div class="converter" style="display: none;">\
						<!--The table hack is because IE is not always regestering click events on the converter div-->\
						<!--[if lte IE 7]>\
						<table style="border-collapse: collapse; width: 199px;">\
							<tr>\
								<td style="">\
								<![endif]-->\
									<div class="converter00">\
										<div class="row">\
											<span>Convert from: </span><label for="iptWeekly">Weekly</label><input type="radio" id="iptWeekly" name="radGroup00" /><label for="iptYearly">Annually</label><input type="radio" id="iptYearly" name="radGroup00" />\
										</div>\
										<div class="row row2">\
											<label for="iptValue">&pound;</label><input id="iptValue" type="text" /><a href="#" id="converterCalculate">Calculate</a>\
										</div>\
									</div>\
									<div class="converter01">\
										&nbsp;\
									</div>\
								<!--[if lte IE 7]>\
								</td>\
							</tr>\
						</table>\
						<![endif]-->\
					</div>\
				</div>',
				
		/** Generates the converter widget using the HTML above */
		create: function(){
			var obj = this;
			
			this.converter = this.input.element.after(this.html).next().find('.converter');
			this.converter.fadeIn((jQuery.support.opacity) ? 200 : 0);
		
			var focusable = this.converter.find('input, a, select, button');
			
			
			this.destroyHandler = function(){
				obj.delayDestroy();
			}
			focusable.blur(this.destroyHandler);
			focusable.focus(function(){
				obj.stopDestroy();
			});
			this.converter.mousedown(function(e){ 
				setTimeout(function(){obj.stopDestroy();}, 0);
				
			});
			$(document).mousedown(this.destroyHandler);
			
			var weeklyRadio = obj.converter.find('#iptWeekly');
			var yearlyRadio = obj.converter.find('#iptYearly');
			//check last selection by default
			if(HW.BudgetPlanner.lastConverted == 'weekly'){
				weeklyRadio.attr('checked', true);
			}
			else{
				if(HW.BudgetPlanner.lastConverted == 'yearly'){
					yearlyRadio.attr('checked', true);
				};
			};
			
			var input = new HW.TextBox(obj.converter.find('#iptValue'), HW.BudgetPlanner.defaultValue, HW.BudgetPlanner.Util.format);
			setTimeout(function(){input.focus();}, 0); //back of stack - prevents problems with automatic highlighting
			
			function submitHandler(e){
		
				var weekly = weeklyRadio.attr('checked');
				var yearly = yearlyRadio.attr('checked');
				var value = input.getVal();
				//do calc, set val
				if (weekly) {
					obj.input.setVal(HW.Util.toNumber(value) * 4.33);
					HW.BudgetPlanner.lastConverted = 'weekly';
				};
				if(yearly){
					obj.input.setVal(HW.Util.toNumber(value) / 365.2425 * 30.4167); 
					HW.BudgetPlanner.lastConverted = 'yearly';
				};
				if(weekly || yearly){
					obj.destroy(); //'close' converter
					obj.input.activate(); //activate input
					if (obj.input.isTotal) {
						obj.input.block.setDif();
					}
					else {
						obj.input.block.doTotal(); //set totals
					}
					HW.BudgetPlanner.doGrandTotal();
					obj.input.focus();
					return false;
				};
				alert('Please indicate whether you would like to convert from a weekly or a yearly value.');
				return false;

			}
			
			this.converter.find('#converterCalculate').click(function(e){
				return submitHandler(e);
			})
			.keypress(function(e){
				if (e.which == '32') {
					return submitHandler(e);
				}
			});
			input.element.keypress(function(e){ 
				if(e.which == '13'){
					return submitHandler(e);
				};
			});
		
		},
		
		/**
		 * removes the converter widget from the DOM and 
		 * informs the spawning input that it has gone
		 */ 
		destroy: function(){
			var obj = this;
			this.converter.fadeOut((jQuery.support.opacity) ? 100 : 0, function(){
				obj.converter.remove();
			});
			this.input.converterWidget = null;
			$(document).unbind('mousedown', this.destroyHandler);
		},
		
		delayDestroy: function(){
			var obj = this;
			this.timers.push(setTimeout(function(){
				obj.destroy(); 
				obj.input.validate(); 
			}, 200));
		},
		
		stopDestroy: function(){
			for (var i = 0; i < this.timers.length; i++) {
				clearTimeout(this.timers[i]);
			};
		}
		
	}

})(jQuery);


