/**
 * rpm JQuery Plugin
 *	overview: This plugin provides utility functions to be used throughout RealPageMaker products.
 *
 *	usage: used on all website pages as a utility class
 *	dependencies: jQuery, jquery.form.js, jquery.backOffice.js (if logged into backoffice), 
 */
(function() {

	/**
	 * Displays the privacy policy
	 *
	 * @return false;
	 */
	$.showPrivacyPolicy = function() {
		$.ajax({
			type: "GET",
			url: "listings?pathway=302",
			success: function(data){
				$.blockUI({
					message:data,
					css: {
						margin:"-175px 0px 0px -335px",
						top:"50%",
						left:"50%",
						width:"670px",
						height:"350px",
						border:"none",
						textAlign:"left",
						cursor:"default",
						backgroundColor:"transparent"
					},
					bindEvents:false
				});
				bindThickboxClose($.hidePrivacyPolicy);
			},
			error: function(data, error){
			}
		});
		return false;
	}

	/** Cancels a VIP registration window
	  *
	  */
	$.hidePrivacyPolicy = function() {
		// remove thickbox window
		return $.unblockUI();
	}
	
	
	/** This function binds the given function to the close event of the Thickbox
	  *
	  * @param onClose function to be called once the thickbox closes
	  */
	$.bindThickboxClose = function(onClose) {
		var self = this;
		self.onClose = onClose;
		$(".blockOverlay").click(function(){return self.onClose();});
		document.onkeyup = function(e){
			if (e == null) { // ie
				keycode = event.keyCode;
			} else { // mozilla
				keycode = e.which;
			}
			if(keycode == 27){ // close
				self.onClose();
			}
		};
		document.onkeydown = function(e){
			if (e == null) { // ie
				keycode = event.keyCode;
			} else { // mozilla
				keycode = e.which;
			}
			if(keycode == 27){ // close
				self.onClose();
			}
		};
	}
	
	/**
	 * formats a number as a dollar currency
	 *
	 * @param num number to be formatted
	 * @param showCents boolean indicating whether to show the cents or not
	 * return formatted number in currency format
	 */
	$.formatAsCurrency = function(num, showCents){
		num = num.toString().replace(/\$|\,/g,'');
		if(isNaN(num))
			num = "0";
		sign = (num == (num = Math.abs(num)));
		num = Math.floor(num*100+0.50000000001);
		cents = num%100;
		num = Math.floor(num/100).toString();
		if(cents<10)
			cents = "0" + cents;
		for (var i = 0; i < Math.floor((num.length-(1+i))/3); i++)
			num = num.substring(0,num.length-(4*i+3))+','+num.substring(num.length-(4*i+3));
		if(showCents)
			return (((sign)?'':'-') + '$' + num + '.' + cents);
		else
			return (((sign)?'':'-') + '$' + num);
	}

	/*
	 * Compares whether two colors are equal. Accepts colors in both hex and RGB format
	 *
	 * @param color1 First color
	 * @param color2 Second color
	 */
	$.compareColors = function(color1, color2){
		// utility functions
		var toHex = function(N) {
 			if (N==null) 
 				return "00";
 			N=parseInt(N);
 			if (N==0 || isNaN(N)) 
 				return "00";
 			N=Math.max(0,N);
 			N=Math.min(N,255);
 			N=Math.round(N);
 			return "0123456789ABCDEF".charAt((N-N%16)/16) + "0123456789ABCDEF".charAt(N%16);
		}

		var RGBtoHex = function(rgbColor) {
			//rgb(123, 116, 165);
			var r = $.trim(rgbColor.substring(rgbColor.indexOf('(')+1, rgbColor.indexOf(',')));
			var g = $.trim(rgbColor.substring(rgbColor.indexOf(',')+1, rgbColor.lastIndexOf(',')));
			var b = $.trim(rgbColor.substring(rgbColor.lastIndexOf(',')+1, rgbColor.lastIndexOf(')')));
			return toHex(r)+toHex(g)+toHex(b)
		};
		
		
		// convert colors to hex if necessary
		if(color1.toLowerCase().indexOf('rgb') >= 0)
			color1 = RGBtoHex(color1);
		if(color2.toLowerCase().indexOf('rgb') >= 0)
			color2 = RGBtoHex(color2);

		// check if they match
		return(color1.replace('#', '') == color2.replace('#', ''));
	}

	/*
	 * shows a png image appropriately
	 *
	 * @param imageId id of image 
	 * @param imageId id of image 
	 */
	$.rpmImageSafe = function(imageId, imagePath, imageWidth, imageHeight, imageClass, imageAlt, browser){
		if((browser.isIE55||browser.isIE6up) && browser.isWin32){
			return('<div style="height:'+imageHeight+'px; width:'+imageWidth+'px; filter:progid:DXImageTransform.Microsoft.AlphaImageLoader (src=\''+imagePath+'\', sizingMethod=\'scale\')" id="'+imageId+'" class="'+imageClass+'"></div>');
		}else{
			return('<img src="'+imagePath+'" width="'+imageWidth+'" height="'+imageHeight+'" name="'+imageId+'" border="0" class="'+imageClass+'" alt="'+imageAlt+'" />');
		}
	}

	/**
	 * detects what browser is being used. Based on:
	 * http://www.dithered.com/javascript/browser_detect/index.html
	 *
	 * @return an object representing the browser
	 */
	$.browserDetectLite = function(){
		var ua = navigator.userAgent.toLowerCase(); 
		var browser = new Array();
		// browser name
		browser.isGecko     = (ua.indexOf('gecko') != -1);
		browser.isMozilla   = (browser.isGecko && ua.indexOf("gecko/") + 14 == ua.length);
		browser.isNS        = ( (browser.isGecko) ? (ua.indexOf('netscape') != -1) : ( (ua.indexOf('mozilla') != -1) && (ua.indexOf('spoofer') == -1) && (ua.indexOf('compatible') == -1) && (ua.indexOf('opera') == -1) && (ua.indexOf('webtv') == -1) && (ua.indexOf('hotjava') == -1) ) );
		browser.isIE        = ( (ua.indexOf("msie") != -1) && (ua.indexOf("opera") == -1) && (ua.indexOf("webtv") == -1) ); 
		browser.isOpera     = (ua.indexOf("opera") != -1); 
		browser.isKonqueror = (ua.indexOf("konqueror") != -1); 
		browser.isIcab      = (ua.indexOf("icab") != -1); 
		browser.isAol       = (ua.indexOf("aol") != -1); 
		browser.isWebtv     = (ua.indexOf("webtv") != -1); 
		browser.isOmniweb   = (ua.indexOf("omniweb") != -1);
		browser.isDreamcast   = (ua.indexOf("dreamcast") != -1);
		
		// spoofing and compatible browsers
		browser.isIECompatible = ( (ua.indexOf("msie") != -1) && !browser.isIE);
		browser.isNSCompatible = ( (ua.indexOf("mozilla") != -1) && !browser.isNS && !browser.isMozilla);
		
		// browser version
		browser.versionMinor = parseFloat(navigator.appVersion); 
		
		// correct version number for NS6+ 
		if (browser.isNS && browser.isGecko) {
			browser.versionMinor = parseFloat( ua.substring( ua.lastIndexOf('/') + 1 ) );
		}
		
		// correct version number for IE4+ 
		else if (browser.isIE && browser.versionMinor >= 4) {
			browser.versionMinor = parseFloat( ua.substring( ua.indexOf('msie ') + 5 ) );
		}
		
		// correct version number for Opera 
		else if (browser.isOpera) {
			if (ua.indexOf('opera/') != -1) {
				browser.versionMinor = parseFloat( ua.substring( ua.indexOf('opera/') + 6 ) );
			}
			else {
				browser.versionMinor = parseFloat( ua.substring( ua.indexOf('opera ') + 6 ) );
			}
		}
		
		// correct version number for Konqueror
		else if (browser.isKonqueror) {
			browser.versionMinor = parseFloat( ua.substring( ua.indexOf('konqueror/') + 10 ) );
		}
		
		// correct version number for iCab 
		else if (browser.isIcab) {
			if (ua.indexOf('icab/') != -1) {
				browser.versionMinor = parseFloat( ua.substring( ua.indexOf('icab/') + 6 ) );
			}
			else {
				browser.versionMinor = parseFloat( ua.substring( ua.indexOf('icab ') + 6 ) );
			}
		}
		
		// correct version number for WebTV
		else if (browser.isWebtv) {
			browser.versionMinor = parseFloat( ua.substring( ua.indexOf('webtv/') + 6 ) );
		}
		
		browser.versionMajor = parseInt(browser.versionMinor); 
		browser.geckoVersion = ( (browser.isGecko) ? ua.substring( (ua.lastIndexOf('gecko/') + 6), (ua.lastIndexOf('gecko/') + 14) ) : -1 );
		
		// platform
		browser.isWin   = (ua.indexOf('win') != -1);
		browser.isWin32 = (browser.isWin && ( ua.indexOf('95') != -1 || ua.indexOf('98') != -1 || ua.indexOf('nt') != -1 || ua.indexOf('win32') != -1 || ua.indexOf('32bit') != -1) );
		browser.isMac   = (ua.indexOf('mac') != -1);
		browser.isUnix  = (ua.indexOf('unix') != -1 || ua.indexOf('linux') != -1 || ua.indexOf('sunos') != -1 || ua.indexOf('bsd') != -1 || ua.indexOf('x11') != -1)
		
		// specific browser shortcuts
		browser.isNS4x = (browser.isNS && browser.versionMajor == 4);
		browser.isNS40x = (browser.isNS4x && browser.versionMinor < 4.5);
		browser.isNS47x = (browser.isNS4x && browser.versionMinor >= 4.7);
		browser.isNS4up = (browser.isNS && browser.versionMinor >= 4);
		browser.isNS6x = (browser.isNS && browser.versionMajor == 6);
		browser.isNS6up = (browser.isNS && browser.versionMajor >= 6);
		
		browser.isIE4x = (browser.isIE && browser.versionMajor == 4);
		browser.isIE4up = (browser.isIE && browser.versionMajor >= 4);
		browser.isIE5x = (browser.isIE && browser.versionMajor == 5);
		browser.isIE55 = (browser.isIE && browser.versionMinor == 5.5);
		browser.isIE5up = (browser.isIE && browser.versionMajor >= 5);
		browser.isIE6x = (browser.isIE && browser.versionMajor == 6);
		browser.isIE6up = (browser.isIE && browser.versionMajor >= 6);
		
		browser.isIE4xMac = (browser.isIE4x && browser.isMac);
		return browser;
	}

	/**
	 * Slider constructor
	 *
	 * @param - id DOM id of the track element
	 * @minVal - minimum value (leftmost value)
	 * @maxVal - maximum value (rightmost value)
	 * @increment - value slider will jump to on drag
	 * @leftStart - starting position of left slider (value)
	 * @rightStart - starting position of right slider (value)
	 * @leftOutput - div ID of left slider value placeholder
	 * @rightOutput - div ID of right slider value placeholder
	 * @format - format of value (used to check if "year")
	 */
	$.createSlider = function(id, minVal, maxVal, increment, leftStart, rightStart, leftOutput, rightOutput, format){
		//for format = year, need to calculate new division depending on current year
		var maxV = parseInt(maxVal);
		increment = parseInt(increment);
		if($.trim(format) == "year"){
			var date = new Date(); 
			maxV = parseInt(date.getFullYear());
		}
		var tWidth = $("#"+id+"Track").width();

		var showValues = function() {
			var leftSliderValue = $("#"+id+"Track").slider( "values", 0 );
			var rightSliderValue = $("#"+id+"Track").slider( "values", 1 );

			if(leftSliderValue == 0){
				$("#"+leftOutput).html("0");
			}else if(parseInt(leftSliderValue) == (maxV + increment)){
				$("#"+leftOutput).html((parseInt(leftSliderValue) - increment) + "+");
			}else{
				$("#"+leftOutput).html(leftSliderValue);
			}
		
			if(rightSliderValue == 0){
				$("#"+rightOutput).html("0");
			}else if(parseInt(rightSliderValue) == (maxV + increment)){
				$("#"+rightOutput).html((parseInt(rightSliderValue) - increment) + "+");
			}else{
				$("#"+rightOutput).html(rightSliderValue);
			}
		}
		//alert("Left Start: " + leftStart + "\nRight Start: " + rightStart + "\nMin: " + minVal + "\nMax: " + maxV);
		$("#"+id+"Track").slider({
			//handle : '.grab',
			//axis : 'horizontal',
			min: minVal,
			max: (maxV + increment),
			range: true,
			//handles: [{id:id+'LeftGrab', start:leftStart}, {id:id+'RightGrab', start:rightStart}], //call for old jQuery UI slider handle set
			values: [leftStart, rightStart],
			//stepping: increment,
			slide : function(e, ui) {
				showValues();
			},
			change : function(e, ui) {
				$.mLSSearchEngineRunSearch();
			},
			stop : function(e, ui) {
				/*
				if($("#"+id+"Track").slider("value", 0 ) == maxV){
					//both handles on far right, put focus on left slider
					$("#"+id+"LeftGrab").css("z-index", 7);
					$("#"+id+"RightGrab").css("z-index", 0);
					$("#"+id+"LeftGrab").focus();
				}else if($("#"+id+"Track").slider("value", 1 ) == increment) {
					//both handles on far left, put focus on right slider
					$("#"+id+"LeftGrab").css("z-index", 0);
					$("#"+id+"RightGrab").css("z-index", 7);
					$("#"+id+"RightGrab").focus();
				}
				*/
			}
		});

		$("#"+id+"LeftGrab").css("cursor", "pointer");
		$("#"+id+"RightGrab").css("cursor", "pointer");
		
		$(document).ready(function(){  //IE Fix 
			$("#"+id+"Track a").each(function(){
				$(this).css("width", "12px");
				$(this).css("height", "24px");
				$(this).css("display", "block");
				$("#"+id+"LeftGrab").css("background-color", "FF9321");
				$("#"+id+"RightGrab").css("background-color", "FF9321");
			});
		});
		showValues();
	}

	/**
	* Range Drop Down Lists Constructor
	* NOTE: setting the selected options were done via the loops and if/else instead of the one line clean jquery way because
	* IE6 is crap.
	* @param leftId - the DOM id of the left drop down list
	* @param rightId - the DOM id of the right drop down list
	* @param minVal - the minimum value of both drop down lists
	* @param maxVal - the maximum value of both drop down lists
	* @param increment - incremental value used to create list of values going from minVal to maxVal
	* @param zeroStartOpt - flag to have first value at zero without minVal having to be set to zero (left slider only)
	* @param overMaxOpt - flag to add extra value to represent option greater than the maximum value (right slider only)
	* @param format - the type of formatting for the option labels
	*/
	$.createDDRange = function(leftId, rightId, minVal, maxVal, leftStart, rightStart, increment, zeroStartOpt, overMaxOpt, format){
		try{
			var minV = parseInt(minVal, 10), maxV = parseInt(maxVal, 10), inc = parseInt(increment, 10), min;
			
			// Round up invalid start values
			if(minV == 0){
				min = inc;
			}else{
				min = minV;
			}
			
			if(leftStart.indexOf("+") == -1){
				if(leftStart % min != 0 && Math.floor(leftStart/min) != inc){
					leftStart = (Math.floor(leftStart / (minVal + increment)) + 1) * (minVal + increment);
				}
			}
			
			if(rightStart.indexOf("+") == -1){
				if(rightStart % min != 0 && Math.floor(rightStart/min) != inc){
					rightStart = (Math.floor(rightStart / (minVal + increment)) + 1) * (minVal + increment);
				}
			}
			
			//var options = "";
			var minOption = "", maxOption = "", maxVLabel = "";
			var optionsLeft = "", optionsRight = "";
			
			// Calculate and create option tags
			if(format === "priceHalfInc") { 
				var label = "";
				for(var i=minV;i<=maxV;i+=inc) {
					if(i < 1000000){
						label = (i/1000) + "k";
						if(i.toString() == leftStart){
							optionsLeft = optionsLeft + "<option value=\"" + i + "\" selected=\"selected\">" + label + "</option>";
						}else{
							optionsLeft = optionsLeft + "<option value=\"" + i + "\">" + label + "</option>";
						}
						
						if(i.toString() == rightStart){
							optionsRight = optionsRight + "<option value=\"" + i + "\" selected=\"selected\">" + label + "</option>";
						}else{
							optionsRight = optionsRight + "<option value=\"" + i + "\">" + label + "</option>";
						}
					}else{
						label = (i/1000000) + "mil";
						if(i.toString() == leftStart){
							optionsLeft = optionsLeft + "<option value=\"" + i + "\" selected=\"selected\">" + label + "</option>";
						}else{
							optionsLeft = optionsLeft + "<option value=\"" + i + "\">" + label + "</option>";
						}
						
						if(i.toString() == rightStart){
							optionsRight = optionsRight + "<option value=\"" + i + "\" selected=\"selected\">" + label + "</option>";
						}else{
							optionsRight = optionsRight + "<option value=\"" + i + "\">" + label + "</option>";
						}
					}
					if(i.toString() == maxV && overMaxOpt){
						maxVLabel = label;
					}
				}
			}else{
				for(var i=minV;i<=maxV;i+=inc){
					if(i.toString() == leftStart){
						optionsLeft = optionsLeft + "<option value=\"" + i + "\" selected=\"selected\">" + i + "</option>";
					}else{
						optionsLeft = optionsLeft + "<option value=\"" + i + "\">" + i + "</option>";
					}
					
					if(i.toString() == rightStart){
						optionsRight = optionsRight + "<option value=\"" + i + "\" selected=\"selected\">" + i + "</option>";
					}else{
						optionsRight = optionsRight + "<option value=\"" + i + "\">" + i + "</option>";
					}
				}
				maxVLabel = maxV;
			}
			
			// Add tags to drop down
			if(zeroStartOpt){
				if(leftStart == "0"){
					optionsLeft = "<option value=\"0\" selected=\"selected\">0</option>" + optionsLeft;
				}else{
					optionsLeft = "<option value=\"0\">0</option>" + optionsLeft;
				}
			}
			
			if(overMaxOpt){
				if(rightStart == maxV.toString() + "+"){
					optionsRight = optionsRight + "<option value=\"" + maxV + "+\" selected=\"selected\">" + maxVLabel + "+</option>";
				}else{
					optionsRight = optionsRight + "<option value=\"" + maxV + "+\">" + maxVLabel + "+</option>";
				}
			}
			
			$("#" + leftId).html(optionsLeft);
			$("#" + rightId).html(optionsRight);
						
			// Create events for drop down
			$("#" + leftId).unbind("change").bind("change", function(){
				//Compensate if overMaxOpt is true
				var curMax = $("#" + rightId).val();
				if(curMax.indexOf("+") != -1){
					curMax = parseInt(maxV, 10) + 1;
				}
				
				if(parseInt($(this).val(), 10) > parseInt(curMax, 10)){
					$("#" + rightId).val($(this).val());
				}
				$.mLSSearchEngineRunSearch();
			});
			
			$("#" + rightId).unbind("change").bind("change", function(){
				//Compensate if overMaxOpt is true
				var curMax = $(this).val();
				var overMax = false;
				if(curMax.indexOf("+") != -1){
					curMax = (maxV + 1);
					overMax = true;
				}
				
				if(parseInt($("#" + leftId).val(), 10) > parseInt(curMax, 10)){
					if(overMax){
						$("#" + leftId).val(maxV);
					}else{
						$("#" + leftId).val($(this).val());
					}
				}
				$.mLSSearchEngineRunSearch();
			});
		}catch(ex){
			alert(ex.name + ": \n" + ex.message); 
		}
		
		//Code below breaks in IE6... please die.
		//$(document).ready(function(){
			//$("#" + leftId + " option").filter("[value='" + leftStart + "']").attr("selected", "selected");
			//$("#" + rightId + " option").filter("[value='" + rightStart + "']").attr("selected", "selected");
		//});
	}
	
	/**
	 * Binds the jQuery datepicker module to any and all input_datetime fields
	 *
	 */
	$.bindDatePickers = function(){
		$('.date').datepicker({
			showOn: 'both',
			firstDay: 1,
			buttonImageOnly: true, 
			buttonImage: '/t/resources/rpm3.0/images/icons/calendar.png',
			dateFormat: 'yy-mm-d'
		});
	}

	/**
	 * Returns a string containing a price formatted with commas
	 *
	 * @param nStr an input value to be formatted
	 * @return string return value containing formatted price string
	 */
	$.formatPrice = function (nStr){
		nStr += '';
		x = nStr.split('.');
		x1 = x[0];
		x2 = x.length > 1 ? '.' + x[1] : '';
		var rgx = /(\d+)(\d{3})/;
		while (rgx.test(x1)) {
			x1 = x1.replace(rgx, '$1' + ',' + '$2');
		}
		return x1 + x2;
	}


	/**
	 * Adds a tree field to an array of fields
	 *
	 * @param fieldId id of field to add
	 * @param fieldIdNumbers field id numbers of all checkboxes in the tree
	 * @param fields a list of fields to add the tree field to
	 */
	$.addTreeField = function(fieldId, fieldIdNumbers, fields){
		var i = fields.length;
		fields[i] = new Array(3);
		fields[i][0] = "input_tree";
		fields[i][1] = fieldId;
		fields[i][2] = fieldIdNumbers;
		return fields;
	}
	
	/**
	 * Adds a range (slider) field to an array of fields
	 *
	 * @param fieldId id of field to add
	 * @param fields a list of fields to add the tree field to
	 */
	$.addRangeField = function(fieldId, fields){
		var i = fields.length;
		fields[i] = new Array(3);
		fields[i][0] = "input_range";
		fields[i][1] = fieldId;
		fields[i][2] = "";
		return fields;
	}
	
	/**
	 * Adds a range (two drop down fields) field to an array of fields
	 *
	 * @param fieldId id of field to add
	 * @param fields a list of fields to add the tree field to
	 */
	$.addDDRangeField = function(fieldId, fields){
		var i = fields.length;
		fields[i] = new Array(3);
		fields[i][0] = "input_DDrange";
		fields[i][1] = fieldId;
		fields[i][2] = "";
		return fields;
	}

	/**
	 * Adds a range (datetime) field to an array of fields
	 *
	 * @param fieldId id of field to add
	 * @param fields a list of fields to add the tree field to
	 */
	$.addDateTimeRangeField = function(fieldId, fields){
		var i = fields.length;
		fields[i] = new Array(3);
		fields[i][0] = "input_datetime_range";
		fields[i][1] = fieldId;
		fields[i][2] = "";
		return fields;
	}

	/**
	 * Adds a checkbox field to an array of fields
	 *
	 * @param fieldId id of field to add
	 * @param fields a list of fields to add the tree field to
	 */
	$.addCheckboxField = function(fieldId, fields){
		var i = fields.length;
		fields[i] = new Array(2);
		fields[i][0] = "input_checkbox";
		fields[i][1] = fieldId;
		return fields;
	}

	/**
	 * Generates a URL friendly serialized string from an array of RPM-type fields (sliders/etc.)
	 *
	 * @param array of non-standard fields
	 */
	$.generateSerializedRPMFields = function(fields){
		var urlString = "";
		for(var i = 0; i < fields.length; i++){
			var temp = "";
			if(fields[i][0] == 'input_range'){
				temp = $.generateSerializedRangeUrl(fields[i][1]);
			}else if(fields[i][0] == 'input_DDrange'){
				temp = $.generateSerializedDDRangeUrl(fields[i][1]);
			}else if(fields[i][0] == 'input_datetime_range'){
				temp = $.generateSerializedDateTimeRangeUrl(fields[i][1]);
			}else if(fields[i][0] == 'input_tree'){
				temp = $.generateSerializedTreeUrl(fields[i][1],fields[i][2]);
			}else if(fields[i][0] == 'input_checkbox'){
				if($('#'+fields[i][1]+':checked').size() > 0)
					temp = fields[i][1]+'=true';
			}
			if(temp != ""){
				// add the '&' if necessary
				if(i > 0){
					urlString = urlString+'&';
				}

				// add the parameter and value
				urlString = urlString+temp;
			}
		}
		return urlString;
	}
	/**
	 * Generates a URL string for a slider
	 *
	 * @param rangeId id of range
	 * @return serialized range values
	 */
	$.generateSerializedRangeUrl = function(rangeId){
		return rangeId+'='+encodeURIComponent($('#'+rangeId+'Left').html())+'&'+rangeId+'='+encodeURIComponent($('#'+rangeId+'Right').html());
	}
	
	/**
	 * Generates a URL string for two range drop downs
	 *
	 * @param rangeId id of range
	 * @return serialized range values
	 */
	$.generateSerializedDDRangeUrl = function(rangeId){
		return rangeId+'='+encodeURIComponent($('#'+rangeId+'Left :selected').val())+'&'+rangeId+'='+encodeURIComponent($('#'+rangeId+'Right :selected').val());
	}
	
	/**
	 * Generates a URL string for a date time range.
	 *
	 * @param rangeId id of range
	 * @return serialized range values
	 */
	$.generateSerializedDateTimeRangeUrl = function(rangeId){
		return rangeId+'='+encodeURIComponent($('#'+rangeId+'Start').val())+'&'+rangeId+'='+encodeURIComponent($('#'+rangeId+'End').val());
	}
	/**
	 * Generates a correctly formatted URL for a Tree field
	 *
	 * @param treeId id of tree
	 * @param elementValues values in tree
	 * @return serialized tree values
	 */
	$.generateSerializedTreeUrl = function(treeId, elementValues){
		var treeString = "";
		var i = 0;
		for(var i = 0; i < elementValues.length-1; i++){
			if(document.getElementById(treeId+elementValues[i]).checked){
				treeString = treeString+treeId+'='+elementValues[i]+'&';
			}
		}
		if(elementValues.length > 0){
			if(document.getElementById(treeId+elementValues[(elementValues.length-1)]).checked){
				treeString = treeString+treeId+'='+elementValues[(elementValues.length-1)];
			}
		}
		return treeString;
	}

	/** clones an object
	  *
	  * @param obj object to be cloned
	  * @param deep boolean as to whether to deep clone or not
	  * @return a clone of the object
	  */
	$.clone = function(obj, deep) {
		var objectClone = new obj.constructor();
		for (var property in obj)
			if (!deep)
				objectClone[property] = obj[property];
			else if (typeof obj[property] == 'object')
				objectClone[property] = obj[property].clone(deep);
			else
				objectClone[property] = obj[property];
		return objectClone;
	}

	/**
	 * preloads an array of images
	 *
	 * @param imgArray array of images to preload
	 */
	$.preloadImages = function(imgArray){
		for(var i = 0; i < imgArray.length; i++){
			jQuery("<img>").attr("src", imgArray[i]);
		}
	}

	/**
	 * Bind to a form binds to a specified form.
	 *
	 * @param opt object containing the configuration properties (
	 *	updateDivId name of the div to update (usually encloses the "form")
	 *	loadingDivId name of the div surrounding the form on which the loading modal is placed
	 *	message message to include in the loading modal
	 *	onRPMSuccessCallback an optional function to be called if response from the submit is "success"
	 *	success an optional function to be called when the function completes that takes the response text
	 *	backOffice optional boolean flag indicating whether this is being called from the back office or not)
	 *  confirm optional string or function to set a confirm message before submitting
	 */
	$.fn.bindToForm = function(opt){
		// configure the function
		var config = {
			updateDivId: opt.updateDivId || null,
			loadingDivId: opt.loadingDivId || null,
			message: opt.message || "Submitting, please wait...",
			onRPMSuccessCallback: opt.onRPMSuccessCallback || function(responseText){return false;},
			success: opt.success || function(responseText){return false;},
			complete: opt.complete || function(XMLHttpRequest, textStatus){return false;},
			backOffice:  opt.backOffice || false,
			confirm: opt.confirm || null,
			multipleForms: opt.multipleForms || false,
			multipleFormId: opt.multipleFormId || null // the outermost div containing for the form element.
		};
		

		// store the name of this form
		var formId = this.attr("id");
		if(opt.multipleForms) {
			formId = config.multipleFormId;
		}
			
		// do error checking to make sure all required options are specified
		if(this == null || config.updateDivId == null || config.loadingDivId == null || config.message == null){
			alert("$().bindToForm missing required parameters");
			return false;
		}else{
			// create options for the form plugin
			var options = {
				//method: 'POST', //activate this when using firebug or error below will occur - will be fixed in Firefox 3.0.3
				/*
				Firebug needs to POST to the server to get this information for url:
				http://localhost:8080/listings?firstName=Anthony&lastName=Abenojar&email_addr=anthony%40realpagemaker.com&home_phone=780-428-3006&fax=&addr=&city=&prov=&coun=&postal_c=&subject=Test&message=TESTING+TESTING!&pathway=21&send=true

				This second POST can interfere with some sites. If you want to send the POST again, open a new tab in Firefox, use URL 'about:config', set boolean value 'extensions.firebug.allowDoublePost' to true
				This value is reset every time you restart Firefox This problem will disappear when https://bugzilla.mozilla.org/show_bug.cgi?id=430155 is shipped.
				*/
				target: '#'+config.updateDivId, //target element(s) to be updated with server response
				beforeSubmit: function (formData, jqForm, options){
					// show the form submission loading window
					$('#'+config.loadingDivId).block({ message: config.message+' <img src="/t/resources/rpm3.0/images/activity/indicator_medium.gif" />' });
					
				},
				success: function(responseText, statusText){
					// hide loading window
					setTimeout(function(){$('#'+config.loadingDivId).unblock();}, 1000);
					//If the RPM framework declares success (RPM_AGENT_AJAX_SUCCESS output) and the onRPMSuccessCallback function != null call it
					if(config.onRPMSuccessCallback != null && $.trim(responseText).toLowerCase() == "success") {
						config.onRPMSuccessCallback(responseText);
					} else {
						$('#'+formId).bindToForm(config);
					}
					
					// run success function
					config.success(responseText);
				},
				complete: function(XMLHttpRequest, textStatus){
					config.complete(XMLHttpRequest, textStatus);
				},
				error: function(data, error){
					alert("Error: could not register: " + error);
					setTimeout(function(){$('#'+config.loadingDivId).unblock();}, 1000);
				}
			};
	
			// run necessary backOffice tasks
			if(config.backOffice)
				$.resetRPMSessionManager();
			
			// bind to the form's submit event 
			//$('#'+formId).submit(function() {
			$('#'+formId).unbind("submit." + formId).bind("submit." + formId, function(){
				if($.isFunction(config.confirm)){
					var confirmFunc = config.confirm;  //Config function must return a boolean...
					if(confirmFunc() == false){
						return false;
					}
				}else if(typeof confirm == "string"){
					if(confirm(config.confirm) == false){
						return false;
					}
				}
				$(this).ajaxSubmit(options);
				return false;
			});
			return false;
		}			
	}
	
	/**
	 * preloads an array of images
	 *
	 * @param imgArray array of images to preload
	 */
	$.fn.clearApplet = function(){
		return this.each( function(){
			if(document.getElementById(this.id)){
				document.getElementById(this.id).innerHTML = "";
			}
		});
	}

	/**
	 * binds a hide/show optional fields button
	 *
	 * @param speed speed at which the hide/show operation takes place (Options: slow, fast)
	 */
	$.fn.bindHideShow = function(speed){
		return this.each(function(){
			var self = $(this);
			self.before('<div style="text-align:center;margin-top:5px;">+ <a href="" id="'+self.attr('id')+'MoreOptions'+'">More Options</a></div>');
			self.prepend('<div style="text-align:center;margin-top:5px;">- <a href="" id="'+self.attr('id')+'LessOptions'+'">Hide Options</a></div>');
			
			$('#'+self.attr('id')+'MoreOptions').click(function(){
				$(this).parent().slideUp(speed, function(){
					self.slideDown(speed);
				});
				return false;
			});
			
			$('#'+self.attr('id')+'LessOptions').click(function(){
				self.slideUp(speed, function(){
					$('#'+self.attr('id')+'MoreOptions').parent().slideDown(speed);
				});
				return false;
			});
			return false;
		});
		
	}
	

	/**
	 * Utility to encode the HTML for the URL
	 *
	 * @param html string with the HTML that needs to be converted
	 * @return HTML encoded into URL format
	 */
	$.encodeEditorHTML = function(content){
		content = content.replace(/\r\n/g,"\n");
		var utftext = "";
		
		for (var n = 0; n < content.length; n++) {
			var c = content.charCodeAt(n);
			if (c < 128) {
				utftext += String.fromCharCode(c);
			}else if((c > 127) && (c < 2048)) {
				utftext += String.fromCharCode((c >> 6) | 192);
				utftext += String.fromCharCode((c & 63) | 128);
			}else {
				utftext += String.fromCharCode((c >> 12) | 224);
				utftext += String.fromCharCode(((c >> 6) & 63) | 128);
				utftext += String.fromCharCode((c & 63) | 128);
			}
		}
	        //return(escape(utftext)); // doesn't encode plus signs (+)
			return(encodeURIComponent(utftext));
	}

	/**
	 * Returns the HTML that composes the whole element.
	 * http://blog.brandonaaron.net/2007/06/17/jquery-snippets-outerhtml/
	 */
	$.fn.outerHTML = function() {
		return $('<div>').append( this.eq(0).clone() ).html();
	};
	
	/**
	*move option items between two multi- selectable combo boxs
	*@param  sourceId combo Box Id, originally hold  some options
	*@param  destId combo Box Id, can add options from the source
	*@param addButtonId  button for moving options from the source to dest combo box
	*@param removeButtonId button for moving options from the dest back to the source combo box
	*/
	//$.initComboBoxTransfer = function(sourceId,destId,addButtonId,removeButtonId){
	$.initComboBoxTransfer = function(componentId){
		
		var sourceId = componentId + "_left"; // _src
		var destId = componentId + "_right"; // _dest
		var addButtonId = componentId + "_add";
		var removeButtonId = componentId + "_remove";
		var sourceCount = 0;
		var destCount = 0;
		
		//initialize counters
		sourceCount = count(sourceId);
		destCount = count(destId);

		//display counters		
		$("#" + sourceId+"Counter").html(sourceCount+" item(s)");
		$("#" + destId+"Counter").html(destCount+" item(s)");
		
	
		$("#" + addButtonId).click(function(){
			moveitem($("#" + sourceId),$("#" + destId));
			sourceCount = count(sourceId);
			destCount = count(destId);
			$("#" + sourceId+"Counter").html(sourceCount+" item(s)");
			$("#" + destId+"Counter").html(destCount+" item(s)");
			
		});
		 
		$("#" + removeButtonId).click(function(){
			moveitem($("#" + destId),$("#" + sourceId));
			sourceCount = count(sourceId);
			destCount = count(destId);
			$("#" + sourceId+"Counter").html(sourceCount+" item(s)");
			$("#" + destId+"Counter").html(destCount+" item(s)");
		}); 
		 
		function moveitem(source,dest){
			source.children("option").each(function(x){
				
				if($(this).attr("selected") == true){
					dest.append($(this).outerHTML());
					$(this).remove();
				}
			});
		}

		function count(id){
			var count = 0;
			$("#"+id).children("option").each(function(x){
				count += 1;
			});
			return count;
		}
	}
	
	/**
	* get the id list from the dest (right) combo box for transferring
	*/
	$.getComboBoxTransferIds = function(componentId){
		var destIds="";
		$("#"+componentId+"_right").children("option").each(function(x){
			destIds += "&" + componentId +"=" + $(this).attr("value");
		  });
		  
		return destIds;
		 
	}

	/**
	* Combines the parameters given into an e-mail address as a way to make it more
	* difficult for e-mail harvesters to find the address.
	*/
	$.obfuscateEmail = function(name, domain){
		location.href = "mailto:" + name + "@" + domain;
	}



})(jQuery);
