/**
 * 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() {
		tb_show("Privacy Policy", "listings?pathway=302&height=350&width=670", false);
		$.bindThickboxClose($.hidePrivacyPolicy);
		return false;
	}

	/** Cancels a VIP registration window
	  *
	  */
	$.hidePrivacyPolicy = function() {
		// remove thickbox window
		return tb_remove();
	}
	
	
	/** 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;
		// override thickbox closing mechanisms
		$("#TB_title").remove();
		$("#TB_overlay").unbind();
		$("#TB_overlay").click(function(){return self.onClose();});
		$("#TB_closeWindowButton").unbind();
		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();
			}
		};
		$("#TB_ImageOff").unbind();
		$("#TB_ImageOff").click(function(){return 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( "value", 0 );
			var rightSliderValue = $("#"+id+"Track").slider( "value", 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);
			}
		}

		$("#"+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}],
			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();
	}

	/**
	 * 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 (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_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 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
		};
		
		// store the name of this form
		var formId = this.attr("id");
		
		// 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));
	}

	/**
	 * 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";
		
		$("#" + addButtonId).click(function(){
			moveitem($("#" + sourceId),$("#" + destId));
		});
		 
		$("#" + removeButtonId).click(function(){
			moveitem($("#" + destId),$("#" + sourceId));
		}); 
		 
		function moveitem(source,dest){
			source.children("option").each(function(x){
				if($(this).attr("selected") == true){
					dest.append($(this).outerHTML());
					$(this).remove();
				}
			});
		}
	}
	
	/**
	* 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;
		 
	}
	
})(jQuery);

