if(!console) { var console = { log : function() {} , debug : function() {}, dir : function() {}}};
function alert(msg) {} // overrides javascripts inherent alert function
var loudwater = {
	popup : {
		show : function(id, options) {
			var settings = jQuery.extend({
				openCallback : null,
				closeCallback : null,
				close: true,
				closeClass: 'container-close',
				closeFn: null
			}, options);
			
			if (settings.close) {
				var selector = "#"+id+" ."+settings.closeClass;
				if(jQuery(selector).size() < 1) {
					jQuery("<span></span>").addClass(settings.closeClass).appendTo("#"+id);
				}
				jQuery(selector).unbind().click((typeof(settings.closeFn)=="function") ? settings.closeFn : function() { loudwater.popup.hide(id, settings.closeCallback)});
			}
			//jQuery("#"+id+" .container-close").unbind().click((typeof(customClose)=="function") ? customClose : function() { loudwater.popup.hide(id) })

			var tot_height, pop_height;
			if (window.innerWidth && window.innerHeight) {
				tot_height = window.innerHeight;
			} else if (document.documentElement) {
				tot_height = document.documentElement.offsetHeight;
			}

			jQuery.blockUI({
				css: {
					border: 'none',
					background: 'none',
					width: '100%',
					left: '0', 
					cursor:'normal'
				},
				overlayCSS: {
					backgroundColor: '#999',
					opacity: 0.5
				},
				message: jQuery('#'+id)
			});

			pop_height = jQuery('.blockPage').height();
			var top_percent = ((tot_height-pop_height)/tot_height) * 50;
			if(top_percent < 0) top_percent = 0;
			jQuery('.blockPage').css('top',top_percent+'%');
			
			if(typeof(settings.openCallback)=="function") settings.openCallback();
		},
		hide : function(id, callback) {
			jQuery.unblockUI();
            jQuery("#"+id).hide();
            
            if(typeof(callback)=="function") callback();
		}
	},
	validate : {
		keys : null,
		settings: {
			submitMask:		'',
			submitTarget:	'',
			submitOnEnter:	false
		},
		bind : function(v, options) {
			lwv = loudwater.validate;
			jQuery.extend(lwv.settings, options);
			lwv.keys = v;

			for(id in lwv.keys) {
				jQuery('.'+id)
					.bind("keyup focus", function(e) {
						var classes = jQuery(this).attr('class').split(' ');

						for(var c=0;c<classes.length;c++) {
							if(lwv.keys[classes[c]] != null) {
								if(typeof(lwv.keys[classes[c]]) == "object") {
									jQuery(this).removeClass('validate_ok validate_info validate_error');

									if(lwv.keys[classes[c]].fn(jQuery(this).val())) {
										jQuery(this).addClass('validate_ok');
										jQuery(this).closest('td').find('.errortip').hide();
									} else {
										jQuery(this).addClass('validate_info');
									}
										
									jQuery(this).closest("td").find(".tooltip").show();
								} else {
									jQuery(this).removeClass('validate_ok validate_info validate_error');

									if(lwv.keys[classes[c]](jQuery(this).val())) {
										jQuery(this).addClass('validate_ok');
										jQuery(this).closest('td').find('.errortip').hide();
									} else {
										jQuery(this).addClass('validate_info');
									}
									jQuery(this).closest("td").find(".tooltip").show();
								}
							}
						}

						if(lwv.checkAll()) {
							jQuery(lwv.settings.submitTarget).show();
							jQuery(lwv.settings.submitMask).hide();
							if(lwv.settings.submitOnEnter && e.keyCode != undefined && e.keyCode == 13) {
								jQuery(lwv.settings.submitTarget).click();
							}
						} else {
							jQuery(lwv.settings.submitTarget).hide();
							jQuery(lwv.settings.submitMask).show();
						}
					})
					.bind("change blur", function() {
						var classes = jQuery(this).attr('class').split(' ');

						for(var c=0;c<classes.length;c++) {
							if(lwv.keys[classes[c]] != null) {
								if(typeof(lwv.keys[classes[c]]) == "object") {
									jQuery(this).removeClass('validate_ok validate_info validate_error');
									if(jQuery(this).val() != '') {
										if(lwv.keys[classes[c]].fn(jQuery(this).val())) {
											jQuery(this).addClass('validate_ok');
											jQuery(this).closest('td').find('.errortip').hide();
										} else {
											jQuery(this).addClass('validate_error');
											jQuery(this).closest('td').find('.errortip').text(lwv.keys[classes[c]].error(jQuery(this).val())).show();
										}
									}
									jQuery(this).closest("td").find(".tooltip").hide();
								} else {
									jQuery(this)
										.removeClass('validate_ok validate_info validate_error');
									if(jQuery(this).val() != '') {
										if(lwv.keys[classes[c]](jQuery(this).val())) {
											jQuery(this).addClass('validate_ok');
											jQuery(this).closest('td').find('.errortip').hide();
										} else {
											jQuery(this).addClass('validate_error');
											jQuery(this).closest('td').find('.errortip').show();
										}
									} else {

									}
									jQuery(this).closest("td").find(".tooltip").hide();
								}

							}
						}

						if(lwv.checkAll()) {
							jQuery(lwv.settings.submitTarget).show();
							jQuery(lwv.settings.subtmitMask).hide();
						} else {
							jQuery(lwv.settings.submitTarget).hide();
							jQuery(lwv.settings.subtmitMask).show();
						}
					});
			}
		},
		checkAll : function() {
			var lwvk = loudwater.validate.keys;
			var valid = true;

			for(key in lwvk) {
				if(valid) {
					if(typeof(lwvk[key]) == "object") {
						valid = lwvk[key].fn(jQuery('.'+key).val());
					} else {
						valid = lwvk[key](jQuery('.'+key).val());
					}
				}
			}

			return valid;
		}
	},
	/* index(Object, Function) - takes in an associative array styled JSON
	 * object, where every sub object is indexed by id, and a function which
	 * returns a comparable value when passed a child of the object. returns an
	 * array of object ids sorted by the return value of the comparator function
	 * in ascending order */
	index : function(object, comparator, descending) {
		var pairs = []
		for(o in object) {
			pairs.push({id: o, data: comparator(object[o])});
		}

		var compare = function(a, b) {
			if(a.data < b.data) {
				return -1;
			} else if(a.data > b.data) {
				return 1;
			} else if(a.data == b.data) {
				return 0;
			}
			return null;
		}

		pairs.sort(compare);

		var indexed = [];
		for(var i=0;i<pairs.length;i++) {
			indexed.push(pairs[i].id);
		}

		if(descending) indexed.reverse();

		return indexed;
	},
	/* _templateMap(Object, Object) - private method which maps an object o onto
	 * an HTML template t and returns the HTML code*/
	templateMap : function(o, t) {
		var result = t.stencil;

		for(k in t.map) {
			try {
				if(typeof(t.map[k]) == "string") {
					result = result.replace('@('+k+')', eval('o'+t.map[k]));
				} else if (typeof(t.map[k]) == "object") {
					var str = eval('o'+t.map[k].p);
					if(t.map[k].op) {
						str = t.map[k].op(str);
					}
					result = result.replace('@('+k+')', str);
				}
			} catch (e) { console.log(new loudwater.exception("error in templateMap function on key \""+k+"\"\n\n"+e.message));  }
		}
		return result;
	},
	exception : function (message) {
		this.message = message;
		this.name = 'LoudwaterException';
		this.toString = function () { return this.name + ': ' + this.message; }
	}
}
/**
 * cross-browser "getElementById"
 */
function go(objectId) {
  if (document.getElementById) {
    return document.getElementById(objectId);
  } else if (document.all) {
    return document.all(objectId);
  } else {
    return document.layers[objectId];
  }
}

/**
 * format as currency with decimalDigit decimal places (including comma placeholders for thousands, hundred thousands, millions, etc).
 */
function formatAsCurrency(numericValue,decimalPlaces,excludeDollarSign) {
	var multiplier = Math.pow(10,decimalPlaces);

    // restrict to decimalPlaces decimal places
    numericValue = Math.round(numericValue*multiplier)/multiplier;
     
    // grab cents              
    var strResult = numericValue + '';                               
    var decimalIndex = strResult.indexOf('.');
    if(decimalPlaces > 0) {
	    if(decimalIndex < 0) {
	        strResult = '.';
	    } else {
	        strResult = strResult.substr(decimalIndex);
	    }   
	    
	    // right zero pad
	    while(strResult.length < (1 + decimalPlaces)) {
	        strResult = strResult + '0';
	    }   	    
    } else {
    	strResult = '';
    }                                     
    
    var isNegative = numericValue < 0;
    
    numericValue = Math.floor(Math.abs(numericValue));            
    
    if(numericValue == 0) {
        strResult = '0' + strResult;
    } else {                
        // now, take care of thousands, hundreds of thousands, millions, etc.
        while(numericValue > 0) {
            mod = numericValue % 1000;
            numericValue = numericValue - mod;
            numericValue = Math.round(numericValue/1000);        

            // zero pad
            modStr = mod + '';
            if(numericValue > 0) {
                while(modStr.length < 3) {
                    modStr = '0' + modStr;
                }
                modStr = ',' + modStr;
            }

            strResult = modStr + strResult;
        }
    }   
    
    var prefix = isNegative ? '-' : '';
    
    if(excludeDollarSign != null && excludeDollarSign == true) {
        return prefix + strResult;    
    } else {
        return prefix + '$' + strResult;
    }
}

function formatAsPercent(numericValue,decimalPlaces) {
	var multiplier = Math.pow(numericValue,decimalPlaces);
	
	if(numericValue > 0 && numericValue < 1) {
		return '<1%';
	}	
	
	numericValue = Math.round(numericValue*multiplier)/multiplier;
	
	return numericValue + '%';
}

function extractNumericValue(text) {	
    if(text != null) {
        text = text.replace(/ /g,'');
        text = text.replace(/,/g,'');
        text = text.replace(/\%/g,'');
    }                      
    if(text.charAt(0) == '.') {
    	text = '0' + text;
    }
    
    return text;
}

function isNumeric(text) {
    var validChars = "0123456789.";
    for (i = 0; i < text.length; i++ ) {
        nextChar = text.charAt(i);
        if (validChars.indexOf(nextChar) == -1) {
            return false;
        }
    }
    
    return true;
}  

function getDaysInCurrentYear() {
    var currentYear = new Date().getFullYear();
    
    var isLeap = false;
    if(currentYear % 400 == 0) {
        isLeap = true;
    } else {
        if(currentYear % 100 == 0) {
            isLeap = false;
        } else {
            if (currentYear % 4 == 0) {
                isLeap = true;
            } else {
                isLeap = false;
            }                    
        }
    }
    
    return isLeap ? 366 : 365;
}     

/**
 * rewrite the contents of element with name if elementName if it can be found.  this may have to be cognizant of different DOM implementations
 */
function rewriteElementContents(elementName, value) {
    var element = go(elementName);
	if(element != null) {
		element.innerHTML = value;
	}
}

function getCookie(cookieName)
{
	if (document.cookie.length>0)
	{
		cookieStart=document.cookie.indexOf(cookieName + "=")
		if (cookieStart!=-1) { 
			cookieStart=cookieStart + cookieName.length+1 
			cookieEnd=document.cookie.indexOf(";",cookieStart)
			if (cookieEnd==-1) cookieEnd=document.cookie.length
			return unescape(document.cookie.substring(cookieStart,cookieEnd))
		} 
	}
	return "";
}

function setCookie(cookieName,value,expiredays)
{
	var exdate=new Date();
	exdate.setDate(exdate.getDate()+expiredays);
	
	document.cookie=cookieName+ "=" + escape(value) + ((expiredays==null) ? "" : ";expires="+exdate.toGMTString());
}

function deleteCookie(cookieName)
{
	if(getCookie(cookieName)) {
		document.cookie=cookieName+ "=;expires=Thu, 01-Jan-1970 00:00:01 GMT";		
	}
}

if (!window.LoudElement)
  var LoudElement = new Object();
   
LoudElement.visibleById = function(element) {
    return go(element).style.display != 'none';
  };

LoudElement.toggleById = function(element) {
    if (LoudElement.visibleById(element)){
    	LoudElement.hideById(element);
    }else{
	    LoudElement.showById(element)
    }
    return element;
  };

LoudElement.hideById = function(element) {
    go(element).style.display = 'none';
    return element;
  };

LoudElement.showById = function(element) {
    go(element).style.display = '';
    return element;
  };
  
/** expense tracking **/  
function rewriteExpenseFrequency(element, newValue){
	go(go(element).title).value = newValue;
};

function onAddMouseOver(inorout, dynamicElementId) {
    currentImg = go(go(dynamicElementId).title);

    if(inorout == 'in') {
        currentImg.src = "@CONTEXT@/img/icons/add_over.gif";
    } else {
        currentImg.src = "@CONTEXT@/img/icons/add_static.gif";        
    }     
}

function onDeleteMouseOver(inorout, dynamicElementId) {
    currentImg = go(go(dynamicElementId).title);

    if(inorout == 'in') {
        currentImg.src = "@CONTEXT@/img/icons/delete_over.gif";
    } else {
        currentImg.src = "@CONTEXT@/img/icons/delete_static.gif";        
    }     
}


/********* end: icon mouseover effects */


function clearValue(dynamicElementId) {
    field = go(go(dynamicElementId).title);
    field.value = "";
}

function getRadioInputValue(form,dynamicElementIdName) {
    var dynamicIdElement = go(dynamicElementIdName);
    
    var radioSelectName = dynamicIdElement.title;            

    // since myfaces gives the same id to the table element it generates around the radio buttons, we have to iterate through the form
    var formElements = form.elements;
    
    for(i = 0; i < formElements.length; i++) {
        if(formElements[i].name == radioSelectName && formElements[i].checked) {
            return formElements[i].value;
        }    
    }            
    
    return null;
}   

// assumes that a close/open div has 4 components:
// 1) the expandable/collapsible div itself, represented by divId
// 2) a control to open the div, represented by openDivControl
// 3) a control to close the div, represented by closeDivControl
// 4) a cookie that is set to the div's current state
//
// examines state of display and controls; toggles the display; sets cookie to new open/closed state
function toggleDivDisplay(divId,openDivControl,closeDivControl,divStateCookieName) {
    var divElement = go(divId);        
    var isClosed = divElement.style.display != null && divElement.style.display == 'none';
    
    if(isClosed) {
        openDivDisplay(divId,openDivControl,closeDivControl,divStateCookieName);        
    } else {
        closeDivDisplay(divId,openDivControl,closeDivControl,divStateCookieName);    
    }

}

function openDivDisplay(divId,openDivControl,closeDivControl,divStateCookieName) {
    var divElement = go(divId);
    var openDivElement = go(openDivControl);
    var closeDivElement = go(closeDivControl);
    
    var isClosed = divElement.style.display != null && divElement.style.display == 'none';    
    
    if(isClosed) {
        
        isClosed = !isClosed;

        divElement.style.display = isClosed ? 'none' : '';
        
        openDivElement.style.display = isClosed ? '' : 'none';
        closeDivElement.style.display = isClosed ? 'none' : '';
                
        if(divStateCookieName != null) {
            setCookie(divStateCookieName,isClosed ? 'closed' : 'expanded');
        }    
    }    
}

function closeDivDisplay(divId,openDivControl,closeDivControl,divStateCookieName) {
    var divElement = go(divId);
    var openDivElement = go(openDivControl);
    var closeDivElement = go(closeDivControl);
    
    var isClosed = divElement.style.display != null && divElement.style.display == 'none';   
    
    if(!isClosed) {
        isClosed = !isClosed;    
    
        divElement.style.display = isClosed ? 'none' : '';
        openDivElement.style.display = isClosed ? '' : 'none';
        closeDivElement.style.display = isClosed ? 'none' : '';
                
        if(divStateCookieName != null) {
            setCookie(divStateCookieName,isClosed ? 'closed' : 'expanded');
        }    
    }
}

// assumes use of <loud:writeId idPrefix=""> tag
function getDynamicId(loudIdPrefixId) {
    var loudIdPrefixElement = go(loudIdPrefixId);
    if(loudIdPrefixElement != null) {
        return loudIdPrefixElement.title;
    } else {
        return null;
    }
}

function getDynamicElement(loudIdPrefixId) {
  var loudIdPrefixElement = go(loudIdPrefixId);
  if(loudIdPrefixElement != null) {
    return go(loudIdPrefixElement.title);
  } else {
    return null;
  }
}

function setFocusOnElementWithDynamicId(dynamicId) {
    var element = getDynamicElement(dynamicId);
    if(null != element) {
        element.focus();
    }
}

/** hacky way to submit form after pressing return key.. revisit this later */
function blurOnEnter(input, evt) {
	if(enterPressed(evt)) {
		input.blur();
	}
	return true;
}

function enterPressed(evt) {
	evt = (evt) ? evt : event;
	var charCode = (evt.charCode) ? evt.charCode : ((evt.which) ? evt.which : evt.keyCode);	        	
	return (charCode == 13 || charCode == 3);
}

/*
 * Date Format 1.2.3
 * (c) 2007-2009 Steven Levithan <stevenlevithan.com>
 * MIT license
 *
 * Includes enhancements by Scott Trenda <scott.trenda.net>
 * and Kris Kowal <cixar.com/~kris.kowal/>
 *
 * Accepts a date, a mask, or a date and a mask.
 * Returns a formatted version of the given date.
 * The date defaults to the current date/time.
 * The mask defaults to dateFormat.masks.default.
 */

var dateFormat = function () {
	var	token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g,
		timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g,
		timezoneClip = /[^-+\dA-Z]/g,
		pad = function (val, len) {
			val = String(val);
			len = len || 2;
			while (val.length < len) val = "0" + val;
			return val;
		};

	// Regexes and supporting functions are cached through closure
	return function (date, mask, utc) {
		var dF = dateFormat;

		// You can't provide utc if you skip other args (use the "UTC:" mask prefix)
		if (arguments.length == 1 && Object.prototype.toString.call(date) == "[object String]" && !/\d/.test(date)) {
			mask = date;
			date = undefined;
		}

		// Passing date through Date applies Date.parse, if necessary
		date = date ? new Date(date) : new Date;
		if (isNaN(date)) throw SyntaxError("invalid date");

		mask = String(dF.masks[mask] || mask || dF.masks["default"]);

		// Allow setting the utc argument via the mask
		if (mask.slice(0, 4) == "UTC:") {
			mask = mask.slice(4);
			utc = true;
		}

		var	_ = utc ? "getUTC" : "get",
			d = date[_ + "Date"](),
			D = date[_ + "Day"](),
			m = date[_ + "Month"](),
			y = date[_ + "FullYear"](),
			H = date[_ + "Hours"](),
			M = date[_ + "Minutes"](),
			s = date[_ + "Seconds"](),
			L = date[_ + "Milliseconds"](),
			o = utc ? 0 : date.getTimezoneOffset(),
			flags = {
				d:    d,
				dd:   pad(d),
				ddd:  dF.i18n.dayNames[D],
				dddd: dF.i18n.dayNames[D + 7],
				m:    m + 1,
				mm:   pad(m + 1),
				mmm:  dF.i18n.monthNames[m],
				mmmm: dF.i18n.monthNames[m + 12],
				yy:   String(y).slice(2),
				yyyy: y,
				h:    H % 12 || 12,
				hh:   pad(H % 12 || 12),
				H:    H,
				HH:   pad(H),
				M:    M,
				MM:   pad(M),
				s:    s,
				ss:   pad(s),
				l:    pad(L, 3),
				L:    pad(L > 99 ? Math.round(L / 10) : L),
				t:    H < 12 ? "a"  : "p",
				tt:   H < 12 ? "am" : "pm",
				T:    H < 12 ? "A"  : "P",
				TT:   H < 12 ? "AM" : "PM",
				Z:    utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""),
				o:    (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4),
				S:    ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10]
			};

		return mask.replace(token, function ($0) {
			return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1);
		});
	};
}();

// Some common format strings
dateFormat.masks = {
	"default":      "ddd mmm dd yyyy HH:MM:ss",
	shortDate:      "m/d/yy",
	mediumDate:     "mmm d, yyyy",
	longDate:       "mmmm d, yyyy",
	fullDate:       "dddd, mmmm d, yyyy",
	shortTime:      "h:MM TT",
	mediumTime:     "h:MM:ss TT",
	longTime:       "h:MM:ss TT Z",
	isoDate:        "yyyy-mm-dd",
	isoTime:        "HH:MM:ss",
	isoDateTime:    "yyyy-mm-dd'T'HH:MM:ss",
	isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'"
};

// Internationalization strings
dateFormat.i18n = {
	dayNames: [
		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
		"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
	],
	monthNames: [
		"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
		"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
	]
};

// For convenience...
Date.prototype.format = function (mask, utc) {
	return dateFormat(this, mask, utc);
};
Array.prototype.swap = function (x, y) {
	if(x && y && x < this.length && x >= 0 && y < this.length && y >= 0) {
		var temp = this[x];
		this[x] = this[y];
		this[y] = temp;
	}
};
Array.prototype.unique = function() {var a = [];var l = this.length;for(var i=0; i<l; i++) { for(var j=i+1; j<l; j++) { if (this[i] === this[j]) j = ++i; } a.push(this[i]);} return a;};
Array.prototype.intersect = function() {if (!arguments.length)return [];var a1 = this;var a = a2 = null;var n = 0;while(n < arguments.length) {a = [];a2 = arguments[n];var l = a1.length;var l2 = a2.length;for(var i=0; i<l; i++) {for(var j=0; j<l2; j++) {if (a1[i] === a2[j]) a.push(a1[i]);}} a1 = a;n++;} return a.unique();};
if(!String.prototype.trim) { String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g,"") } }
if (!Array.prototype.indexOf)
{
  Array.prototype.indexOf = function(elt /*, from*/)
  {
    var len = this.length;

    var from = Number(arguments[1]) || 0;
    from = (from < 0)
         ? Math.ceil(from)
         : Math.floor(from);
    if (from < 0)
      from += len;

    for (; from < len; from++)
    {
      if (from in this &&
          this[from] === elt)
        return from;
    }
    return -1;
  };
}