jQuery.extend(jQuery.fn,{
	serializeFormToJson : function(selector){
		var o = {};
		var a = this.find('select,input,textarea');
		a = ((selector)?a.filter(selector):a.not("*[skip=skip]"))
		.filter(function() {
			return this.name && !this.disabled && this.type!='submit';
		});
		jQuery.each(a, function() {
			var v = (!(/checkbox|radio/).test(this.type) || this.checked) ? (jQuery(this).val()) : '';
			if (o[this.name] && o[this.name].length) {
				if(v !== ''){
					if (!o[this.name].push) {
						o[this.name] = [o[this.name]];
					}
					o[this.name].push(v|| '');
				}
			} else {
				o[this.name] = v || '';
			}
		});
		return o;
	}
});

/**
 * carnival
 * @namespace
 */
var carnival = function($,document,window,undefined){
	var config= {};

	/**
	 * init - Constructor for carnival object
	 * @constructor
	 */
	var init = function(configs){
		config = {
			currentDomain : document.domain +(document.location.port.length ? (':'+document.location.port) : ''),
			cookieDomain : getCookieDomain(),
			c_mid : 'c_mId',
			c_unm : 'c_unm',
			c_llp : 'c_llp',
			userPath : '/registration',
			subscribePath : '/registration/subscription',
			modalCloseButton : '/hive/images/close.png',
			activateMessages : 'true'
		};
		config = carnival.utils.merge(config,configs);
		
		if(!config.WSHostname){config.WSHostname = config.hostname;}
		carnival.user.getCookieProfile();
		handshake();
		carnival.utils.listener.listen('handshake',handshake);
	};

	/**
	 * getCookieDomain - gets the proper domain name that cookies should be set on
	 */
	var getCookieDomain = function(){
		var dDom = document.domain;
		return dDom.match(/^[0-9]{1,3}(\.[0-9]{1,3}){3}$/)?
			dDom //ip address
			:(dDom.split('.').length<3)?
				"."+dDom
				:!dDom.match(/\.trb$/)?
					"."+dDom.split('.').slice(1).join('.')
					:dDom.match(/\.(testprod|test|design|local)\.(tii|tila)\.trb$/)?
						"."+dDom.split('.').slice(-4).join('.')
						:"."+dDom.split('.').slice(-3).join('.');
	};

	/**
	 * handshake - method for determining actions based on URL parameters
	 */
	var handshake = function(argToken){
		// Getting URL var by its name
		var token = carnival.utils.getUrlVar('token');
		// Getting URL var by its name
		var isLogout = carnival.utils.getUrlVar('logout');
		
		if(token===undefined && argToken!==undefined){
			token = argToken;
		}
		
		
		if(isLogout == 'true' && argToken===undefined){
			if(carnival.user.isLoggedIn()){
				carnival.user.logout();
			}
			$(function(){carnival.utils.listener.fire('_carnival_after_logout_success');});
		} else {
			if( token !== undefined && !carnival.user.isLoggedIn() ){
				if(!!token) {
					carnival.user.getBasicUserInfo(token,function(data){
						if(data.displayName=="trb_system"||data.id=="trbsignontoken_qaz_wsx"){
							carnival.user.carnivalLogout();
						}else if(data.isEmailExists == 'true' && data.isEmailVerified == 'true' && data.isConsumerProfileVerified !='true'){
							$(function(){carnival.utils.listener.fire('_carnival_email_taken',{data:[data.sameEmailProvider]});});
						}else{
							$.cookies.del(configuration('c_unm'),{domain:configuration('cookieDomain'),path:'/'});
							$.cookies.del(configuration('c_llp'),{domain:configuration('cookieDomain'),path:'/'});
							$(function(){carnival.utils.listener.fire('_carnival_after_handshake_success');});
						}
					});
				} else {
					carnival.user.carnivalLogout();
				}
			} else if(carnival.utils.getUrlVar('verified') == 'true'){
				$(function(){carnival.utils.listener.fire('_carnival_activate_done');});
			} else if(carnival.utils.getUrlVar('verified') == 'false'){
				$(function(){carnival.utils.listener.fire('_carnival_activate_error');});
			}	 else if(token !== undefined){
				window.location = carnival.utils.cleanLocation();
			} else if(carnival.utils.getUrlVar('reset_flag') !== undefined){
				var rFlag = carnival.utils.getUrlVar('reset_flag');
				if(rFlag ==='true'){
					$(function(){carnival.utils.listener.fire('_carnival_pass_create',
						{data:[{
							h_token:carnival.utils.getUrlVar('h_token'),
							email_token:carnival.utils.getUrlVar('email_token')
						}]}
					);});
				}else if(rFlag ==='false'){
					$(function(){carnival.utils.listener.fire('_carnival_pass_create_error');});
				}
			}
		}
	};

	var afterHandShakeSuccess = function(func,scope){
		if(func && func.constructor == Function){
			carnival.utils.listener.listen('_carnival_after_handshake_success',func,scope);
		}
	};

	var afterHandShakeFail = function(func,scope){
		if(func && func.constructor == Function){
			carnival.utils.listener.listen('_carnival_after_handshake_fail',func,scope);
		}
	};

	/**
	 * configuration - Dynamic getter/setter for carnival configurations.  If no arguments
	 *   are passed, config object is returned.
	 * @param {String|Object} key Can be a string containing the name of the configuration
	 *   or an object containing configurations.
	 * @param {Object} value Value of cofig element set
	 */
	var configuration = function(key,value){
		if(key){
			if(key.constructor == String) {
				if(value!==undefined&&value!==null){
					config[key] = value;
					return value;
				}
			} else {
				config = carnival.utils.merge(config,key);
			}
			return config[key];
		}
		return config;
	};

	// return public methods
	return {init:init,configuration:configuration,afterHandShakeSuccess:afterHandShakeSuccess};
}(jQuery,document,window);


/**
 * modal - All functions needed for modal popup window
 * @namespace
 */
carnival.modal = function($,window,document,undefined){

	/**
	 * contentEl - jQuery object around DomNode to hold content
	 * initialized - boolean flag if modal has been initialized
	 * @private
	 */
	var contentEl,initialized = false;

	/**
	 * popit - function for opening a modal popup
	 * @param {String|DomNode} contents either a string containing the content or an element with an href to open
	 * @param {Function} callback
	 * @param {Function} closeCallback calback to call if window close
	 * @returns {Boolean} always false to stop further processing of link tags
	 */
	var popit = function(contents,callback,closeCallback) {
	
		if(!initialized) {init();}
		//fix for selects showing above any element regardless of zindex
		if ( $.browser.msie && (/6.0/).test(navigator.userAgent) ) {
			$('select').hide();
		}
		$('#carnivalModalWrapper').width('');
		waitForIt();
		insertContent(contents,function(){
			positionIt.call(); 
			if(callback != null){ callback.call(); }
		});
		if(closeCallback){carnival.utils.listener.listen('popitClose',closeCallback);}
		setTimeout(function(){
			$(document).bind('keypress.carnivalModal',closeHandler);
		},200);
		return false;
	};

	/**
	 * dropit - closes a modal window
	 */
	var dropit = function(cancelCallback){
		cancelCallback = (cancelCallback === true);
		$(document).unbind('.carnivalModal');
		$('#carnivalModal,#carnivalModalOverlay').hide();
		//fix for selects showing above any element regardless of zindex
		if ( $.browser.msie && (/6.0/).test(navigator.userAgent) ) {
			$('select').show();
		}
		$('#'+config.contentId).html('');
		if(!cancelCallback){carnival.utils.listener.fire('popitClose',{remove:true});}
		else{carnival.utils.listener.kill('popitClose');}
		try{window.scroll(0,0);}catch(e){}	
		try{window.scrollTo(0,0);}catch(f){}
	};

	/**
	
	 * init - initializes the modal window object
	 * @constructor
	 * @param {Object} configs JSON object with overrides of default configuration
	 */
	var init = function(configs){
		if(!initialized) {
			$('body').append(popWrapper());
			config = carnival.utils.merge(config,configs);
			contentEl = $('#'+config.contentId);
			preloadWaitImg();
			attachModals();
			initialized = 1;
		}
	};

	/**
	 * positionIt - Moves modal window into appropriate location
	 * @private
	 */
	var positionIt = function(){
		var win = $(window);
		$('#carnivalModal').show().css({
			'top': win.scrollTop() + 40 + 'px',
			'left': win.width() / 2 - ($('#carnivalModal').width() / 2) + 'px'
		});
		$('#carnivalModalOverlay').show().fadeTo(0,0.5);
	};

	/**
	 * attachModals - automatically adds popup to links of class carnivalModal
	 * @private
	 */
	var attachModals = function(){
		$('.carnivalModal').click(function(){
			return popit(this);
		});
	};

	/**
	 * preloadWaitImg - called in contructor.  Prefetches the ajax load image
	 * @private
	 */
	var preloadWaitImg = function() {
		var img = new Image();
		img.src = config.waitImg;
	};

	/**
	 * waitForIt - inserts loading icon into modal window
	 * @private
	 */
	var waitForIt = function() {
		contentEl.html('<div style="width:200px;height:300px;text-align:center"><img style="display:inline;padding-top:130px" src="'+config.waitImg+'" /></div>');
		positionIt();
	};

	/**
	 * insertContent - inserts into modal window. called by popit
	 * @private
	 * @param {String|DomNode} contents What goes in
	 * @param {Function} callback What comes out
	 */
	var insertContent = function(contents,callback) {
		if(contents.constructor == String && !(/^https?\:\/\//.test(contents))) {
			contentEl.html(contents);
			callback.call(this);
		} else {
			try{
				var target = (contents.constructor == String) ? contents : $(contents).attr('href');
				if(carnival.core.isExternal(target) || ((contents.constructor != String) && $(contents).attr('rel') && $(contents).attr('rel') != '')) {
					var rel = $(contents).attr('rel');
					contentEl.html('<iframe frameBorder=0 border=0 style="border:0px" '+rel+' src="'+target+'"></iframe>');
					callback.call(this);
				} else {
					var that = this;
					$.ajax({
						type:'GET',
						url: target,
						success:function(data){
							contentEl.html(data);
							callback.call(that);
						}
					});
				}
			} catch(e) {}
		}
	};

	/**
	 * config - default values for modal window
	 * @private
	 */
	var config = {
		contentId : 'carnivalContent',
		waitImg : '/images/ajax-loader.gif'
	};

	/**
	 * closeHandler - handles keypress and mouseclick events
	 * @private
	 * @param {Event} e event that triggered call
	 */
	var closeHandler = function(e) {
		if(e.keyCode==27 || (e.type!='keypress' && !($(e.target).parents('#carnivalModal')[0]))){
			dropit();
			return false;
		}
	};

	/**
	 * popWrapper - html for modal window
	 * @private
	 */
	var popWrapper = function(){
		if(config.popWrapper || carnival.configuration('popWrapper')){
			return config.popWrapper || carnival.configuration('popWrapper');
		}
	
		return '<div id="carnivalModal" style="display:none;">' +
			'<div id="carnivalModalWrapper">'+
				'<div id="carnivalModalHead" >'+
					'<a id="carnivalModalClose" href="javascript:carnival.modal.dropit();" ><span class="carnivalCloseButton" >Close</span></a>'+
				'</div>'+
				'<div id="'+config.contentId+'" class="full clearfix" >'+
				'</div>'+
			'</div>'+
		'</div>'+
		'<div id="carnivalModalOverlay" ></div>';
	};

	// return public methods
	return {popit:popit,dropit:dropit,init:init,positionIt:positionIt};
}(jQuery,window,document);

/**
	* tooltip - Listener for tooltip popup
	* Required Markup:
	* <div class="ssor-help"></div>
  * <div class="ssor-toolTip" style="display:none;">[copy]</div>
	*/
carnival.tooltip = function($,window,document,undefined){
	var tooltip,myTimer=null;

	var tip = function(){
		$('.ssor-help').mouseover(function() {
			var elem = $(this);
			tooltip = elem.next('.ssor-toolTip');
			var help_position = elem.position();
			$(tooltip).css({'top':help_position.top - $(tooltip).height() - 20, 'left':help_position.left + 10});
			$(tooltip).show('slow');
			if(myTimer) { clearTimeout(myTimer); }
		});

		$('.ssor-help').mouseout(function(){
			hideTooltip(tooltip);
			$(tooltip).mouseover(function () {
				if(myTimer) { clearTimeout(myTimer); }
				$(tooltip).mouseout(function () {
					hideTooltip(tooltip);
				});
			});
		});
	}

	var hideTooltip = function(elm) {
		myTimer = setTimeout(function() { 
			$(elm).hide('slow');
		}, 1000);
	};
	
	return tip;
}(jQuery,window,document);


/**
 * forms
 * @namespace
 */
carnival.forms = function($,document,window,undefined){

	if(!!($.validator)) {
		// add custom validators to validate plugin
		$.validator.addMethod("stopchars", function(value, element, param) {
			var p = new RegExp('['+param+']','g');
			return !(value.match(p));
		}, 'Please only enter only alphanumeric characters');
		
		$.validator.addMethod("validationrule", function(value, element, param) {
			var p = new RegExp(param,'g');
			return !(value.length && !(value.match(p)));
		}, 'This field is invalid');
		
		$.validator.addMethod("profane", function(value, element) {
			var fullMatch = "\x73\x68\x69\x74\x7C\x66\x75\x63\x6B\x7C\x63\x6F\x63\x6B\x73\x75\x63\x6B\x65\x72\x7C\x61\x73\x73\x28\x68\x6F\x6C\x65\x7C\x6D\x75\x6E\x63\x68\x7C\x77\x69\x70\x65\x29\x7C\x62\x69\x74\x63\x68\x7C\x64\x69\x63\x6B\x68\x65\x61\x64\x7C\x6A\x61\x63\x6B\x6F\x66\x66\x7C\x77\x68\x6F\x72\x65\x7C\x66\x61\x67\x67\x6F\x74\x7C\x28\x6E\x7C\x77\x29\x69\x67\x67\x28\x65\x72\x7C\x61\x7A\x29\x7C\x74\x69\x74\x74\x69\x65\x73\x7C\x28\x62\x6C\x6F\x77\x7C\x68\x61\x6E\x64\x29\x6A\x6F\x62\x7C\x66\x65\x6C\x6C\x61\x74\x69\x6F";
			
			var halfMatch = "\x28\x5E\x7C\x5B\x5E\x61\x2D\x7A\x5D\x29\x28\x61\x73\x73\x7C\x74\x77\x61\x74\x7C\x63\x6F\x63\x6B\x7C\x63\x75\x6D\x7C\x74\x69\x74\x74\x69\x65\x73\x7C\x6B\x69\x6B\x65\x7C\x73\x70\x69\x63\x7C\x28\x61\x7C\x6D\x79\x7C\x79\x6F\x75\x72\x29\x5B\x5E\x61\x2D\x7A\x5D\x2B\x64\x69\x63\x6B\x7C\x70\x69\x73\x73\x7C\x63\x75\x6E\x74\x7C\x74\x69\x74\x73\x7C\x66\x61\x67\x7C\x68\x6F\x6D\x6F\x7C\x70\x75\x73\x73\x79\x7C\x63\x75\x6E\x74\x7C\x6E\x69\x67\x72\x61\x73\x7C\x70\x69\x6E\x63\x68\x65\x29\x28\x24\x7C\x5B\x5E\x61\x2D\x7A\x5D\x29";
			return !(value.match(new RegExp(fullMatch,"gi"))) && !(value.match(new RegExp(halfMatch,"gi")));
		}, 'This field is unacceptable');
	
		$.validator.addMethod("birthYear", function(value, element) {
			var inMonth = $('#birthMonth').val();
			var inDay = $('#birthDay').val();
			var inYear = $('#birthYear').val();
			if(inMonth == "" || inDay == "" || inYear == ""){return true;}
			var outDate = new Date(inMonth+"/"+inDay+"/"+inYear);
			if(/Invalid|NaN/.test(outDate)){return false;}
			
			return (
				inMonth == (outDate.getMonth()+1) && inDay == outDate.getDate() && inYear == outDate.getFullYear()
			);
			
		}, 'This is an invalid date');
	}
		
	/**
	 * form - All functions needed for processing and populating forms.
	 * @constructor
	 */
	var form = function(formid){
		this.formEl = $('#'+formid);
		this.addValue('productCode',carnival.configuration('product'));
	};

	/**
	 * carnival.forms.form prototype functions
	 * @memberOf carnival.forms.form.prototype
	 */
	form.prototype = {
		
		/**
		 * addValue - adds a hidden value field to a form
		 * @param {String} key the Name attribute of the value
		 * @param {String} value the value of the attribue 
		 */
		addValue : function(key,value){
			if($('input[name='+key+']').length) {
				$('input[name='+key+']').val(value);
			} else {
				this.formEl.append('<input type="hidden" name="'+key+'" value="'+value+'"/>');
			}
		},
	
		serializeForm : function(selector){
			return this.formEl.serializeFormToJson(selector);
		},

		/**
		 * showRequired - adds markers to labels to required field
		 *   Can be changed using setShowRequired.
		 * @private
		 * @param {String} key Name of input to make required.
		 */
		showRequired : function(key){
			this.formEl.find('label[for='+key+']').addClass('required_label').append(' <span>*</span>');
		},

		/**
		 * makeRequired - adds rules for required field validation.
		 * @private
		 * @param {String} formid The id of the form to populate
		 * @param {Object} data JSON Object with required fields
		 */
		makeRequired : function(data){
			var el;
			for(var i = data.length;i--;){
				el = this.formEl.find('*[name='+data[i]+']');
				el.addClass('required');
				if(data[i] == 'email'){el.addClass('email');}
				if(data[i] == 'userName'){el.addClass('userName');}
				this.showRequired(data[i]);
				
				if(el[0].id.match(/birth/)){
					el.addClass('birthYear');
				}
			}
		},
		
		/**
		 * addValidationClasses - use profile rules to add validation classes to input fields
		 * @param {Object} data the Profile rules to add to the fields 
		 */
		addValidationClasses : function(data){
			var el;
			for (var i = data.length;i--;) {
				el = this.formEl.find('input[name='+data[i].name+']');
				if( data[i].maxLen != undefined){
					el.attr('maxlength',data[i].maxLen);
				}
				if( data[i].minLen != undefined){
					el.attr('minlength',data[i].minLen);
				}
				if( data[i].stopChars != undefined){
					el.attr('stopchars',data[i].stopChars);
				}
				if( data[i].validationRule != undefined){
					el.attr('validationRule',data[i].validationRule);
				}
			}
		},
	
		/**
		 * makeReadonly - adds readonly fields .
		 * @private
		 * @param {String} formid The id of the form to populate
		 * @param {Object} data JSON Object with readonly fields
		 */
		makeReadonly : function(data){
			for (var i = data.length;i--;) {
				if('string' == typeof carnival.user.profile(data[i]) && carnival.user.profile(data[i]).length){
					this.formEl.find('input[name='+data[i]+']').attr('readonly','readonly');
				}
			}
		},
		
		makeProfane : function(){
			this.formEl.find('input:visible').addClass('profane');
		},
	
		makeForm : function(data){
			carnival.forms.replicateDataPoints(this.formEl[0],data);
		},
	
		/**
		 * formatForm - adds required fields and redonly fields to form
		 * @param {Object} data JSON object with data
		 * @param {Boolean} build whether or not to dynamically build the form
		 */
		formatForm : function(data,build){
			//if(build){this.makeForm(data.displayAttrs);}
			this.makeProfane();
			if(data.nonEditAttrs){this.makeReadonly(data.nonEditAttrs);}
			if(data.requiredAttrs){this.makeRequired(data.requiredAttrs);}
			if(data.attrRules){this.addValidationClasses(data.attrRules);}
			if('true' == carnival.user.profile('isEmailVerified')){
				if(data.toBeVerifiedAttrs){
					this.makeReadonly(data.toBeVerifiedAttrs);
				}
			}
		},
		
		
		/**
		 * populateData - puts data into form fields
		 * @param {String} formid The id of the form to populate
		 * @param {Object} data JSON Object with data to go into form
		 */
		populateData : function(data) {
		
			var el;
			
			//special case for email address array
			var emails = data.emailArray;
			
			for (var key in data) {
				if (data.hasOwnProperty(key) && !!(data[key]) ){
					el = this.formEl.find('input[name='+key+'][type!=checkbox][type!=radio]');
					
					//*********************
					// Convention: for views (spans) and edit fiels (forms) on the same page,
					// append a "v" to the end of your spans to solve naming conflict
					//**********************
					var elv = this.formEl.find('span#'+key+"v");
					elv.html(data[key]);
					//end profile fix
					
					if(el.length){
						el.val(data[key]);
					} else {
						try{
							el = this.formEl.find('[value='+key+'][type=checkbox],[name='+key+'][type=radio][value='+data[key]+']');
						}catch(e){el=[];}
						if(el.length){el.attr('checked',true);
						} else {
							el = this.formEl.find('[name='+key+'][type=checkbox]');
							if(el.length){el.attr('checked',data[key]);}
							else {
								el = this.formEl.find('select[name='+key+']');
								if(el.length ){
									if(key == 'email'){
										for(var i = emails.length;i--;){
											el.append($('<option ></option>').val(emails[i]).html(emails[i]));
										}
									}
									try {el.val(data[key]);}
									catch(ex) {setTimeout(function(){el.val(data[key]);},10);}
								}else {
									el = this.formEl.find('span#'+key);
									el.html(data[key]);
								}
							}
						}
					}
				}
			}
		},
	
		populateErrors : function(data){
			var error = '';
			data = data || {};
			carnival.captcha.reload();
			//must exist and must be array
			if( data.vErrs ) {
				var errors = data.vErrs;
				if(!errors.push){errors = [data.vErrs];}
				for (var i = errors.length;i--;) {
					if( carnival.forms.errCodes[errors[i].errCode] != undefined){
						error = carnival.forms.errCodes[errors[i].errCode];
					} else {
						error = errors[i].errMsgs;
					}
					var errSelect = this.formEl.find("*[name="+errors[i].attrName+"]");
					if(errSelect.length){
						errorplacement('<label for="'+errors[i].attrName+'" generated="true" class="error">'+error+'</label>',errSelect);
					} else {
						this.formError(error);
					}
				}
			} else if(data.errMsgs){
				if( carnival.forms.errStatus[data.status] === undefined ){
					this.formError(data.errMsgs);
				} else {
					this.formError(carnival.forms.errStatus[data.status]);
				}
			} else {
				this.formError('An error has occured. Please try again.');
			}
		},
	
		formError : function(msg){
			if(!this.formEl.children('div.carnival-error').length){
				this.formEl.prepend('<div class="carnival-error" style="display:none" ></div>');
			}
			var el = this.formEl.children('div.carnival-error');
			el.html(msg).show();
		},

		flashMessage: function(msg){
			if(!this.formEl.children('div.carnival-flash').length){
				this.formEl.prepend('<div class="carnival-flash" style="display:none" ></div>');
			}
			var el = this.formEl.children('div.carnival-flash');
			el.html(msg).show();
		}
	};
	
	var errCodes = {};

	/**
	 * addErrorCode - method for adding custom error messages
	 * @param {String} code The code for the error message to override
	 * @param {String} message The custom message
	 */
	var addErrorCode = function(code,message){
		errCodes[code] = message;
	};

	var errStatus = {
		'3': 'An error occured.  Please try again.'
	};

	var addErrorStatus = function(code,message){
		errStatus[code] = message;
	};

	/**
	 * replicateDataPoints - Duplicate an HTML template and populates it with data
	 *   This method uses html comments as a data container.  The nodeValue of the
	 *   first comment in the container becomes the HTML template.
	 * @param {String|DomNode} moldContainer Container of HTML template to be duplicated
	 * @param {Object} values Data to be injected into template.
	 */
	var replicateDataPoints = function(container,values,templateContainer) {
		container = $(container);
		templateContainer = $(templateContainer || container);
		var template = (templateContainer.length ? templateContainer : container)[0].firstChild.nodeValue;
		for (var key in values) {
			if (values.hasOwnProperty(key)) {
				template = template.replace(new RegExp('\\$\\{'+key+'\\}','g'),carnival.utils.escapeHTML(values[key]));
			}
		}
		container.append(template.replace(/\$\{[^\}]*\}/g,''));
	};

	var createFormFields = function(location,data,templateContainer){
		for( var j = data.length, i = j+1 ; --i ;){
		    carnival.forms.replicateDataPoints(location,data[j-i], templateContainer);
		}
	};

	var errorplacement = function(er, element) {
		var el = element.siblings('span.signon-error') ;
		if(!el.length) {
			el = $('<span class="signon-error"><span>').insertAfter(element);
		}
		el.html(er);
	};

	return {
		form:form,replicateDataPoints:replicateDataPoints,
		createFormFields:createFormFields,errorplacement:errorplacement,
		addErrorCode:addErrorCode,errCodes:errCodes,
		addErrorStatus:addErrorStatus,errStatus:errStatus
	};
}(jQuery,document,window);


/**
 * utils
 * @namespace
 */
carnival.utils = function($,window,document,undefined){
	var // keep all declarations under one var
	/**
	 * birthyear - makes sure that age is greater than 13
	 * @param {String} yearid the id for the select element with year values
	 * @param {String} monthid the id for the select element with month values
	 * @param {String} dayid the id for the select element with day values
	 */
	 birthyear = function(yearid,monthid,dayid) {	
		yearid = yearid || 'birth_year_span';
		monthid = monthid || 'birth_month_span';
		dayid = dayid || 'birth_day_span';
		var yearspan, monthspan, dayspan,
				today = new Date(), // better validate serverside later;
		checkYear = function(){
			if(yearinput.val() == today.getFullYear() - 13) {
				monthspan.css({display:'block'});
			}
		},
		checkMonth = function(){
			if(monthinput.val() > today.getMonth() ) {
				dayspan.css({display:'block'});
			}
		};
		if((yearspan = $('#'+yearid)) && (monthspan = $('#'+monthid)) && (dayspan = $('#'+dayid))) {
			yearinput = yearspan.find('select');
			monthinput = monthspan.find('select');
			yearinput.bind('focus change',checkYear);
			monthinput.bind('focus change',checkMonth);
		}
	},

	createHiddenFrame = function(location){
		var frameId = 'xd_frame_'+(new Date).getTime()+Math.floor(Math.random()*1001);
		var e = document.createElement('div');
		e.innerHTML =  '<iframe id="'+frameId+'" src="'+location+'"  ></iframe>';
		document.body.appendChild(e.firstChild);
		var frameEl = document.getElementById(frameId);
		frameEl.src = location;
		return false;
	},

	cleanLocation = function(){
		return document.location.toString().replace(/(?:(\?)|&)(reset_flag|email_token|h_token|logout|token|verified)=[^&]*/g,'$1').split("#")[0];
	},

	/**
	 * serializeJSON - turns JSON to string.  Uses built in
	 *   JSON.stringify if available
	 */
	serializeJSON = /* (typeof JSON != 'undefined' && !!(JSON.stringify) ) ? JSON.stringify : */ function (obj) {
		var t = typeof (obj);
		if (t != "object" || obj === null) {
			if (t == "string"){ obj = '"'+obj+'"'; }
			return obj;
		} else {
			var n, v, json = [], arr = (obj && obj.constructor == Array);
			for (n in obj) {
				if (obj.hasOwnProperty(n)) {
					v = obj[n]; t = typeof(v);
					if (t == "string") {v = '"'+v+'"';}
					else if (t == "object" && v !== null) {v = serializeJSON(v);}
					json.push((arr ? "" : '"' + n + '":') + String(v));
				}
			}
			return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}");
		}
	},

	parseJSON = function(stringJSON){
		var data = '';
		try{data = (new Function("return "+stringJSON))();}catch(e){try{data=eval("("+stringJSON+")");}catch(f){}}
		return data;
	},

	/**
	 * sameHeight - adjusts elements to all have the same height
	 * @param elems {Object} elements that need to adjusted
	 * @param colCount {Number} allows for grouping of x number of elements at a time
	 */
	sameHeight = function(elems,colCount){
		colCount = colCount || elems.length;
		var totalCount = elems.length;
		elems.css({height:""});
		for(var i = 0;i-colCount <= totalCount;i += colCount){
			var heights = [0],j;
			for(j = i;(j==i || (j % colCount)) && j < totalCount;j++){
				heights.push($(elems[j]).height());
			}
			heights = Math.max(heights);
			for(j = i;(j==i || (j % colCount)) && j < totalCount;j++){
				$(elems[j]).height(heights);
			}
		}
	},

	/**
	 * merge - merges two objects
	 * @param {Object} original Original object
	 * @param {Object} update Object to merge in.  These values overwrite.
	 * @returns {Object} Merged object
	 */
	merge = function(original,update){
		var value='';
		for (var key in update) {
			if (update.hasOwnProperty(key)) {
				value = ((!!update[key]) && update[key].constructor == String)?update[key].replace(/<[^>]*>/g,''):update[key];
				original[key] = value;
			}
		}
		return original;
	},

	/**
	 * popup - open popup window
	 *   opens empty window first, then opens url inside that window to set referrer in IE
	 * @param {String} URL url of page to open
	 */
	popUp = function(){
		var page = null;
		var _popUp = function(URL) {
			if(page != null){
				page.close();
			}
			page = window.open('','_carnival_popup','toolbar=0,scrollbars=1,location=0,statusbar=1,menubar=0,resizable=1,width=768,height=480');
			$(window).bind("unload", function() {
				page.close();
			});
			if(URL){page.location = URL;}
			try{page.focus();}catch(e){}
		};
		return _popUp;
	}(),

	/**
	 * listener - Singleton.  used to subscribe to and fire events
	 */
	listener = function(){
		/**
		 * @param {Object} evts the container for all event listeners
		 * @private
		 */
		var evts = {};

		/**
		 * listen - subscribe to events
		 * @param {String} evtName the name to be listening for
		 * @param {Function} method the function to be fired
		 * @param {Object} scope the value for the 'this' object inside the function
		 */
		var listen = function(evtName,method,scope) {
			if(method && method.constructor == Function) {
				evts[evtName] = evts[evtName]||[];
				evts[evtName][evts[evtName].length] = {method:method,scope:scope};
			}
		};

		/**
		 * fire - call all the event handlers
		 * @param {String} evtName the name of the event listner to invoke
		 * @param {Boolean} remove Whether or not to unsubscribe all listeners
		 */
		var fire = function(evtName,args) {
			args = args || [];
			if( evts.hasOwnProperty(evtName)) {
				var evt = evts[evtName].reverse();
				for(var i=evt.length;i--;) {
					try{
						evt[i].method.apply(evt[i].scope||[],(args.data||[]));
					}catch(e){
						if(args && args.data && args.data.push) {
							evt[i].method(args.data[0]);
						} else {
							evt[i].method();
						}
					}
				}
				if(args.remove) {kill(evtName);}
			}
		};
	
		var listening = function(evtName){
			return !!evts[evtName];
		};
		
		var kill = function(evtName){
			if(evts.hasOwnProperty(evtName)) {delete evts[evtName];}
		};
	
		// return the public functions
		return {listen:listen,fire:fire,kill:kill,listening:listening};
	}(),

	getUrlVar = function(){
		var vars;
	
		/**
		 * getVar - Get values from url parameters.  If no arguments are passed,
		 *   object with all URL parameters is returned.
		 * @param {String} key URL parameter to retrieve.
		 */
		var getVar  = function(key){
			if(!key){return getVars();}
			return getVars()[key];
		};
	
		/**
		 * getVars - Get object with all URL parameters
		 * @private
		 * @returns {Object} all URL parameters
		 */
		var getVars = function(){
			if(vars){return vars;}
			vars = {};
			var hashes;
			if(!!(hashes = window.location.href.replace(/(\?[^\?]*)\?/g,'$1&').split('?')[1])) {
				var hash;
				hashes = hashes.split('&');
				for(var i=hashes.length;i--;){
					hash = hashes[i].split('=');
					vars[hash[0]] = hash[1];
				}
			}
			return vars;
		};
	
		return getVar;
	}(),

	contains = function(arr,obj){
		if(arr.indexOf){
			return arr.indexOf(obj)<0;
		}
		return $.inArray(arr,obj);
	},
	
	uniqueArray = function(a) {
		var retVal = [];
		for(i=0;i<a.length;i++){
			if(contains(retVal,a[i])){
				retVal.push(a[i]);
			}
		}
		return retVal;
	},
	
	escapeHTML = function(input){
		return input.replace(/&/g,'&amp;').replace(/>/g,'&gt;').replace(/</g,'&lt;').replace(/"/g,'&quot;');
	},
	
	cookies = $.cookies; // this can be changed to another cookie lib later if we need to
	
	return {birthyear:birthyear,listener:listener,merge:merge,sameHeight:sameHeight,cookies:cookies,
		getUrlVar:getUrlVar,serializeJSON:serializeJSON,popUp:popUp,escapeHTML:escapeHTML,
		cleanLocation:cleanLocation,uniqueArray:uniqueArray,parseJSON:parseJSON,createHiddenFrame:createHiddenFrame};
}(jQuery,window,document);


/**
 * user - functions related to userprofile
 * @namespace
 */
carnival.user = function($,window,document,undefined){
	var data={};
	var _profile={}, _provider = {};
	var config = carnival.configuration;

	var profile = function(key,value){
		if(key){
			if(key.constructor == String){
				if(value!==undefined&&value!==null){
					if(value.constructor == String){value = value.replace(/<[^>]*>/g,'');}
					_profile[key] = value;
				}
				return _profile[key];
			} else {
				_profile = carnival.utils.merge(_profile,key);
			}
		}
		return _profile;
	};

	var provider = function(key,value){
		if(key){
			if(key.constructor == String){
				if(value!=undefined){
					_provider[key] = value;
				}
				return _provider[key];
			} else {
				_provider = carnival.utils.merge(_provider,key);
			}
		}
		return _provider;
	};

	/**
	 * isLoggedIn - Function for determining if current user is logged in.
	 * @returns {Boolean} logged in or not
	 */
	var isLoggedIn = function(){
		// is there a way to contact the backend server to double-check?
		try{
		return carnival.utils.cookies.get(config('c_mid')) !== null;
		}catch(e){return false;}
	};

	/**
	 * getCookieProfile - Function for determining if current user is logged in.
	 * @returns {Object} userprofile with data set coming from cookies
	 */
	var getCookieProfile = function(){
		profile('userName', carnival.utils.cookies.get(config('c_unm')) );
		profile('masterId', carnival.utils.cookies.get(config('c_mid')) );
		profile('lastProviderLogin', carnival.utils.cookies.get(config('c_llp')) );
		return profile();
	};

	var afterLogin = function(callback,scope){
		carnival.utils.listener.kill('_carnival_after_login');
		carnival.utils.listener.listen('_carnival_after_login',callback,scope);
	};

	var login = function(cancelCallback){
		cancelCallback = (cancelCallback === true);
		setCarnivalCookie();
		if(!cancelCallback){carnival.utils.listener.fire('_carnival_after_login');}
	};

	var getEmailAddresses = function(){
		var p = profile(),i;
		var emails = [];
		var _verified = 0;
		if('false' != carnival.user.profile('isEmailVerified')  && 'false' != carnival.user.profile('isEmailExists')) {
			if(p.linkedIdentifiers && p.linkedIdentifiers.push){
				for(i = p.linkedIdentifiers.length;i--;){
					if(p.linkedIdentifiers[i].emailAddress && p.linkedIdentifiers[i].emailAddress.length){
						if(p.linkedIdentifiers[i].emailStatus == 'verified'){
							if((!profile('email') || !profile('email').length)){
								profile('email',p.linkedIdentifiers[i].emailAddress);
							}
							emails.push(p.linkedIdentifiers[i].emailAddress);
							profile('isEmailVerified','true');	
						}else if((!profile('email') || profile('email').length) && p.linkedIdentifiers[i].emailAddress.length){
							profile('email',p.linkedIdentifiers[i].emailAddress);
							emails.push(p.linkedIdentifiers[i].emailAddress);
						}
					}
				}
			} 
			if(!emails.length && profile('email') && profile('email').length) {
				emails.push(profile('email'));
			}
		} else {
			profile('email',(p.email && p.email.length)?p.email:'');
			emails.push(profile('email'));
			profile('isEmailVerified','false');
			if(p.linkedIdentifiers && p.linkedIdentifiers.push){
				for(i = p.linkedIdentifiers.length;i--;){
					if(p.linkedIdentifiers[i].emailAddress == profile('email') && p.linkedIdentifiers[i].emailStatus == 'verified'){
						profile('isEmailVerified','true');
						break;
					}
				}
			}
		}
		return carnival.utils.uniqueArray(emails);
	};

	/**
	 * logout - Function to log user out.  Only deletes masterid cookieTime
	 */
	var logout = function(logoutTarget){
		carnival.utils.cookies.del(config('c_mid'),{domain:config('cookieDomain'),path:'/'});
		carnival.utils.cookies.del(config('c_unm'),{domain:config('cookieDomain'),path:'/'});
		carnival.user.profile('masterId','');
		window.location = logoutTarget || carnival.utils.cleanLocation();
		return false;
	};

	/**
	 * carnivalLogout - redirects user to logout on carnival
	 * @param {String} logoutUrl Location to redirect user to logout.  If empty will go to carnival
	 *   default logout location.
	 */
	var carnivalLogout = function(logoutUrl){
		document.location = (logoutUrl||getLogoutUrl());
		return false;
	};

	var getLoginUrl = function() {
		return config('hostname') + config('userPath') + "/signon.html?callbackUrl=" + carnival.utils.cleanLocation() + '&lastProvider=' + carnival.utils.cookies.get(config('c_llp')) + '&lastUser=' + carnival.utils.cookies.get(config('c_unm'));
	};

	var getLoginUrlPopup = function() {
		return config('hostname') + config('userPath') + '?callbackUrl=' + carnival.utils.cleanLocation() + '&lastProvider=' + carnival.utils.cookies.get(config('c_llp')) + '&lastUser=' + carnival.utils.cookies.get(config('c_unm'));
	};

	var getLogoutUrl = function(callbackPage) {
		return config('hostname')+config('userPath')+'/logout.html?callbackUrl='+encodeURIComponent(callbackPage || carnival.utils.cleanLocation());
	}; 

	var getUserName = function(){
		return (carnival.user.profile('userName')||carnival.user.profile('displayName')||'Visitor');
	};
	
	/**
	 * getBasicUserInfo - retrieves initial user info using provided session token
	 * 
	 */
	var getBasicUserInfo = function(token,callback,scope) {
		$.getJSON(config('WSHostname')+config('userPath')+"/trbsecurity/userprofile?callback=?",
			{ signon_token : token},
			function(data) {
				carnival.user.profile(data);
				if(callback && callback.apply){callback.apply(scope||window,arguments);}
			}
		);
	};

	var setCarnivalCookie = function() {
		if(!config('cookieTime')){
			var date = new Date();
			date.setTime(date.getTime()+(14*24*60*60*1000));
			config('cookieTime',date);
		}
		carnival.utils.cookies.set(config('c_mid'), profile('masterId'), {domain:config('cookieDomain')});
		carnival.utils.cookies.set(config('c_unm'), profile('userName'), {expiresAt: config('cookieTime'),domain:config('cookieDomain')});
		carnival.utils.cookies.set(config('c_llp'), profile('lastProviderLogin'), {domain:config('cookieDomain')});
	
		try{carnival.utils.listener.fire('_carnival_after_cookie');}catch(e){}
		return false;
	};

	var profileIncomplete = function(incompleteCallback,completeCallback){
		var missingData = [];
		if(carnival.user.profile('isEmailVerified') != 'true' ) {
			
			var _verified = false;
			if(carnival.user.profile('linkedIdentifiers').push){
				var lp = carnival.user.profile('linkedIdentifiers');
				for(var i = lp.length;i--;){
					if(lp[i].emailStatus == 'verified') {
						_verified = true;
					}
				}
			}
			if(!_verified) {
				return incompleteCallback(missingData);
			}
		}
		getConfigRules(function(data){
			if(data.requiredAttrs){
				for(var i= data.requiredAttrs.length;i--;) {
					if(!carnival.user.profile(data.requiredAttrs[i]) ){
						missingData.push(data.requiredAttrs[i]);
					}
				}
			}
			if(missingData.length){return incompleteCallback(missingData);}
			if(completeCallback){return completeCallback.call(window,[carnival.user.profile()]);}
		});
		return undefined;
	};

	var cancelLogin = function(logoutUrl) {
		carnivalLogout(logoutUrl);
	};

	
	/**
	 * submitUserResetRequest - request an email for a password reset
	 * @param {Object} email Email address requesting reset
	 * @param {Function} successMethod  The function to be called if success
	 * @param {Function} errorMethod  The function to be called if error
	 */
	var submitUserResetRequest = function(email,successMethod,errorMethod){
		var conf = config(),
			requestString = [
				conf.WSHostname,conf.userPath,
				'/trbsecurity/isoemailresetinstructions'
			].join(''),
			pCode = carnival.configuration('product') || 'default';
		
		carnival.core.postData({
			url:requestString,
			data:carnival.utils.serializeJSON({email:email, productCode:pCode}),
			success:function(data){
				if(typeof data == 'string') {
					data = carnival.utils.parseJSON(data);
				}
				if(data.status=="0"){
					successMethod(data);
				}
				else{errorMethod(data);}
			},
			error: function(transport){
				var data = carnival.utils.parseJSON(transport.responseText);
				if(data===undefined||data===''){data = transport;}
				errorMethod(data||transport);
			}
		});
	};
	
	/**
	 * submitUserReset -
	 * @param {Object}
	 * @param {Function} successMethod  The function to be called if success
	 * @param {Function} errorMethod  The function to be called if error
	 */
	var submitUserReset = function(inputs,successMethod,errorMethod){
		var conf = config(),
			requestString = [
				conf.WSHostname,conf.userPath,
				'/trbsecurity/isoresetpassword'
			].join(''),
			pCode = carnival.configuration('product') || 'default';
		
		carnival.core.postData({
			url:requestString,
			data:carnival.utils.serializeJSON(inputs),
			success:function(data){
				if(typeof data == 'string') {
					data = carnival.utils.parseJSON(data);
				}
				if(data.status=="0"){
					successMethod(data);
				}
				else{errorMethod(data);}
			},
			error: function(transport){
				var data = carnival.utils.parseJSON(transport.responseText);
				if(data===undefined||data===''){data = transport;}
				errorMethod(data||transport);
			}
		});
	};
	
	/**
	 * submitUserChange 
	 * @param {Object} 
	 * @param {Function} successMethod  The function to be called if success
	 * @param {Function} errorMethod  The function to be called if error
	 */
	var submitUserChange = function(inputs,successMethod,errorMethod){
		var conf = config(),
			requestString = [
				conf.WSHostname,conf.userPath,
				'/trbsecurity/isochangepassword'
			].join(''),
			pCode = carnival.configuration('product') || 'default';
		
		carnival.core.postData({
			url:requestString,
			data:carnival.utils.serializeJSON(inputs),
			success:function(data){
				if(typeof data == 'string') {
					data = carnival.utils.parseJSON(data);
				}
				if(data.status=="0"){
					successMethod(data);
				}
				else{errorMethod(data);}
			},
			error: function(transport){
				var data = carnival.utils.parseJSON(transport.responseText);
				if(data===undefined||data===''){data = transport;}
				errorMethod(data||transport);
			}
		});
	};
	
	/**
	 * submitNewUser - sends new user username and password request to carnival
	 * @param {Object} inputs data to be saved
	 * @param {Function} successMethod  The function to be called if success
	 * @param {Function} errorMethod  The function to be called if error
	 */
	var submitNewUser = function(inputs,successMethod,errorMethod){
		var conf = config();
		var requestString = [conf.WSHostname,conf.userPath,
			'/trbsecurity/isocreatenewaccount?',
			'&api_key=',encodeURIComponent(inputs.apiKey),
			'&product_code=',encodeURIComponent(inputs.productCode)
			].join('');
		carnival.core.postData({
			url:requestString,
			data:transformNewUser(inputs),
			success:function(data){
				if(typeof data == 'string') {
					data = carnival.utils.parseJSON(data);
				}
				if(data.status=="0"){successMethod(data);}
				else{errorMethod(data);}
			},
			error: function(transport){
				var data = carnival.utils.parseJSON(transport.responseText);
				if(data===undefined||data===''){data = transport;}
				errorMethod(data||transport);
			}
		});
	};

	/**
	 * submitConsumerProfile - save user data
	 * @param {Object} inputs data to be saved
	 * @param {Function} successMethod  The function to be called if success
	 * @param {Function} errorMethod  The function to be called if error
	 */
	var submitConsumerProfile = function(inputs,successMethod,errorMethod){
		var conf = config();
		var external = carnival.core.isExternal(conf.WSHostname);
		// form is in JSON, but needs to be transformed to match what consumerprofile expects
		var transformedinputs = (external)?
			transformConsumerProfileXD(inputs) //using XD service
			: transformConsumerProfile(inputs);
		var requestString = [conf.WSHostname,conf.userPath,
			'/trbsecurity/consumerprofile',(external?'/store/json/jsonp':''),
			'?master_id=',encodeURIComponent(inputs.masterId),
			'&api_key=',encodeURIComponent(inputs.apiKey),
			'&product_code=',encodeURIComponent(inputs.productCode),
			(external?'&profile='+encodeURIComponent(transformedinputs)+'&callback=?':'')
			].join('');
		carnival.core[(external?"loadData":"postData")]({
			url:requestString,
			data:transformedinputs,
			success:function(data){
				if(typeof data == 'string') {
					data = carnival.utils.parseJSON(data);
				}
				//fixes unm cookie setting issue
				if(!!inputs.userName){profile('userName',inputs.userName);}
				if(data.status=="0"){
					if(!!inputs.email){profile('email',inputs.email);}
					successMethod(data);
				}
				else{errorMethod(data);}
			},
			error: function(transport){
				var data = carnival.utils.parseJSON(transport.responseText);
				if(data===undefined||data===''){data = transport;}
				errorMethod(data||transport);
			}
		});
	};

	/**
	 * transformUserReset - Formats data to be sent to webservice
	 * @param {Object} input
	 */
	var transformUserReset = function(input){
		var out = {};
		for(var key in input) {
			if(input.hasOwnProperty(key)){
				if(!key.match(/recaptcha|adcopy|masterId|apiKey|productCode/i) && input[key].length) {
					out[key] = carnival.core.cleanInputs(input[key]);
				}
			}
		}
		return carnival.utils.serializeJSON(out);
	};
	
	/**
	 * transformNewUser - Formats data to be sent to webservice
	 * @param {Object} input
	 */
	var transformNewUser = function(input){
		var out = {};
		for(var key in input) {
			if(input.hasOwnProperty(key)){
				if(!key.match(/recaptcha|adcopy|masterId|apiKey|productCode/i) && input[key].length) {
					out[key] = carnival.core.cleanInputs(input[key]);
				}
			}
		}
		return carnival.utils.serializeJSON(out);
	};
	
	var transformConsumerProfileXD = function(input){
		var out = {};
		for(var key in input) {
			if(input.hasOwnProperty(key)){
				if(!key.match(/recaptcha|adcopy|masterId|apiKey|productCode/i)) {
					out[key] = carnival.core.cleanInputs(input[key]);
				}
			}
		}
		return carnival.utils.serializeJSON(out);
	};

	/**
	 * transformConsumerProfile - Formats data to be sent to webservice
	 * @param {Object} input
	 */
	var transformConsumerProfile = function(input){
		var out = {extendedProfileAttributes:{entry:[]}};
		for(var key in input) {
			if(input.hasOwnProperty(key)){
				if(!key.match(/recaptcha|adcopy|masterId|apiKey|productCode/i)) {
					out.extendedProfileAttributes.entry.push({key:key,value:carnival.core.cleanInputs(input[key])});
				}
			}
		}
		return carnival.utils.serializeJSON(out);
	};

	/**
	 * translateConsumerProfile - Formats data returned by webservice
	 * @param {Object} data
	 */
	var translateConsumerProfile = function(data){
		var i;
		if(data.extendedProfileAttributes && data.extendedProfileAttributes.length){
			for(i = data.extendedProfileAttributes.length;i--;){
				data[data.extendedProfileAttributes[i].name] = data.extendedProfileAttributes[i].value;
			}
		}
		if(data.linkedIdentifiers && data.linkedIdentifiers.length){
			for(i = data.linkedIdentifiers.length;i--;){
				carnival.user.provider(data.linkedIdentifiers[i].providerName,data.linkedIdentifiers[i].openId);
			}
		}
		return data;
	};

	/**
	 * getConsumerProfile - Function to get consumer data
	 * @param {Function|Object} callback method to call on success or object w/ arguments
	 * @param {Object} scope the scope that the callbacks will fire in
	 * @param {Function} error method to call on failure
	 * @param {Object} args any extra data passed to the method
	 */
	var getConsumerProfile = function(callback,scope,error,args){
		var conf = carnival.core.setConfigs(callback,scope,error,args);
		conf = carnival.utils.merge(conf,config());
		if(!isLoggedIn() && error){error.apply(scope);}
		var requestString = [conf.WSHostname,conf.userPath,
			'/trbsecurity/consumerprofile/jsonp?',
			'api_key=',conf.apiKey,
			'&product_code=',conf.product,
			'&master_id=',profile('masterId'),
			'&callback=?'].join('');
		carnival.core.loadData({
			key:'consumerProfile',
			url:requestString,
			success:function(data){
				if( data.errorNumber != undefined && data.errorNumber == 0) {
					data = translateConsumerProfile(data);
					carnival.user.profile(data);
					carnival.core.injectData([data],conf.callback,conf.scope);
				}else {
					if(conf.error){
						conf.error(data);
					}
				}
			},
			error:conf.error
		});
	};

	var verifyLogin = function(success,error){
		carnival.user.getConsumerProfile(
			{callback:function(data){
				data = translateConsumerProfile(data);
				success(data);
			},
			error:error}
		);
	};

	var protect = function(redirectUrl){
		redirectUrl = redirectUrl || '/';
		verifyLogin(function(){},
			function(){
				document.location.replace(redirectUrl.replace(/(\?|&)(logout|token)=[^&]*/g,''));
			}
		);
	};
	
	/**
	 * getConfigRules - Function to get form from configuration service
	 * @param {Function|Object} callback method to call on success or object w/ arguments
	 * @param {Object} scope the scope that the callbacks will fire in
	 * @param {Function} error method to call on failure
	 * @param {Object} args any extra data passed to the method
	 */
	var getConfigRules = function(callback,scope,error,args){
		var conf = carnival.core.setConfigs(callback,scope,error,args);
		conf = carnival.utils.merge(conf,config());
		var requestString = [conf.WSHostname,conf.userPath,
			'/trbsecurity/profilecontentrules/jsonp?',
			'api_key=',conf.apiKey,
			'&product_code=',conf.product,
			'&profile_type=/',
			'&callback=?'].join('');
		carnival.core.loadData({
			key:'profileContentRules',
			url:requestString,
			success:function(){carnival.core.injectData(arguments,conf.callback,conf.scope);},
			error:error
		});
	};

	var is3rdPartyLogin = function(){
		return profile('lastProviderLogin') != 'isoprovider';
	};
	
	// return public methods
	return {
		data:data,
		getConsumerProfile:getConsumerProfile,
		getConfigRules:getConfigRules,
		submitConsumerProfile:submitConsumerProfile,
		isLoggedIn:isLoggedIn,
		login:login,
		logout:logout,
		carnivalLogout:carnivalLogout,
		getLoginUrl:getLoginUrl,
		getLoginUrlPopup:getLoginUrlPopup,
		getLogoutUrl:getLogoutUrl,
		cancelLogin:cancelLogin,
		getBasicUserInfo:getBasicUserInfo,
		setCarnivalCookie:setCarnivalCookie,
		getCookieProfile:getCookieProfile,
		provider:provider,
		profile:profile,profileIncomplete:profileIncomplete,
		verifyLogin:verifyLogin,
		protect:protect,getUserName:getUserName,
		afterLogin:afterLogin,
		getEmailAddresses:getEmailAddresses,
		submitUserResetRequest:submitUserResetRequest,
		submitUserReset:submitUserReset,
		submitNewUser:submitNewUser,
		submitUserChange:submitUserChange,
		is3rdPartyLogin:is3rdPartyLogin
	};
}(jQuery,window,document);


carnival.subscription = function($,window,document,undefined){

	var config = carnival.configuration;
	var profile = carnival.user.profile;
	/**
	 * getNewsletters - Function to get newsletter data
	 * @param {Function|Object} callback method to call on success or object w/ arguments
	 * @param {Object} scope the scope that the callbacks will fire in
	 * @param {Function} error method to call on failure
	 * @param {Object} args any extra data passed to the method
	 */
	var getNewsletters = function(callback,scope,error,data){
		var conf = carnival.core.setConfigs(callback,scope,error,data);
		conf = carnival.utils.merge(conf,config());
		var requestString = [conf.WSHostname,conf.subscribePath,
			'/subscribe/offers/jsonp?',
			'master_id=',encodeURIComponent(profile('masterId')),
			'&product_code=',conf.product,
			'&callback=?'].join('');
		carnival.core.loadData({
			url:requestString,
			success:function(data){carnival.core.injectData([translateNewsletters(data)],conf.callback,conf.scope);},
			error:error
		});
	};

	var getNewsletterSubscriptions = function(callback,scope,error,data){
		var conf = carnival.core.setConfigs(callback,scope,error,data);
		conf = carnival.utils.merge(conf,config());
		var requestString = [conf.WSHostname,conf.subscribePath,
			'/subscribe/subscriptions/jsonp?',
			'master_id=',encodeURIComponent(profile('masterId')),
			'&product_code=',conf.product,
			'&callback=?'].join('');
		carnival.core.loadData({
			url:requestString,
			key:'newsletterSubs',
			success:function(data){carnival.core.injectData([translateNewsletterSubscriptions(data)],conf.callback,conf.scope);},
			error:error
		});
	};

	var submitNewsletters = function(inputs,successMethod,errorMethod){
		var path="";
		var conf = config();
		var requestString = [conf.WSHostname,conf.subscribePath,
			'/subscribe/subscriptions?',
			'master_id=',encodeURIComponent(profile('masterId')),
			'&product_code=',encodeURIComponent(conf.product)
			].join('');
		// form is in JSON, but needs to be transformed to match what subscriptionservice expects
		inputs = transformNewsletters(inputs);
		carnival.core.postData({
			url:requestString,
			type:'PUT',
			data:inputs,
			success:successMethod,
			error: errorMethod
		});
	};

	var transformNewsletters = function(data){
		return carnival.utils.serializeJSON({"email":profile('email'),"format":"HTML","masterId":profile('masterId'),"subList":data.subs,"unSubList":data.unsubs});
	};
	
	var transformNewslettersXD = function(data){
		return carnival.utils.serializeJSON({"email":profile('email'),"format":"HTML","masterId":profile('masterId'),"subList":data.subs,"unSubList":data.unsubs});
	};

	var translateNewsletterSubscriptions = function(data){
		var returnData = {};
		if(!!data.newsletters){
			if(!data.newsletters.push){data.newsletters = [data.newsletters];}
			for(var i = data.newsletters.length;i--;){
				if(data.newsletters[i].status == '0'){
					returnData[data.newsletters[i].newsletter.code] = data.newsletters[i].newsletter.description;
				}
			}
		}
		return returnData;
	};
	
	var translateNewsletters = function(data){
		var returnData = {}, i, cats = {};
		if(!!data.newsletterCategories){
			for(i = data.newsletterCategories.length;i--;){
				cats[data.newsletterCategories[i].code] = data.newsletterCategories[i].name;
			}
		}
		
		if(!!data.newsletters){
			if(!data.newsletters.push){data.newsletters = [data.newsletters];}
			for(i = data.newsletters.length, a = data.newsletters.reverse();i--;){
				if(!!data.newsletters[i].description && data.newsletters[i].description!=='null'){
					if(returnData[cats[data.newsletters[i].categoryCode]] == null){
						returnData[cats[data.newsletters[i].categoryCode]] = [];
					}
					returnData[cats[data.newsletters[i].categoryCode]].push(data.newsletters[i]);
				}
			}
			return returnData;
		} else {
			return 0;
		}
	};
	
	return{
		getNewsletters:getNewsletters,
		getNewsletterSubscriptions:getNewsletterSubscriptions,
		submitNewsletters:submitNewsletters
	};

}(jQuery,window,document);


/**
 * carnival.captcha - Functions related to captcha
 * @namespace
 */
carnival.captcha = function($,document,window,undefined){

	var config = carnival.configuration, capElement, defaultCaptcha = 'securityFilter';

	var _capConf = {
		reCaptcha : {
			getPath : '/trbsecurity/captcha/jsonp',
			postPath : '/trbsecurity/validcaptcha/jsonp',
			responseField : '#recaptcha_response_field',
			challengeField : '#recaptcha_challenge_field',
			writeRegex : /captcha/i
		},
		adCopy : {
			getPath : '/trbsecurity/adcopy/jsonp',
			postPath : '/trbsecurity/validadcopy/jsonp',
			responseField : '#adcopy_response',
			challengeField : '#adcopy_challenge',
			writeRegex : /adcopy|ad_copy/i
		},
		securityFilter : {
			postPath : '/trbsecurity/tokenretriever/jsonp'
		}
	}	;
	
	var capType;
	var capConf = function(){
			if(!capType){
			// if no type, use default
			if(!config('captchaType')){config('captchaType',defaultCaptcha);}
			var capType = _capConf[config('captchaType')];
			// if type is not in configs use default
			if(!capType){capType=_capConf[defaultCaptcha];}
		}
		return capType;
	};
	/**
	 * getCaptcha - Function to get captcha from service
	 * @param {Function|Object} callback method to call on success or object w/ arguments
	 * @param {Object} scope the scope that the callbacks will fire in
	 * @param {Function} error method to call on failure
	 * @param {Object} args any extra data passed to the method
	 *
	 * Example:
	 * carnival.captcha.getCaptcha(function(data){
	 *   $('#cap').append(data.htmlText);
	 * });
	 */
	var getCaptcha = function(callback,scope,error,args){	
		if(capConf().getPath){
			var conf = carnival.core.setConfigs(callback,scope,error,args);
			conf = carnival.utils.merge(conf,config());
			var requestString = [conf.WSHostname,conf.userPath,
				capConf().getPath,
				'?product_code=',config('product'),'&callback=?'].join('');
			carnival.core.loadData({
				url:requestString,
				success:function(){carnival.core.injectData(arguments,conf.callback,conf.scope);},
				error:error
			});
		}
	};

	var verify = function(){
		carnival.utils.listener.fire('submit_captcha');
		return false;
	};

	/**
	 * postCaptcha - sends Captcha to verification service for validation token
	 * @param {Function|Object} callback method to call on success or object w/ arguments
	 * @param {Object} scope the scope that the callbacks will fire in
	 * @param {Function} error method to call on failure
	 * @param {Object} args any extra data passed to the method
	 *
	 * Example:
	 * carnival.captcha.postCaptcha({
	 *   callback:function(){
	 *     carnival.user.submitConsumerProfile({args:data});
	 *   },
	 *   error:function(){console.log('!!!')}
	 * });
	 */
	var postCaptcha = function(callback,scope,error,args){
		var conf = carnival.core.setConfigs(callback,scope,error,args);
		conf = carnival.utils.merge(conf,config());
		var ans = $(capConf().responseField).val() || "";
		var chl = $(capConf().challengeField).val() || "";
		var requestString = [conf.WSHostname,conf.userPath,
			capConf().postPath,
			'?product_code=',config('product'),
			'&answer=',ans,
			'&challenge=',chl,
			'&ip_address=',
			'&callback=?'].join('');
		conf.success = conf.callback;
		var sMethod = function(data){
			if(data.isValidAnswer ) {
				conf.success.apply(conf.scope, arguments);
			} else {
				reload();
				if(conf.error){conf.error.apply(conf.scope, arguments);}
			}
		};
		conf.callback = sMethod;
		carnival.core.loadData({
			url:requestString,
			success:function(){carnival.core.injectData(arguments,conf.callback,conf.scope);},
			error:function(){Recaptcha.reload ();error.apply(conf.scope, arguments);}
		});
	
	};

	var reload = function(){
		try{
			Recaptcha.reload();
			return;
		}catch(e){}
	
		try{
			ACPuzzle.reload();
			return;
		}catch(e){}
	};

	/**
	 * initCaptcha - Defines where captcha will be located.  Adjusts document.write
	 * Function to handle captcha code appropriately
	 * @param {String|DomNode} elem Location that captcha should be inserted
	 * @param {RegExp} regex Regular expression of text catch.  Defaults to /captcha/
	 */
	var initCaptcha = function(elem,regex){
		var el = $(elem);
		capElement = el;
		var oldWrite = document.write;
		regex = regex || capConf().writeRegex;
		// we need to get creative with document.write
		// this is a repurposing of John Resig's document.write
		// http://ejohn.org/blog/xhtml-documentwrite-and-adsense/
		document.write = function(text){
			if(text.match(regex)){
				el.append(text);
			} else {
				try{
					var pos,div = document.createElementNS("http://www.w3.org/1999/xhtml","div");
					div.innerHTML = text;
					if(document.lastChild){
						pos = document;
						while (pos.lastChild&&pos.lastChild.nodeType===1){
							pos = pos.lastChild;
						}
					} else {
						pos = document.getElementsByTagName("*");
						pos = pos[pos.length-1];
					}
					var nodes = div.childNodes;
					while(nodes.length){pos.parentNode.appendChild(nodes[0]);}
				}catch(e){
					try {
						oldWrite.call(document,text);
					}catch(ee){/*hopeless?help me*/}
				}
			}
		};
		//attachCaptcha();
	};

	/**
	 * getCaptchaElement - Getter for captcha container element
	 */
	var getCaptchaElement = function(){
		return capElement;
	};

	/**
	 * attachCaptcha - attaches captcha verification to form
	 * @param {Function} callback Function to call after success
	 * @param {Object} scope scope for callback to run in
	 */
	var attachCaptcha = function(callback,scope){
		var formEl = getCaptchaElement().parents('form');
		carnival.utils.listener.listen('submit_captcha',function(){
			postCaptcha({
				callback:function(data){
					if(callback){callback.apply((scope||window),arguments);}
					else{formEl[0].submit();}
				},
				error: function(){
					if($('#cap_message').length === 0) {
						getCaptchaElement().append('<span class="captcha-error"><label id="cap_message" ></label></span>');
					}
					$('#cap_message').html('The text you entered is incorrect.  Please try again.');
				}
			});
			return false;
		});
	};

	return {getCaptcha:getCaptcha,attachCaptcha:attachCaptcha,initCaptcha:initCaptcha,verify:verify,reload:reload};

}(jQuery,document,window);


/**
 * carnival.core - Core Functions
 * @namespace
 */
carnival.core = function($,document,window,undefined){

	/**
	 * loadQueue - variable keeping track of what data is being loaded
	 */
	var loadQueue = [],
	
	data = {};

	/**
	 * loadData - Utility function for making ajax calls and inserting
	 *   data into carnival user object
	 * @param {Object} args A JSON objecto containing configurations
	 *   available options:
	 *     url {String} Url to get data from
	 *     key {String} A location to store the returned data in the
	 *       carnival user object.  If a key is passed that already exists
	 *       in the cache, ajax get is skipped.
	 *     success {Function} Function to call on success
	 *     error {Function} Function to call on error
	 */
	var loadData = function(args){
		if(args.key){
			if( data[args.key] != undefined) {
				if(args.success){args.success.apply(this,[data[args.key]]);}
				return;
			} else if(loadQueue[args.key]) {
				// this is in the loadQueue and is currently being fetched.
				// Wait for it to finish then fire the callback.
				if(args.success){
					carnival.utils.listener.listen('_loadData_'+args.key,function(){
						args.success.apply(this,[data[args.key]]);
					});
				}
				return;
			}
			loadQueue[args.key] = true;
		}
		$.ajax({
			url: args.url,
		  dataType: 'json',
			success:function(f){
				if(args.success){args.success.apply(this,arguments);}
				if(args.key){
					data[args.key] = f;
					loadQueue[args.key] = false;
				}
				carnival.utils.listener.fire('_loadData_'+args.key,{close:true});
			},
			error:args.error
		});
	};

	var isExternal = function(){
		var protocol = location.protocol,
				hostname = location.hostname,
				exRegex = RegExp('^'+protocol + '//' + hostname);
		
		
		var _isExternal = function(url) {
				// if xd library is being used, treat everything as internal
				if(carnival.configuration('xd')){return false;}
			return !exRegex.test(url) && /^https?:\/\//.test(url);
		};
	
		return _isExternal;
	}();

	var postData = function(args){
		$.ajax({
			url:args.url,
			data:args.data,
			contentType:"application/json",
			type:(args.type || 'POST'),
			success:function(f){
				if(args.key){data[args.key] = f;}
				if(args.success){args.success.apply(this,arguments);}
			},
			error:function(data){args.error(data);}
		});
	};

	/**
	 * setConfigs - Utility for setting up user methods
	 * @param {Function|Object} callback method to call on success or object w/ arguments
	 * @param {Object} scope the scope that the callbacks will fire in
	 * @param {Function} error method to call on failure
	 * @param {Object} args any extra data passed to the method
	 */
	var setConfigs = function(callback,scope,error,args){
		var conf = {};
		if(callback.constructor != Function){
			conf = carnival.utils.merge(conf,callback);
		} else {
			if(callback){conf.callback = callback;}
			if(scope){conf.scope = scope;}
			if(error){conf.error = error;}
			if(args){conf.args = args;}
		}
		return conf;
	};

	/**
	 * injectData - fire a callback with data injected in
	 * @param data {Object} Data returned from JSONP call
	 * @param	callback {Function} Method to fire
	 * @param scope {Object} Scope of callback.  Defaults to window
	 */
	var injectData = function(data,callback,scope){
		if(!scope){scope = window;}
		callback.apply(scope,data);
	};
	
	var cleanInputs = function(input){
		return input.replace(/<[^>]*>/g,'').replace(/^\s*|\s*$/g,'');
	}

	return {loadData:loadData,postData:postData,setConfigs:setConfigs,injectData:injectData,cleanInputs:cleanInputs,isExternal:isExternal,data:data};
}(jQuery,document,window);


/**
 * username	 - Functions related to username verification
 */
carnival.username = function($,window,document,undefined){
	var isGood = true;
	var config ={};
	config.availableHTML = '<label id="userNameAvailable">${userName} is available.</label>';
	config.unavailableHTML = '<label id="userNameUnavailable" class="label">${userName} is unavailable. The following recommended names are available.</label>'+
		'<label> </label><span class="form_radio_container">'+
				'<input type="radio" value="${reccomend}" class="form_radio" name="recommendedUserName" id="recommendedUserName" />'+
				'<label for="recommendedUserName" class="form_choice">${reccomend}</label>'+
			'</span>';
	config.userNameRegex = new RegExp('\\$\\{userName\\}','g');
	config.recommendRegex = new RegExp('\\$\\{reccomend\\}','g');

	/**
	 * configuration - Dynamic getter/setter for configurations.  If no arguments
	 *   are passed, config object is returned.
	 * @param {String|Object} key Can be a string containing the name of the configuration
	 *   or an object containing configurations.
	 * @param {Object} value Value of cofig element set
	 */
	var configuration = function(key,value){
		if(key){
			if(key.constructor == String) {
				if(value !== undefined){
					config[key] = value;
					return value;
				}
			} else {
				config = carnival.utils.merge(config,key);
			}
			return config[key];
		}
		return config;
	};

	/**
	 * verify - Function to check uniqueness of username
	 * @param {String} username The username to be checked
	 * @param {Function} successMethod  The function to be called if unique
	 * @param {Function} errorMethod  The function to be called if not unique
	 */
	var verify = function(username,successMethod,errorMethod) {
		var requestString = [carnival.configuration('WSHostname'),carnival.configuration('userPath'),
			'/trbsecurity/suggestedusernames/jsonp?',
			'&product_code=',carnival.configuration().product,
			'&master_id=',carnival.user.profile('masterId'),
			'&user_name=',username,
			'&callback=?'].join('');
		carnival.core.loadData({url:requestString,
			success:function(data){
				if(!(data.suggestedUserName)){
					successMethod.apply(this,arguments);
				} else {
					errorMethod.apply(this,arguments);
				}
			},
			error: errorMethod
		});
	};

	var checker = function(field){
		var messages, field_input;
		field = $(field);
		field.after('<li style="display:none"></li>');
		messages = field.next('li');
		field_input = field.find('input');
		field_input.bind('change',function(){checkit.call(this,messages,field_input);});
	};

	var checkit = function(messages,field_input){
		this.value = this.value.replace(/<[^>]*>/g,'');
		var that = this;
		if($(this).valid() && (this.value != carnival.user.profile('userName') || carnival.user.profile('status') != 'verified')){
			verify(this.value,function(data){
				if(data.suggestedUserNames) {//no good
					isGood = false;
					$(messages).show()
						.html(
							configuration('unavailableHTML')
								.replace(configuration('userNameRegex'),that.value)
								.replace(configuration('recommendRegex'),data.suggestedUserNames.userName)
						);
					$('#recommendedUserName').click(function(){
						if(this.checked){field_input.val(this.value);}
					});
				} else {//good
					isGood = true;
					if(configuration('availableHTML').length) {
						$(messages).show().html(configuration('availableHTML').replace(configuration('userNameRegex'),that.value));
					} else {$(messages).hide().html();}
				}
			});
		}  else {
			$(messages).hide().html();
		}
	};

	var status = function(){
		return isGood;
	};

	return {verify:verify,checker:checker,configuration:configuration,status:status};
}(jQuery,window,document);

carnival.metrics = function($){
	var _metricsOrder = [0,5,3,2,4,4,6,2,7,1];
	var init = function(){
		if(!carnival.utils.listener.listening('_carnival_after_cookie')){
			carnival.utils.listener.listen('_carnival_after_cookie',function(){
				setMetricsId(carnival.user.profile('consumerId'));
			});
			carnival.utils.listener.listen('_carnival_after_logout_success',function(){
				removeMetricsId();
			});
		}
	};
	
	var pad  = function(n) {
		n = n.toString();
		while(n.length < 4) {n=Math.floor(Math.random()*10)+n;}
		return n;
	};
	
	var rand = function(){
		return pad(Math.floor(Math.random()*1000));
	};
	
	var removeMetricsId = function(){
		$.cookies.del('metrics_id', {domain:carnival.configuration('cookieDomain')});
	};
	
	var setMetricsId = function(consumerId){
		var _metricsId;
		var metricArr = consumerId.split('-');
		while(metricArr.length < 8){
			metricArr.push(rand());
		}
		var retVal = '';
		for(var i=_metricsOrder.length;i--;){
			retVal += '-'+metricArr[_metricsOrder[i]];
		}
		_metricsId = retVal.substring(1).replace(/\s/g,'');
		$.cookies.set('metrics_id', _metricsId , {domain:carnival.configuration('cookieDomain')});
		return _metricsId;
	};
	
	var getMetricsId = function(){
		var m = $.cookies.get('metrics_id');
		if(null===m || ''== m){return false;}
		var metricArr = m.split('-').reverse();
		var retArr = [];
		for(var i=_metricsOrder.length;i--;){
			retArr[_metricsOrder[i]] = metricArr[i];
		}
		return retArr.slice(0,3).join('-');
	};
	
	var _target;
	var setMetricsFrame = function(target){
		_target = target;
	};
	
	var addMetricsFrame = function(){
		$('body').prepend('<iframe border=0 style="border:0px;height:0;width:0;position:absolute;top:-10000px" src="'+_target+'"></iframe>');
	};
	
	$(function(){carnival.metrics.init()});
	
	return {
		init:init,
		getMetricsId:getMetricsId,
		setMetricsFrame:setMetricsFrame,
		addMetricsFrame:addMetricsFrame
	};
}(jQuery);


var memberNav = (function($){
	
	/**
	 * memberNav - object for creating navigation elements
	 * @param {String|Object} elem CSS selector or element to insert nav into
	 * @param {String|Function} loggedintext String to insert or function to call if user is logged in
	 * @param {String|Function} loggedouttext String to insert or function to call if user is logged out
	 */
	var memberNav = function(elem,loggedintext,loggedouttext){
		var _configs = {};
		this.navLocation = $(elem);
		this.configs = function(key,value){
			if(key){
				if(key.constructor == String){
					if(typeof value != 'undefined'){
						_configs[key] = value;
					}
					return _configs[key] || '';
				} else {
					_configs = carnival.utils.merge(_configs,key);
				}
			}
			return _configs;
		};
		this.loggedintext = loggedintext || this.profileItems;
		this.loggedouttext = loggedouttext || this.loggedoutItems;
		return this;
	};

	memberNav.prototype = {
		writeNav : function(){
			if(carnival.user.isLoggedIn()) {
				this.writeProfileItems();
			} else {
				this.writeLoginItems();
				carnival.utils.listener.listen('_carnival_after_cookie',this.writeProfileItems,this);
			}
		},
		writeProfileItems : function(){
			if(this.loggedintext.constructor == String){
				try {
					this.navLocation.each(function(){
						this.innerHTML = '';
					});
				}catch(e){}
				this.navLocation.html(this.loggedintext);
			} else {
				this.loggedintext.call(this);
			}
		},
		writeLoginItems : function(){
			if(this.loggedouttext.constructor == String){
				try {
					this.navLocation.each(function(){
						this.innerHTML = '';
					});
				}catch(e){}
				this.navLocation.html(this.loggedouttext);
			} else {
				this.loggedouttext.call(this);
			}
		},
		profileItems : function(){
			this.navLocation.html('');
			this.navLocation.append('Hello <a href="/services/site/registration/show-profile.signon"  >'+carnival.user.getUserName()+'</a> | ');
			this.navLocation.append('<a href="'+ carnival.user.getLogoutUrl()+ '" onclick=" carnival.user.logout(carnival.user.getLogoutUrl());return false;">Log Out</a>');
		},
		loggedoutItems :  function(){
			this.navLocation.html('');
			this.navLocation.append('Hello '+carnival.user.getUserName()+'! | ');
			this.navLocation.append('<a href="'+carnival.configuration('hostname')+carnival.configuration('userPath')+'/popupsignon.html?callbackUrl='+carnival.utils.cleanLocation()+'" target="_carnival_popup" onclick="carnival.utils.popUp()" >Log In '+((!carnival.user.profile('displayName') && !carnival.user.profile('userName') )?'or Register':'')+'</a>');
		}
	};

	return memberNav;
})(jQuery);


(function(){
	// initializer will take data between open/close script tags and initialize
	// js client with it.
	var carnival_script = document.getElementsByTagName('script');
	carnival_script = carnival_script[carnival_script.length-1];
	// src.match required to protect against deferred or dom injected script loads
	if(carnival_script.innerHTML.length && carnival_script.src.match(/carnival\.js/)){
		var text = carnival_script.innerHTML;
		try{carnival_data = JSON.parse(text);}
		catch(e){
			try{carnival_data = new Function("return " + $.trim(text))();}
			catch(f){carnival_data = eval("("+text+")");}
		}
		carnival.init(carnival_data);
	}
})();
