


 








	








  


 

 

 
	

var pmHost='starstyler.gala.de'	
var pmApplicationDeploymentInfo = "{cdnPrefix:'http://new.taaz.com',resourcesPrefix:'assets/taaz2'}";
var pmApplicationInfo = "{applicationDir:'',application:'taaz2',applicationModifier:'',applicationVersion:'V20120222020842'}";
	
/*
 * Debug support
 * 
 * Usage:
 * 	Debug.log(msg, [devname])
 * 	Debug.note(msg, [devname, [color]])
 * 		Color: aqua, black, blue, fuchsia, gray, green, lime, maroon, navy, olive, purple, red, silver, teal, whte, yellow, or a hex value.
 * 	Debug.assert(str && str.length > 0, "str not be empty")
 * 	Debug.breakpoint()
 * 
 * Debug.notify(msg, user) -- display msg in popup over UI 
 * 
 * @author Brent (2011-04-09)
 * 2011-10-21 (Brent) - Turned off Debug output for non-dev2 deployments (DEBUG defined at the bottom of this file)
 */

Debug = {
	firebugActive: window.console && navigator.userAgent.indexOf('Firefox') != -1,

	allowOutputFor: function (username) {
		return window.console && Debug.isUser(username)
	},
	
	isUser: function (username) {
		return !username || document.location.href.match(RegExp('/' + username + '/'))
	},
	
 	isLive: function () { 
 		return !document.location.href.match(RegExp('/(dev|qa)2/'));
 	},
 	
 	isDev: function () {
 		var onDevServer = document.location.href.match(RegExp('/dev2/'));
 		
 		Debug.isDev = function () { return onDevServer; }
 		if (window.console) {
	 		window.console.debug = window.console.debug || window.console.log;	// for Chrome support
	 		window.console.trace = window.console.trace || window.console.log;	// for IE support
 		}
 		
 		return onDevServer;
	},
	
	/*
	 * Assertions
	 */
	
	assert: function (cond, msg, username) {
		if (DEBUG && Debug.allowOutputFor(username)) {
			if (!cond) {
				console.assert(cond)
				Debug.trace("Assertion Failed! " + msg)
				Debug.breakpoint()
			}
		}
	},
	
	breakpoint: function (username) {	// Break into FireBug, if open
		if (DEBUG && Debug.allowOutputFor(username))
			debugger;
	},
	
	/*
	 * Messages
	 */
	
	/**
	 * Display in over UI as pop-up notification for 1s
	 * 
	 * @param msg String message to display in a pop-up.
	 * @param username String the usual developer login id used as part of the developer site URL
	 * @param optDisplayMs int # ms to display the message (1.5s if not given)
	 * 
	 * @param return true if a message was output, false otherwise
	 */
	notify: function (title, msg, username, optDisplayMs) {
		if (DEBUG && Debug.allowOutputFor(username)) {
			if (TZ && TZ.notification)
				TZ.notification.show(title, msg, optDisplayMs || 2000, 200);	// displayMs, fade-in Ms
			Debug.log(msg, username);
			
			return true;
		}
		return false;
	},
	
	trace: function (msg, username) {
		if (DEBUG && Debug.allowOutputFor(username))
			console.trace(msg)
	},
 	
	group: function (groupName, username) {
		if (DEBUG && Debug.allowOutputFor(username))
			console.group(groupName)
	},
 	
	log: function (msg, username) {
		if (DEBUG && Debug.allowOutputFor(username))
			if (typeof msg === "object" && console.dir)
				console.dir(msg);
			else
				console.log(msg);
	},
 	
	// Specify your background color (default='yellow')
	note: function (msg, username, color) {
		color = color || 'yellow'
		if (DEBUG && Debug.allowOutputFor(username))
			if (typeof msg === "object" || !Debug.firebugActive)
				console.debug(msg);
			else
				console.debug("%c%s", "color:blue; background-color:"+color, msg)
	},
 	
	info: function (msg, username) {
		if (DEBUG && Debug.allowOutputFor(username))
			console.info(msg)
	},
 	
	warn: function (msg, username) {
		if (DEBUG && Debug.allowOutputFor(username))
			console.warn(msg)
	},
 	
	error: function (msg, username) {
		if (DEBUG && Debug.allowOutputFor(username)) {
			console.error(msg)
			Debug.trace(msg);
			Debug.notify(msg);
		}
	},
 	
	groupEnd: function (username) {
		if (DEBUG && Debug.allowOutputFor(username))
			console.groupEnd()
	},
 	
	/*
	 * Profiling
	 */
	
	_time: function (f) {
		if (DEBUG) {
			var start = new Date();
			f();
			var delta = new Date() - start;
			
			return delta;
		}
		return 0;
	},
	
	/**
	 * @return time in ms to execute the given function
	 */
	logTime: function (msg, timeThisFunction, username) {
		if (DEBUG && Debug.allowOutputFor(username))
		{
			console.info(msg + " ran in " + Debug._time(timeThisFunction + " ms"));
		}
	},
 	
	/** call at the beginning of a section to profile using the FireBug profiler */
	profileStart: function (msg, username) {
		if (DEBUG && Debug.allowOutputFor(username)) {
			console.log("Begin Profiling " + msg)
			console.profile();
		}
	},
 	
	/** call at the end of a section to profile (started with Debug.profile() */
	profileEnd: function (username) {
		if (DEBUG && Debug.allowOutputFor(username))
			console.profileEnd()
	}
}

var /** @define {boolean} */ DEBUG = Debug.isDev() && window.console && window.console.log;

if (!DEBUG && Debug) {
	var doNothing = function () {};
	Debug.assert = Debug.notify = doNothing;
	Debug.log = Debug.note = Debug.info = Debug.warn = Debug.error = doNothing;
	Debug.logTime = Debug.profileStart = Debug.profileEnd = doNothing;
	Debug.breakpoint = doNothing;
}

/******************************************************************************************************

	jQuery.ThreeDots

	Author Jeremy Horn
	Version 1.0.10 (Developed in Aptana Studio 1.5.1)
	Date: 1/25/2010

	Copyright (c) 2010 Jeremy Horn- jeremydhorn(at)gmail(dot)c0m | http://tpgblog.com
	Dual licensed under MIT and GPL.

	For more detailed documentation, including the latest updates and links to more usage and 
	examples, go to:
	
			http://tpgblog.com/ThreeDots/

	KNOWN BUGS
		None

	DESCRIPTION

		Sometimes the text ...
			... is too long ...
			... won't fit within the number of rows you have available.
		
		Sometimes all you need is ... ThreeDots!
		
		ThreeDots is a customizable jQuery plugin for the smart truncation of text.  It shortens 
		provided text to fit specified dimensions and appends the desired ellipsis style
		if/when truncation occurs.  		
		
		For example ---
		
			This:
				There was once a brown fox
				that liked to eat chocolate
				pudding.
			
			When restricted to 2 lines by ThreeDots, can become:
				There was once a brown fox
				that liked to eat ...
				
			Or:
				There was once a brown fox
				that liked to (click for more)

			... and most any other permutation you desire.


	BY DEFAULT
		The three dots ellipsis ("...") is used, as shown in the prior example, and limits
		text to a maximum of 2 lines.  These and many other characteristics are fully customizable,
		and fully itemized and explained below.


	IMPLEMENTATION

		HTML:		<div class='text_here'><span class='ellipsis_text'>TEXT</span></div>
		JS:			$('.text_here').ThreeDots(); // USE DEFAULTS
					$('.text_here2').ThreeDots({ { max_rows:3 });
		

	COMPATIBILITY

		Tested in FF3.5, IE7, Chrome
		With jQuery 1.3.x, 1.4

	METHODS

		ThreeDots()
		
		When intialized the ThreeDots plugin creates and assigns the full set of provided text 
		to each container element as a publically accessible attribute, 'threedots'.  Method 
		implementation supports chaining and returns jQuery object.

		Note that to implement, the text that you wish to ellipsize must be wrapped in a span
		assigned either the default class 'ellipsis_text' or other custom class of your 
		preference -- customizable via the options/settings.
		
		If the text becomes truncated to fit within the constrained space defined by the 
		container element that holds the 'ellipsis_text' span then an additional span is
		appended within the container object, and after the 'ellipsis_text' span.
		
		Note, that the span class of 'threedots_ellipsis' can also be customized via the 
		options/settings and have it's own CSS/jQuery styles/actions/etc. applied to it as
		desired.
		
		If any of the specified settings are invalid or the 'ellipsis_text' span is missing
		nothing will happen.

		IMPORTANT:	The horizontal constrains placed upon each row are controled by the 
					container object.  The container object is the object specified in the 
					primary selector.
					
						e.g. $('container_object').ThreeDots();
					
					So, remember to set container_object's WIDTH.
						
		ThreeDots.update()
			Refreshes the contents of the text within the target object inline with the
			options provided. Note, that the current implementation of options/settings
			are destructive.  This means that whenever OPTIONS are specified they are
			merged with the DEFAULT options and applied to the current object(s), and 
			destroy/override any previously specified options/settings.
			
				example:
					var obj = $('.text_here').ThreeDots();  // uses DEFAULT: max_rows = 2
					obj.update({max_rows:3});				// update the text with max_rows = 3

	CUSTOMIZATION

		ThreeDots(OPTIONS)
		e.g. $('.text_here').ThreeDots({ max_rows: 4 });
					
		
		valid_delimiters:	character array of special characters upon which the text string may be broken up;
							defines what characters can be used to express the bounds of a word
							
							all elements in this array must be 1 character in length; any delimiter less than 
							or greater than	1 character will be ignored
														
							
		ellipsis_string: 	defines what to display at the tail end of the text provided if the text becomes 
							truncated to fit within the space defined by the container object
												
							
		max_rows:			specifies the upper limit for the number of rows that the object's text can use
				
		
		text_span_class:	by default ThreeDots will look within the specified object(s) for a span
							of the class 'ellipsis_text'
							
		
		e_span_class:		if an ellipsis_string is displayed at the tail end of the selected object's
							text due to truncation of that text then it will be displayed wrapped within
							a span associated with the class defined by e_span_class and immediately
							following the text_span_class' span
		
		
		whole_word:			when fitting the provided text to the max_rows within the container object
							this boolean setting defines whether or not the 
							
								if true
									THEN	don't truncate any words; ellipsis can ONLY be placed after 
											the last whole word that fits within the provided space, OR
											
								if false
									THEN	maximuze the text within the provided space, allowing the 
											PARTIAL display of words before the ellipsis
		
		
		allow_dangle:		a dangling ellipsis is an ellipsis that typically occurs due to words that
							are longer than a single row of text, resulting, upon text truncation in
							the ellipsis being displayed on a row all by itself
													
							if allow_dangle is set to false, whole_words is overridden ONLY in the 
							circumstances where a dangling ellipsis occurs and the displayed text
							is adjusted to minimize the occurence of such dangling
									
		
		alt_text_e: 		alt_text_e is a shortcut to enabling the user of the product that 
							made use of ThreeDots to see the full text, prior to truncation
							
							if the value is set to true, then the ellipsis span's title property
							is set to the full, original text (pre-truncation)
		
		
		alt_text_t: 		alt_text_t is a shortcut to enabling the user of the product that 
							made use of ThreeDots to see the full text, prior to truncation
							
							if the value is set to true AND the ellipsis is displayed, then the 
							text span's title property is set to the full, original text 
							(pre-truncation) 
	

	MORE

		For latest updates and links to more usage and examples, go to:
			http://tpgblog.com/ThreeDots/
			
	FUTURE NOTE
	
		Do not write any code dependent on the c_settings variable.  If you don't know what this is
		cool -- you don't need to. ;-)  c_settings WILL BE DEPRECATED.
		
		Further optimizations in progress...

******************************************************************************************************/


(function($) {

	/**********************************************************************************

		METHOD
			ThreeDots {PUBLIC}

		DESCRIPTION
			ThreeDots method constructor
			
			allows for the customization of ellipsis, delimiters, etc., and smart 
			truncation of provided objects' text
					
				e.g. $(something).ThreeDots();
				* 
				* Add optional 'trailingText' string to reduce size by its length.

	**********************************************************************************/

	$.fn.ThreeDots = function(options, trailingText) {
		var return_value = this;

		// check for new & valid options
		if ((typeof options == 'object') || (options == undefined)) {
			$.fn.ThreeDots.the_selected = this;
			if (trailingText == null)
				trailingText = ''
			$.fn.ThreeDots.leaveSpaceForText = trailingText
			
			return_value = $.fn.ThreeDots.update(options);

		}
		
		return return_value;
	};


	/**********************************************************************************

		METHOD
			ThreeDots.update {PUBLIC}

		DESCRIPTION
			applies the core logic of ThreeDots
			
			allows for the customization of ellipsis, delimiters, etc., and smart 
			truncation of provided objects' text
			
			updates the objects' visible text to fit within its container(s)
		
		TODO
			instead of having all options/settings calls be constructive have 
			settings associated w/ object returned also accessible from HERE 
			[STATIC settings, associated w/ the initial call] 

	**********************************************************************************/

	$.fn.ThreeDots.update = function(options) {
		// initialize local variables
		var obj, last_word = null;
		var lineh, paddingt, paddingb, innerh, temp_height;
		var curr_text_span, lws; /* last word structure */
		var last_text, three_dots_value, last_del;

		// check for new & valid options
		if ((typeof options == 'object') || (options == undefined)) {

			// then update the settings
			// CURRENTLY, settings are not CONSTRUCTIVE, but merged with the DEFAULTS every time
			$.fn.ThreeDots.c_settings = $.extend({}, $.fn.ThreeDots.settings, options);
			var max_rows = $.fn.ThreeDots.c_settings.max_rows;
			if (max_rows < 1) {
				return $.fn.ThreeDots.the_selected;
			}

			// make sure at least 1 valid delimiter
			var valid_delimiter_exists = false;
			jQuery.each($.fn.ThreeDots.c_settings.valid_delimiters, function(i, curr_del) {
				if (((new String(curr_del)).length == 1)) {
					valid_delimiter_exists = true; 
				}
			});
			if (valid_delimiter_exists == false) {
				return $.fn.ThreeDots.the_selected;
			}
			
			// process all provided objects
			$.fn.ThreeDots.the_selected.each(function() {

				// element-specific code here
				obj = $(this);
		
				// obtain the text span
				if ($(obj).children('.'+$.fn.ThreeDots.c_settings.text_span_class).length == 0) { 
					// if span doesnt exist, then go to next
					return true;
				}
				curr_text_span = $(obj).children('.'+$.fn.ThreeDots.c_settings.text_span_class).get(0);

				// pre-calc fixed components of num_rows
				var nr_fixed = num_rows(obj, true);

				// remember where it all began so that we can see if we ended up exactly where we started
				var init_text_span = $(curr_text_span).text();

				// preprocessor
				the_bisector(obj, curr_text_span, nr_fixed);
				var init_post_b = $(curr_text_span).text();

				// if the object has been initialized, then user must be calling UPDATE
				// THEREFORE refresh the text area before re-operating
				if ((three_dots_value = $(obj).attr('threedots')) != undefined) {
					$(curr_text_span).text(three_dots_value);						
					$(obj).children('.'+$.fn.ThreeDots.c_settings.e_span_class).remove();
				}

				last_text = $(curr_text_span).text();
				if (last_text.length <= 0) {
					last_text = '';
				}
				
				//
				// Save initial text as an attr
				//
				$(obj).attr('threedots', init_text_span);

				if (num_rows(obj, nr_fixed) > max_rows) {
					// append the ellipsis span & remember the original text
					curr_ellipsis = $(obj).append('<span style="white-space:nowrap" class="'	
														+ $.fn.ThreeDots.c_settings.e_span_class + '">'
														+ $.fn.ThreeDots.c_settings.ellipsis_string 
														+ '</span>');
	
					// remove 1 word at a time UNTIL max_rows
					while (num_rows(obj, nr_fixed) > max_rows) {
						
						lws = the_last_word($(curr_text_span).text());// HERE
						$(curr_text_span).text(lws.updated_string);
						last_word = lws.word;
						last_del = lws.del;

						if (last_del == null) {
							break;					
						}
					} // while (num_rows(obj, nr_fixed) > max_rows)

					// check for super long words
					if (last_word != null) {
						var is_dangling = dangling_ellipsis(obj, nr_fixed);

						if ((num_rows(obj, nr_fixed) <= max_rows - 1) 
							|| (is_dangling) 
							|| (!$.fn.ThreeDots.c_settings.whole_word)) {

							last_text = $(curr_text_span).text();
							if (lws.del != null) {
								$(curr_text_span).text(last_text + last_del);
							}
									
							if (num_rows(obj, nr_fixed) > max_rows) {
								// undo what i just did and stop
								$(curr_text_span).text(last_text);
							} else {
								// keep going
								$(curr_text_span).text($(curr_text_span).text() + last_word);
								
								// break up the last word IFF (1) word is longer than a line, OR (2) whole_word == false
								if ((num_rows(obj, nr_fixed) > max_rows + 1) 
									|| (!$.fn.ThreeDots.c_settings.whole_word)
									|| (init_post_b == last_word)
									|| is_dangling) {
									// remove 1 char at a time until it all fits
									while ((num_rows(obj, nr_fixed) > max_rows)) {
										if ($(curr_text_span).text().length > 0) {
											$(curr_text_span).text(
												$(curr_text_span).text().substr(0, $(curr_text_span).text().length - 1)
											);
										} else {
											/* 
											 there is no hope for you; you are crazy;
											 either pick a shorter ellipsis_string OR
											 use a wider object --- geeze!
											 */
											break;
										}
									}							
								}
							}
						}
					}
				}	
				
				// Restore the initial text
				var textSpan = $(obj).children('.' + $.fn.ThreeDots.c_settings.text_span_class).get(0);
				$(textSpan).text($(textSpan).text().substr(0, $(textSpan).text().length - $.fn.ThreeDots.leaveSpaceForText.length))
				
				// if nothing has changed, remove the ellipsis
				if (init_text_span == $($(obj).children('.' + $.fn.ThreeDots.c_settings.text_span_class).get(0)).text()) {
					$(obj).children('.' + $.fn.ThreeDots.c_settings.e_span_class).remove();
				} else {				
					// only add any title text if the ellipsis is visible
					if (($(obj).children('.' + $.fn.ThreeDots.c_settings.e_span_class)).length > 0) {
						if ($.fn.ThreeDots.c_settings.alt_text_t) {
							$(obj).children('.' + $.fn.ThreeDots.c_settings.text_span_class).attr('title', init_text_span);
						}
						
						if ($.fn.ThreeDots.c_settings.alt_text_e) {
							$(obj).children('.' + $.fn.ThreeDots.c_settings.e_span_class).attr('title', init_text_span);
						}
						
					}
				}
			}); // $.fn.ThreeDots.the_selected.each(function() 
		}

		return $.fn.ThreeDots.the_selected;
	};


	/**********************************************************************************

		METHOD
			ThreeDots.settings {PUBLIC}

		DESCRIPTION
			data structure containing the max_rows, ellipsis string, and other
			behavioral settings
			
			can be directly accessed by '$.fn.ThreeDots.settings = ...... ;'

	**********************************************************************************/

	$.fn.ThreeDots.settings = {
		valid_delimiters: 	[' ', ',', '.'],		// what defines the bounds of a word to you?
		ellipsis_string: 	'...',
		max_rows:			2,
		text_span_class:	'ellipsis_text',
		e_span_class:		'threedots_ellipsis',
		whole_word:			true,
		allow_dangle:		false,
		alt_text_e: 		false,					// if true, mouse over of ellipsis displays the full text
		alt_text_t: 		false  					// if true & if ellipsis displayed, mouse over of text displays the full text
	};


	/**********************************************************************************

		METHOD
			dangling_ellipsis {private}

		DESCRIPTION
			determines whether or not the currently calculated ellipsized text
			is displaying a dangling ellipsis (= an ellipsis on a line by itself)
			
			returns true if ellipsis is dangling, otherwise false

	**********************************************************************************/

	function dangling_ellipsis(obj, nr_fixed){
		if ($.fn.ThreeDots.c_settings.allow_dangle == true) {
			return false; // why do when no doing need be done?
		}

		// initialize variables
		var ellipsis_obj 		= $(obj).children('.'+$.fn.ThreeDots.c_settings.e_span_class).get(0);
		var remember_display 	= $(ellipsis_obj).css('display');
		var num_rows_before 	= num_rows(obj, nr_fixed);

		// temporarily hide ellipsis
		$(ellipsis_obj).css('display','none');
		var num_rows_after 		= num_rows(obj, nr_fixed);

		// restore ellipsis
		$(ellipsis_obj).css('display',remember_display);
		
		if (num_rows_before > num_rows_after) {
			return true; 	// ASSUMPTION: 	removing the ellipsis changed the height
							// 				THEREFORE the ellipsis was on a row all by its lonesome
		} else {
			return false;	// nothing dangling here
		}
	}


	/**********************************************************************************

		METHOD
			num_rows {private}

		DESCRIPTION
			returns the number of rows/lines that the current object's text covers if
			cstate is an object
			
			this function can be initially called to pre-calculate values that will 
			stay fixed throughout the truncation process for the current object so
			that the values do not have to be called every time; to do this the
			num_rows function is called with a boolean value within the cstate
			
			when boolean cstate, an object is returned containing padding and line
			height information that is then passed in as the cstate object on
			subsequent calls to the function

	**********************************************************************************/

	function num_rows(obj, cstate){	
		var the_type = typeof cstate;
	
		if (	(the_type == 'object') 
			||	(the_type == undefined)	) {

			// do the math & return
			return $(obj).height() / cstate.lh;
			
		} else if (the_type == 'boolean') {
			var lineheight	= lineheight_px($(obj));

			return {
				lh: lineheight
			};
		} 
	}

	
	/**********************************************************************************

		METHOD
			the_last_word {private}

		DESCRIPTION
			return a data structure containing...
			 
				[word] 				the last word within the specified text	defined 
									by the specified valid_delimiters, 
				[del] 				the delimiter occurring	directly before the 
									word, and 
				[updated_string] 	the updated text minus the last word 
			
			[del] is null if the last word is the first and/or only word in the text 
			string

	**********************************************************************************/

	function the_last_word(str){
		var temp_word_index;
		var v_del = $.fn.ThreeDots.c_settings.valid_delimiters;
		
		// trim the string
		str = jQuery.trim(str);
		
		// initialize variables
		var lastest_word_idx = -1;
		var lastest_word = null;
		var lastest_del = null;

		// for all given delimiters, determine which delimiter results in the smallest word cut
		jQuery.each(v_del, function(i, curr_del){
			if (((new String(curr_del)).length != 1)
				|| (curr_del == null)) {  // implemented to handle IE NULL condition; if only typeof could say CHAR :(
				return false; // INVALID delimiter; must be 1 character in length
			}

			var tmp_word_index = str.lastIndexOf(curr_del);
			if (tmp_word_index != -1) {
				if (tmp_word_index > lastest_word_idx) {
					lastest_word_idx 	= tmp_word_index;
					lastest_word 		= str.substring(lastest_word_idx+1);
					lastest_del			= curr_del;
				}
			}
		});
		
		// return data structure of word reduced string and the last word
		if (lastest_word_idx > 0) {
			return {
				updated_string:	jQuery.trim(str.substring(0, lastest_word_idx/*-1*/)),
				word: 			lastest_word,
				del: 			lastest_del
			};
		} else { // the lastest word
			return {
				updated_string:	'',
				word: 			jQuery.trim(str),
				del: 			null
			};
		}
	}

			
	/**********************************************************************************

		METHOD
			lineheight_px {private}

		DESCRIPTION
			returns the line height of a row of the provided text (within the text 
			span) in pixels

	**********************************************************************************/

	function lineheight_px(obj) {
		// shhhh... show
		$(obj).append("<div id='temp_ellipsis_div' style='position:absolute; visibility:hidden'>H</div>");
		// measure
		var temp_height = $('#temp_ellipsis_div').height();
		// cut
		$('#temp_ellipsis_div').remove();

		return temp_height;
	}
	
	/**********************************************************************************

		METHOD
			the_bisector (private)

		DESCRIPTION
			updates the target objects current text to shortest overflowing string 
			length (if overflowing is occurring) by adding/removing halves (like
			binary search)

			because...
				taking some bigger steps at the beginning should save us some real 
				time in the end

	**********************************************************************************/
	
	function the_bisector(obj, curr_text_span, nr_fixed){
		var init_text = $(curr_text_span).text();
		var curr_text = init_text;
		var max_rows = $.fn.ThreeDots.c_settings.max_rows;
		var front_half, back_half, front_of_back_half, middle, back_middle;
		var start_index;
		
		if (num_rows(obj, nr_fixed) <= max_rows) {
			// do nothing
			return;
		} else {
			// zero in on the solution
			start_index = 0;
			curr_length = curr_text.length;

			curr_middle = Math.floor((curr_length - start_index) / 2);
			front_half = init_text.substring(start_index, start_index+curr_middle);
			back_half = init_text.substring(start_index + curr_middle);
				
			while (curr_middle != 0) {
				$(curr_text_span).text(front_half);
				
				if (num_rows(obj, nr_fixed) <= (max_rows)) {
					// text = text + front half of back-half
					back_middle 		= Math.floor(back_half.length/2);
					front_of_back_half 	= back_half.substring(0, back_middle);
					
					start_index = front_half.length;
					curr_text 	= front_half+front_of_back_half;
					curr_length = curr_text.length;

					$(curr_text_span).text(curr_text);
				} else {
					// text = front half (which it already is)
					curr_text = front_half;
					curr_length = curr_text.length;
				}
				
				curr_middle = Math.floor((curr_length - start_index) / 2);
				front_half = init_text.substring(0, start_index+curr_middle);
				back_half = init_text.substring(start_index + curr_middle);
			}
		}
	}
	
})(jQuery);
/*
 * Add :focus to jQuery (Brent)
 */
jQuery.expr[':'].focus = function(elem) {
	return elem === document.activeElement && (elem.type || elem.href);
};

/*
 * Fast .each()
 * 	No need to convert to a jQuery object within the loop.
 * 
 * Usage: .each$(function(i, element){...})
 * 
 * 	$('.myClass').each$(function(i, elem) { elem.text(i); ... })
 */
jQuery.fn.each$ = (function() {
    var jq = jQuery([1]);
	
    return function (f) {
        try {
			var len = this.length;
			var elem;
	        var i = -1;
			
			// The meat: call given function using pre-allocated jQuery element
            while (++i < len && (elem = jq[0] = this[i])) 
                f.call(jq, i, elem);
			
            delete jq[0];
        } 
        catch (e) {
            delete jq[0];
            throw e;
        }
        
        return this;
    };
}());

jQuery.fn.select = function(){
    var text = $(this).get(0);
    if ($.browser.msie) {
        var range = document.body.createTextRange();
        range.moveToElementText(text);
        range.select();
    }
    else 
        if ($.browser.mozilla || $.browser.opera) {
            var selection = window.getSelection();
            var range = document.createRange();
            range.selectNodeContents(text);
            selection.removeAllRanges();
            selection.addRange(range);
        }
        else 
            if ($.browser.safari) {
                var selection = window.getSelection();
                selection.setBaseAndExtent(text, 0, text, 1);
            }
    return this;
    
};

/*!
 * jQuery BBQ: Back Button & Query Library - v1.2.1 - 2/17/2010
 * http://benalman.com/projects/jquery-bbq-plugin/
 * 
 * Copyright (c) 2010 "Cowboy" Ben Alman
 * Dual licensed under the MIT and GPL licenses.
 * http://benalman.com/about/license/
 */

// Script: jQuery BBQ: Back Button & Query Library
//
// *Version: 1.2.1, Last updated: 2/17/2010*
// 
// Project Home - http://benalman.com/projects/jquery-bbq-plugin/
// GitHub       - http://github.com/cowboy/jquery-bbq/
// Source       - http://github.com/cowboy/jquery-bbq/raw/master/jquery.ba-bbq.js
// (Minified)   - http://github.com/cowboy/jquery-bbq/raw/master/jquery.ba-bbq.min.js (4.0kb)
// 
// About: License
// 
// Copyright (c) 2010 "Cowboy" Ben Alman,
// Dual licensed under the MIT and GPL licenses.
// http://benalman.com/about/license/
// 
// About: Examples
// 
// These working examples, complete with fully commented code, illustrate a few
// ways in which this plugin can be used.
// 
// Basic AJAX     - http://benalman.com/code/projects/jquery-bbq/examples/fragment-basic/
// Advanced AJAX  - http://benalman.com/code/projects/jquery-bbq/examples/fragment-advanced/
// jQuery UI Tabs - http://benalman.com/code/projects/jquery-bbq/examples/fragment-jquery-ui-tabs/
// Deparam        - http://benalman.com/code/projects/jquery-bbq/examples/deparam/
// 
// About: Support and Testing
// 
// Information about what version or versions of jQuery this plugin has been
// tested with, what browsers it has been tested in, and where the unit tests
// reside (so you can test it yourself).
// 
// jQuery Versions - 1.3.2, 1.4.1, 1.4.2
// Browsers Tested - Internet Explorer 6-8, Firefox 2-3.7, Safari 3-4,
//                   Chrome 4-5, Opera 9.6-10.1.
// Unit Tests      - http://benalman.com/code/projects/jquery-bbq/unit/
// 
// About: Release History
// 
// 1.2.1 - (2/17/2010) Actually fixed the stale window.location Safari bug from
//         <jQuery hashchange event> in BBQ, which was the main reason for the
//         previous release!
// 1.2   - (2/16/2010) Integrated <jQuery hashchange event> v1.2, which fixes a
//         Safari bug, the event can now be bound before DOM ready, and IE6/7
//         page should no longer scroll when the event is first bound. Also
//         added the <jQuery.param.fragment.noEscape> method, and reworked the
//         <hashchange event (BBQ)> internal "add" method to be compatible with
//         changes made to the jQuery 1.4.2 special events API.
// 1.1.1 - (1/22/2010) Integrated <jQuery hashchange event> v1.1, which fixes an
//         obscure IE8 EmulateIE7 meta tag compatibility mode bug.
// 1.1   - (1/9/2010) Broke out the jQuery BBQ event.special <hashchange event>
//         functionality into a separate plugin for users who want just the
//         basic event & back button support, without all the extra awesomeness
//         that BBQ provides. This plugin will be included as part of jQuery BBQ,
//         but also be available separately. See <jQuery hashchange event>
//         plugin for more information. Also added the <jQuery.bbq.removeState>
//         method and added additional <jQuery.deparam> examples.
// 1.0.3 - (12/2/2009) Fixed an issue in IE 6 where location.search and
//         location.hash would report incorrectly if the hash contained the ?
//         character. Also <jQuery.param.querystring> and <jQuery.param.fragment>
//         will no longer parse params out of a URL that doesn't contain ? or #,
//         respectively.
// 1.0.2 - (10/10/2009) Fixed an issue in IE 6/7 where the hidden IFRAME caused
//         a "This page contains both secure and nonsecure items." warning when
//         used on an https:// page.
// 1.0.1 - (10/7/2009) Fixed an issue in IE 8. Since both "IE7" and "IE8
//         Compatibility View" modes erroneously report that the browser
//         supports the native window.onhashchange event, a slightly more
//         robust test needed to be added.
// 1.0   - (10/2/2009) Initial release

(function($,window){
  '$:nomunge'; // Used by YUI compressor.
  
  // Some convenient shortcuts.
  var undefined,
    aps = Array.prototype.slice,
    decode = decodeURIComponent,
    
    // Method / object references.
    jq_param = $.param,
    jq_param_fragment,
    jq_deparam,
    jq_deparam_fragment,
    jq_bbq = $.bbq = $.bbq || {},
    jq_bbq_pushState,
    jq_bbq_getState,
    jq_elemUrlAttr,
    jq_event_special = $.event.special,
    
    // Reused strings.
    str_hashchange = 'hashchange',
    str_querystring = 'querystring',
    str_fragment = 'fragment',
    str_elemUrlAttr = 'elemUrlAttr',
    str_location = 'location',
    str_href = 'href',
    str_src = 'src',
    
    // Reused RegExp.
    re_trim_querystring = /^.*\?|#.*$/g,
    re_trim_fragment = /^.*\#/,
    re_no_escape,
    
    // Used by jQuery.elemUrlAttr.
    elemUrlAttr_cache = {};
  
  // A few commonly used bits, broken out to help reduce minified file size.
  
  function is_string( arg ) {
    return typeof arg === 'string';
  };
  
  // Why write the same function twice? Let's curry! Mmmm, curry..
  
  function curry( func ) {
    var args = aps.call( arguments, 1 );
    
    return function() {
      return func.apply( this, args.concat( aps.call( arguments ) ) );
    };
  };
  
  // Get location.hash (or what you'd expect location.hash to be) sans any
  // leading #. Thanks for making this necessary, Firefox!
  function get_fragment( url ) {
    return url.replace( /^[^#]*#?(.*)$/, '$1' );
  };
  
  // Get location.search (or what you'd expect location.search to be) sans any
  // leading #. Thanks for making this necessary, IE6!
  function get_querystring( url ) {
    return url.replace( /(?:^[^?#]*\?([^#]*).*$)?.*/, '$1' );
  };
  
  // Section: Param (to string)
  // 
  // Method: jQuery.param.querystring
  // 
  // Retrieve the query string from a URL or if no arguments are passed, the
  // current window.location.
  // 
  // Usage:
  // 
  // > jQuery.param.querystring( [ url ] );
  // 
  // Arguments:
  // 
  //  url - (String) A URL containing query string params to be parsed. If url
  //    is not passed, the current window.location is used.
  // 
  // Returns:
  // 
  //  (String) The parsed query string, with any leading "?" removed.
  //
  
  // Method: jQuery.param.querystring (build url)
  // 
  // Merge a URL, with or without pre-existing query string params, plus any
  // object, params string or URL containing query string params into a new URL.
  // 
  // Usage:
  // 
  // > jQuery.param.querystring( url, params [, merge_mode ] );
  // 
  // Arguments:
  // 
  //  url - (String) A valid URL for params to be merged into. This URL may
  //    contain a query string and/or fragment (hash).
  //  params - (String) A params string or URL containing query string params to
  //    be merged into url.
  //  params - (Object) A params object to be merged into url.
  //  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
  //    specified, and is as-follows:
  // 
  //    * 0: params in the params argument will override any query string
  //         params in url.
  //    * 1: any query string params in url will override params in the params
  //         argument.
  //    * 2: params argument will completely replace any query string in url.
  // 
  // Returns:
  // 
  //  (String) Either a params string with urlencoded data or a URL with a
  //    urlencoded query string in the format 'a=b&c=d&e=f'.
  
  // Method: jQuery.param.fragment
  // 
  // Retrieve the fragment (hash) from a URL or if no arguments are passed, the
  // current window.location.
  // 
  // Usage:
  // 
  // > jQuery.param.fragment( [ url ] );
  // 
  // Arguments:
  // 
  //  url - (String) A URL containing fragment (hash) params to be parsed. If
  //    url is not passed, the current window.location is used.
  // 
  // Returns:
  // 
  //  (String) The parsed fragment (hash) string, with any leading "#" removed.
  
  // Method: jQuery.param.fragment (build url)
  // 
  // Merge a URL, with or without pre-existing fragment (hash) params, plus any
  // object, params string or URL containing fragment (hash) params into a new
  // URL.
  // 
  // Usage:
  // 
  // > jQuery.param.fragment( url, params [, merge_mode ] );
  // 
  // Arguments:
  // 
  //  url - (String) A valid URL for params to be merged into. This URL may
  //    contain a query string and/or fragment (hash).
  //  params - (String) A params string or URL containing fragment (hash) params
  //    to be merged into url.
  //  params - (Object) A params object to be merged into url.
  //  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
  //    specified, and is as-follows:
  // 
  //    * 0: params in the params argument will override any fragment (hash)
  //         params in url.
  //    * 1: any fragment (hash) params in url will override params in the
  //         params argument.
  //    * 2: params argument will completely replace any query string in url.
  // 
  // Returns:
  // 
  //  (String) Either a params string with urlencoded data or a URL with a
  //    urlencoded fragment (hash) in the format 'a=b&c=d&e=f'.
  
  function jq_param_sub( is_fragment, get_func, url, params, merge_mode ) {
    var result,
      qs,
      matches,
      url_params,
      hash;
    
    if ( params !== undefined ) {
      // Build URL by merging params into url string.
      
      // matches[1] = url part that precedes params, not including trailing ?/#
      // matches[2] = params, not including leading ?/#
      // matches[3] = if in 'querystring' mode, hash including leading #, otherwise ''
      matches = url.match( is_fragment ? /^([^#]*)\#?(.*)$/ : /^([^#?]*)\??([^#]*)(#?.*)/ );
      
      // Get the hash if in 'querystring' mode, and it exists.
      hash = matches[3] || '';
      
      if ( merge_mode === 2 && is_string( params ) ) {
        // If merge_mode is 2 and params is a string, merge the fragment / query
        // string into the URL wholesale, without converting it into an object.
        qs = params.replace( is_fragment ? re_trim_fragment : re_trim_querystring, '' );
        
      } else {
        // Convert relevant params in url to object.
        url_params = jq_deparam( matches[2] );
        
        params = is_string( params )
          
          // Convert passed params string into object.
          ? jq_deparam[ is_fragment ? str_fragment : str_querystring ]( params )
          
          // Passed params object.
          : params;
        
        qs = merge_mode === 2 ? params                              // passed params replace url params
          : merge_mode === 1  ? $.extend( {}, params, url_params )  // url params override passed params
          : $.extend( {}, url_params, params );                     // passed params override url params
        
        // Convert params object to a string.
        qs = jq_param( qs );
        
        // Unescape characters specified via $.param.noEscape. Since only hash-
        // history users have requested this feature, it's only enabled for
        // fragment-related params strings.
        if ( is_fragment ) {
          qs = qs.replace( re_no_escape, decode );
        }
      }
      
      // Build URL from the base url, querystring and hash. In 'querystring'
      // mode, ? is only added if a query string exists. In 'fragment' mode, #
      // is always added.
      result = matches[1] + ( is_fragment ? '#' : qs || !matches[1] ? '?' : '' ) + qs + hash;
      
    } else {
      // If URL was passed in, parse params from URL string, otherwise parse
      // params from window.location.
      result = get_func( url !== undefined ? url : window[ str_location ][ str_href ] );
    }
    
    return result;
  };
  
  jq_param[ str_querystring ]                  = curry( jq_param_sub, 0, get_querystring );
  jq_param[ str_fragment ] = jq_param_fragment = curry( jq_param_sub, 1, get_fragment );
  
  // Method: jQuery.param.fragment.noEscape
  // 
  // Specify characters that will be left unescaped when fragments are created
  // or merged using <jQuery.param.fragment>, or when the fragment is modified
  // using <jQuery.bbq.pushState>. This option only applies to serialized data
  // object fragments, and not set-as-string fragments. Does not affect the
  // query string. Defaults to ",/" (comma, forward slash).
  // 
  // Note that this is considered a purely aesthetic option, and will help to
  // create URLs that "look pretty" in the address bar or bookmarks, without
  // affecting functionality in any way. That being said, be careful to not
  // unescape characters that are used as delimiters or serve a special
  // purpose, such as the "#?&=+" (octothorpe, question mark, ampersand,
  // equals, plus) characters.
  // 
  // Usage:
  // 
  // > jQuery.param.fragment.noEscape( [ chars ] );
  // 
  // Arguments:
  // 
  //  chars - (String) The characters to not escape in the fragment. If
  //    unspecified, defaults to empty string (escape all characters).
  // 
  // Returns:
  // 
  //  Nothing.
  
  jq_param_fragment.noEscape = function( chars ) {
    chars = chars || '';
    var arr = $.map( chars.split(''), encodeURIComponent );
    re_no_escape = new RegExp( arr.join('|'), 'g' );
  };
  
  // A sensible default. These are the characters people seem to complain about
  // "uglifying up the URL" the most.
  jq_param_fragment.noEscape( ',/' );
  
  // Section: Deparam (from string)
  // 
  // Method: jQuery.deparam
  // 
  // Deserialize a params string into an object, optionally coercing numbers,
  // booleans, null and undefined values; this method is the counterpart to the
  // internal jQuery.param method.
  // 
  // Usage:
  // 
  // > jQuery.deparam( params [, coerce ] );
  // 
  // Arguments:
  // 
  //  params - (String) A params string to be parsed.
  //  coerce - (Boolean) If true, coerces any numbers or true, false, null, and
  //    undefined to their actual value. Defaults to false if omitted.
  // 
  // Returns:
  // 
  //  (Object) An object representing the deserialized params string.
  
  $.deparam = jq_deparam = function( params, coerce ) {
    var obj = {},
      coerce_types = { 'true': !0, 'false': !1, 'null': null };
    
    // Iterate over all name=value pairs.
    $.each( params.replace( /\+/g, ' ' ).split( '&' ), function(j,v){
      var param = v.split( '=' ),
        key = decode( param[0] ),
        val,
        cur = obj,
        i = 0,
        
        // If key is more complex than 'foo', like 'a[]' or 'a[b][c]', split it
        // into its component parts.
        keys = key.split( '][' ),
        keys_last = keys.length - 1;
      
      // If the first keys part contains [ and the last ends with ], then []
      // are correctly balanced.
      if ( /\[/.test( keys[0] ) && /\]$/.test( keys[ keys_last ] ) ) {
        // Remove the trailing ] from the last keys part.
        keys[ keys_last ] = keys[ keys_last ].replace( /\]$/, '' );
        
        // Split first keys part into two parts on the [ and add them back onto
        // the beginning of the keys array.
        keys = keys.shift().split('[').concat( keys );
        
        keys_last = keys.length - 1;
      } else {
        // Basic 'foo' style key.
        keys_last = 0;
      }
      
      // Are we dealing with a name=value pair, or just a name?
      if ( param.length === 2 ) {
        val = decode( param[1] );
        
        // Coerce values.
        if ( coerce ) {
          val = val && !isNaN(val)            ? +val              // number
            : val === 'undefined'             ? undefined         // undefined
            : coerce_types[val] !== undefined ? coerce_types[val] // true, false, null
            : val;                                                // string
        }
        
        if ( keys_last ) {
          // Complex key, build deep object structure based on a few rules:
          // * The 'cur' pointer starts at the object top-level.
          // * [] = array push (n is set to array length), [n] = array if n is 
          //   numeric, otherwise object.
          // * If at the last keys part, set the value.
          // * For each keys part, if the current level is undefined create an
          //   object or array based on the type of the next keys part.
          // * Move the 'cur' pointer to the next level.
          // * Rinse & repeat.
          for ( ; i <= keys_last; i++ ) {
            key = keys[i] === '' ? cur.length : keys[i];
            cur = cur[key] = i < keys_last
              ? cur[key] || ( keys[i+1] && isNaN( keys[i+1] ) ? {} : [] )
              : val;
          }
          
        } else {
          // Simple key, even simpler rules, since only scalars and shallow
          // arrays are allowed.
          
          if ( $.isArray( obj[key] ) ) {
            // val is already an array, so push on the next value.
            obj[key].push( val );
            
          } else if ( obj[key] !== undefined ) {
            // val isn't an array, but since a second value has been specified,
            // convert val into an array.
            obj[key] = [ obj[key], val ];
            
          } else {
            // val is a scalar.
            obj[key] = val;
          }
        }
        
      } else if ( key ) {
        // No value was defined, so set something meaningful.
        obj[key] = coerce
          ? undefined
          : '';
      }
    });
    
    return obj;
  };
  
  // Method: jQuery.deparam.querystring
  // 
  // Parse the query string from a URL or the current window.location,
  // deserializing it into an object, optionally coercing numbers, booleans,
  // null and undefined values.
  // 
  // Usage:
  // 
  // > jQuery.deparam.querystring( [ url ] [, coerce ] );
  // 
  // Arguments:
  // 
  //  url - (String) An optional params string or URL containing query string
  //    params to be parsed. If url is omitted, the current window.location
  //    is used.
  //  coerce - (Boolean) If true, coerces any numbers or true, false, null, and
  //    undefined to their actual value. Defaults to false if omitted.
  // 
  // Returns:
  // 
  //  (Object) An object representing the deserialized params string.
  
  // Method: jQuery.deparam.fragment
  // 
  // Parse the fragment (hash) from a URL or the current window.location,
  // deserializing it into an object, optionally coercing numbers, booleans,
  // null and undefined values.
  // 
  // Usage:
  // 
  // > jQuery.deparam.fragment( [ url ] [, coerce ] );
  // 
  // Arguments:
  // 
  //  url - (String) An optional params string or URL containing fragment (hash)
  //    params to be parsed. If url is omitted, the current window.location
  //    is used.
  //  coerce - (Boolean) If true, coerces any numbers or true, false, null, and
  //    undefined to their actual value. Defaults to false if omitted.
  // 
  // Returns:
  // 
  //  (Object) An object representing the deserialized params string.
  
  function jq_deparam_sub( is_fragment, url_or_params, coerce ) {
    if ( url_or_params === undefined || typeof url_or_params === 'boolean' ) {
      // url_or_params not specified.
      coerce = url_or_params;
      url_or_params = jq_param[ is_fragment ? str_fragment : str_querystring ]();
    } else {
      url_or_params = is_string( url_or_params )
        ? url_or_params.replace( is_fragment ? re_trim_fragment : re_trim_querystring, '' )
        : url_or_params;
    }
    
    return jq_deparam( url_or_params, coerce );
  };
  
  jq_deparam[ str_querystring ]                    = curry( jq_deparam_sub, 0 );
  jq_deparam[ str_fragment ] = jq_deparam_fragment = curry( jq_deparam_sub, 1 );
  
  // Section: Element manipulation
  // 
  // Method: jQuery.elemUrlAttr
  // 
  // Get the internal "Default URL attribute per tag" list, or augment the list
  // with additional tag-attribute pairs, in case the defaults are insufficient.
  // 
  // In the <jQuery.fn.querystring> and <jQuery.fn.fragment> methods, this list
  // is used to determine which attribute contains the URL to be modified, if
  // an "attr" param is not specified.
  // 
  // Default Tag-Attribute List:
  // 
  //  a      - href
  //  base   - href
  //  iframe - src
  //  img    - src
  //  input  - src
  //  form   - action
  //  link   - href
  //  script - src
  // 
  // Usage:
  // 
  // > jQuery.elemUrlAttr( [ tag_attr ] );
  // 
  // Arguments:
  // 
  //  tag_attr - (Object) An object containing a list of tag names and their
  //    associated default attribute names in the format { tag: 'attr', ... } to
  //    be merged into the internal tag-attribute list.
  // 
  // Returns:
  // 
  //  (Object) An object containing all stored tag-attribute values.
  
  // Only define function and set defaults if function doesn't already exist, as
  // the urlInternal plugin will provide this method as well.
  $[ str_elemUrlAttr ] || ($[ str_elemUrlAttr ] = function( obj ) {
    return $.extend( elemUrlAttr_cache, obj );
  })({
    a: str_href,
    base: str_href,
    iframe: str_src,
    img: str_src,
    input: str_src,
    form: 'action',
    link: str_href,
    script: str_src
  });
  
  jq_elemUrlAttr = $[ str_elemUrlAttr ];
  
  // Method: jQuery.fn.querystring
  // 
  // Update URL attribute in one or more elements, merging the current URL (with
  // or without pre-existing query string params) plus any params object or
  // string into a new URL, which is then set into that attribute. Like
  // <jQuery.param.querystring (build url)>, but for all elements in a jQuery
  // collection.
  // 
  // Usage:
  // 
  // > jQuery('selector').querystring( [ attr, ] params [, merge_mode ] );
  // 
  // Arguments:
  // 
  //  attr - (String) Optional name of an attribute that will contain a URL to
  //    merge params or url into. See <jQuery.elemUrlAttr> for a list of default
  //    attributes.
  //  params - (Object) A params object to be merged into the URL attribute.
  //  params - (String) A URL containing query string params, or params string
  //    to be merged into the URL attribute.
  //  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
  //    specified, and is as-follows:
  //    
  //    * 0: params in the params argument will override any params in attr URL.
  //    * 1: any params in attr URL will override params in the params argument.
  //    * 2: params argument will completely replace any query string in attr
  //         URL.
  // 
  // Returns:
  // 
  //  (jQuery) The initial jQuery collection of elements, but with modified URL
  //  attribute values.
  
  // Method: jQuery.fn.fragment
  // 
  // Update URL attribute in one or more elements, merging the current URL (with
  // or without pre-existing fragment/hash params) plus any params object or
  // string into a new URL, which is then set into that attribute. Like
  // <jQuery.param.fragment (build url)>, but for all elements in a jQuery
  // collection.
  // 
  // Usage:
  // 
  // > jQuery('selector').fragment( [ attr, ] params [, merge_mode ] );
  // 
  // Arguments:
  // 
  //  attr - (String) Optional name of an attribute that will contain a URL to
  //    merge params into. See <jQuery.elemUrlAttr> for a list of default
  //    attributes.
  //  params - (Object) A params object to be merged into the URL attribute.
  //  params - (String) A URL containing fragment (hash) params, or params
  //    string to be merged into the URL attribute.
  //  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
  //    specified, and is as-follows:
  //    
  //    * 0: params in the params argument will override any params in attr URL.
  //    * 1: any params in attr URL will override params in the params argument.
  //    * 2: params argument will completely replace any fragment (hash) in attr
  //         URL.
  // 
  // Returns:
  // 
  //  (jQuery) The initial jQuery collection of elements, but with modified URL
  //  attribute values.
  
  function jq_fn_sub( mode, force_attr, params, merge_mode ) {
    if ( !is_string( params ) && typeof params !== 'object' ) {
      // force_attr not specified.
      merge_mode = params;
      params = force_attr;
      force_attr = undefined;
    }
    
    return this.each(function(){
      var that = $(this),
        
        // Get attribute specified, or default specified via $.elemUrlAttr.
        attr = force_attr || jq_elemUrlAttr()[ ( this.nodeName || '' ).toLowerCase() ] || '',
        
        // Get URL value.
        url = attr && that.attr( attr ) || '';
      
      // Update attribute with new URL.
      that.attr( attr, jq_param[ mode ]( url, params, merge_mode ) );
    });
    
  };
  
  $.fn[ str_querystring ] = curry( jq_fn_sub, str_querystring );
  $.fn[ str_fragment ]    = curry( jq_fn_sub, str_fragment );
  
  // Section: History, hashchange event
  // 
  // Method: jQuery.bbq.pushState
  // 
  // Adds a 'state' into the browser history at the current position, setting
  // location.hash and triggering any bound <hashchange event> callbacks
  // (provided the new state is different than the previous state).
  // 
  // If no arguments are passed, an empty state is created, which is just a
  // shortcut for jQuery.bbq.pushState( {}, 2 ).
  // 
  // Usage:
  // 
  // > jQuery.bbq.pushState( [ params [, merge_mode ] ] );
  // 
  // Arguments:
  // 
  //  params - (String) A serialized params string or a hash string beginning
  //    with # to merge into location.hash.
  //  params - (Object) A params object to merge into location.hash.
  //  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
  //    specified (unless a hash string beginning with # is specified, in which
  //    case merge behavior defaults to 2), and is as-follows:
  // 
  //    * 0: params in the params argument will override any params in the
  //         current state.
  //    * 1: any params in the current state will override params in the params
  //         argument.
  //    * 2: params argument will completely replace current state.
  // 
  // Returns:
  // 
  //  Nothing.
  // 
  // Additional Notes:
  // 
  //  * Setting an empty state may cause the browser to scroll.
  //  * Unlike the fragment and querystring methods, if a hash string beginning
  //    with # is specified as the params agrument, merge_mode defaults to 2.
  
  jq_bbq.pushState = jq_bbq_pushState = function( params, merge_mode ) {
    if ( is_string( params ) && /^#/.test( params ) && merge_mode === undefined ) {
      // Params string begins with # and merge_mode not specified, so completely
      // overwrite window.location.hash.
      merge_mode = 2;
    }
    
    var has_args = params !== undefined,
      // Merge params into window.location using $.param.fragment.
      url = jq_param_fragment( window[ str_location ][ str_href ],
        has_args ? params : {}, has_args ? merge_mode : 2 );
    
    // Set new window.location.href. If hash is empty, use just # to prevent
    // browser from reloading the page. Note that Safari 3 & Chrome barf on
    // location.hash = '#'.
    window[ str_location ][ str_href ] = url + ( /#/.test( url ) ? '' : '#' );
  };
  
  // Method: jQuery.bbq.getState
  // 
  // Retrieves the current 'state' from the browser history, parsing
  // location.hash for a specific key or returning an object containing the
  // entire state, optionally coercing numbers, booleans, null and undefined
  // values.
  // 
  // Usage:
  // 
  // > jQuery.bbq.getState( [ key ] [, coerce ] );
  // 
  // Arguments:
  // 
  //  key - (String) An optional state key for which to return a value.
  //  coerce - (Boolean) If true, coerces any numbers or true, false, null, and
  //    undefined to their actual value. Defaults to false.
  // 
  // Returns:
  // 
  //  (Anything) If key is passed, returns the value corresponding with that key
  //    in the location.hash 'state', or undefined. If not, an object
  //    representing the entire 'state' is returned.
  
  jq_bbq.getState = jq_bbq_getState = function( key, coerce ) {
    return key === undefined || typeof key === 'boolean'
      ? jq_deparam_fragment( key ) // 'key' really means 'coerce' here
      : jq_deparam_fragment( coerce )[ key ];
  };
  
  // Method: jQuery.bbq.removeState
  // 
  // Remove one or more keys from the current browser history 'state', creating
  // a new state, setting location.hash and triggering any bound
  // <hashchange event> callbacks (provided the new state is different than
  // the previous state).
  // 
  // If no arguments are passed, an empty state is created, which is just a
  // shortcut for jQuery.bbq.pushState( {}, 2 ).
  // 
  // Usage:
  // 
  // > jQuery.bbq.removeState( [ key [, key ... ] ] );
  // 
  // Arguments:
  // 
  //  key - (String) One or more key values to remove from the current state,
  //    passed as individual arguments.
  //  key - (Array) A single array argument that contains a list of key values
  //    to remove from the current state.
  // 
  // Returns:
  // 
  //  Nothing.
  // 
  // Additional Notes:
  // 
  //  * Setting an empty state may cause the browser to scroll.
  
  jq_bbq.removeState = function( arr ) {
    var state = {};
    
    // If one or more arguments is passed..
    if ( arr !== undefined ) {
      
      // Get the current state.
      state = jq_bbq_getState();
      
      // For each passed key, delete the corresponding property from the current
      // state.
      $.each( $.isArray( arr ) ? arr : arguments, function(i,v){
        delete state[ v ];
      });
    }
    
    // Set the state, completely overriding any existing state.
    jq_bbq_pushState( state, 2 );
  };
  
  // Event: hashchange event (BBQ)
  // 
  // Usage in jQuery 1.4 and newer:
  // 
  // In jQuery 1.4 and newer, the event object passed into any hashchange event
  // callback is augmented with a copy of the location.hash fragment at the time
  // the event was triggered as its event.fragment property. In addition, the
  // event.getState method operates on this property (instead of location.hash)
  // which allows this fragment-as-a-state to be referenced later, even after
  // window.location may have changed.
  // 
  // Note that event.fragment and event.getState are not defined according to
  // W3C (or any other) specification, but will still be available whether or
  // not the hashchange event exists natively in the browser, because of the
  // utility they provide.
  // 
  // The event.fragment property contains the output of <jQuery.param.fragment>
  // and the event.getState method is equivalent to the <jQuery.bbq.getState>
  // method.
  // 
  // > $(window).bind( 'hashchange', function( event ) {
  // >   var hash_str = event.fragment,
  // >     param_obj = event.getState(),
  // >     param_val = event.getState( 'param_name' ),
  // >     param_val_coerced = event.getState( 'param_name', true );
  // >   ...
  // > });
  // 
  // Usage in jQuery 1.3.2:
  // 
  // In jQuery 1.3.2, the event object cannot to be augmented as in jQuery 1.4+,
  // so the fragment state isn't bound to the event object and must instead be
  // parsed using the <jQuery.param.fragment> and <jQuery.bbq.getState> methods.
  // 
  // > $(window).bind( 'hashchange', function( event ) {
  // >   var hash_str = $.param.fragment(),
  // >     param_obj = $.bbq.getState(),
  // >     param_val = $.bbq.getState( 'param_name' ),
  // >     param_val_coerced = $.bbq.getState( 'param_name', true );
  // >   ...
  // > });
  // 
  // Additional Notes:
  // 
  // * Due to changes in the special events API, jQuery BBQ v1.2 or newer is
  //   required to enable the augmented event object in jQuery 1.4.2 and newer.
  // * See <jQuery hashchange event> for more detailed information.
  
  jq_event_special[ str_hashchange ] = $.extend( jq_event_special[ str_hashchange ], {
    
    // Augmenting the event object with the .fragment property and .getState
    // method requires jQuery 1.4 or newer. Note: with 1.3.2, everything will
    // work, but the event won't be augmented)
    add: function( handleObj ) {
      var old_handler;
      
      function new_handler(e) {
        // e.fragment is set to the value of location.hash (with any leading #
        // removed) at the time the event is triggered.
        var hash = e[ str_fragment ] = jq_param_fragment();
        
        // e.getState() works just like $.bbq.getState(), but uses the
        // e.fragment property stored on the event object.
        e.getState = function( key, coerce ) {
          return key === undefined || typeof key === 'boolean'
            ? jq_deparam( hash, key ) // 'key' really means 'coerce' here
            : jq_deparam( hash, coerce )[ key ];
        };
        
        old_handler.apply( this, arguments );
      };
      
      // This may seem a little complicated, but it normalizes the special event
      // .add method between jQuery 1.4/1.4.1 and 1.4.2+
      if ( $.isFunction( handleObj ) ) {
        // 1.4, 1.4.1
        old_handler = handleObj;
        return new_handler;
      } else {
        // 1.4.2+
        old_handler = handleObj.handler;
        handleObj.handler = new_handler;
      }
    }
    
  });
  
})(jQuery,this);

/*!
 * jQuery hashchange event - v1.2 - 2/11/2010
 * http://benalman.com/projects/jquery-hashchange-plugin/
 * 
 * Copyright (c) 2010 "Cowboy" Ben Alman
 * Dual licensed under the MIT and GPL licenses.
 * http://benalman.com/about/license/
 */

// Script: jQuery hashchange event
//
// *Version: 1.2, Last updated: 2/11/2010*
// 
// Project Home - http://benalman.com/projects/jquery-hashchange-plugin/
// GitHub       - http://github.com/cowboy/jquery-hashchange/
// Source       - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.js
// (Minified)   - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.min.js (1.1kb)
// 
// About: License
// 
// Copyright (c) 2010 "Cowboy" Ben Alman,
// Dual licensed under the MIT and GPL licenses.
// http://benalman.com/about/license/
// 
// About: Examples
// 
// This working example, complete with fully commented code, illustrate one way
// in which this plugin can be used.
// 
// hashchange event - http://benalman.com/code/projects/jquery-hashchange/examples/hashchange/
// 
// About: Support and Testing
// 
// Information about what version or versions of jQuery this plugin has been
// tested with, what browsers it has been tested in, and where the unit tests
// reside (so you can test it yourself).
// 
// jQuery Versions - 1.3.2, 1.4.1, 1.4.2
// Browsers Tested - Internet Explorer 6-8, Firefox 2-3.7, Safari 3-4, Chrome, Opera 9.6-10.1.
// Unit Tests      - http://benalman.com/code/projects/jquery-hashchange/unit/
// 
// About: Known issues
// 
// While this jQuery hashchange event implementation is quite stable and robust,
// there are a few unfortunate browser bugs surrounding expected hashchange
// event-based behaviors, independent of any JavaScript window.onhashchange
// abstraction. See the following examples for more information:
// 
// Chrome: Back Button - http://benalman.com/code/projects/jquery-hashchange/examples/bug-chrome-back-button/
// Firefox: Remote XMLHttpRequest - http://benalman.com/code/projects/jquery-hashchange/examples/bug-firefox-remote-xhr/
// WebKit: Back Button in an Iframe - http://benalman.com/code/projects/jquery-hashchange/examples/bug-webkit-hash-iframe/
// Safari: Back Button from a different domain - http://benalman.com/code/projects/jquery-hashchange/examples/bug-safari-back-from-diff-domain/
// 
// About: Release History
// 
// 1.2   - (2/11/2010) Fixed a bug where coming back to a page using this plugin
//         from a page on another domain would cause an error in Safari 4. Also,
//         IE6/7 Iframe is now inserted after the body (this actually works),
//         which prevents the page from scrolling when the event is first bound.
//         Event can also now be bound before DOM ready, but it won't be usable
//         before then in IE6/7.
// 1.1   - (1/21/2010) Incorporated document.documentMode test to fix IE8 bug
//         where browser version is incorrectly reported as 8.0, despite
//         inclusion of the X-UA-Compatible IE=EmulateIE7 meta tag.
// 1.0   - (1/9/2010) Initial Release. Broke out the jQuery BBQ event.special
//         window.onhashchange functionality into a separate plugin for users
//         who want just the basic event & back button support, without all the
//         extra awesomeness that BBQ provides. This plugin will be included as
//         part of jQuery BBQ, but also be available separately.

(function($,window,undefined){
  '$:nomunge'; // Used by YUI compressor.
  
  // Method / object references.
  var fake_onhashchange,
    jq_event_special = $.event.special,
    
    // Reused strings.
    str_location = 'location',
    str_hashchange = 'hashchange',
    str_href = 'href',
    
    // IE6/7 specifically need some special love when it comes to back-button
    // support, so let's do a little browser sniffing..
    browser = $.browser,
    mode = document.documentMode,
    is_old_ie = browser.msie && ( mode === undefined || mode < 8 ),
    
    // Does the browser support window.onhashchange? Test for IE version, since
    // IE8 incorrectly reports this when in "IE7" or "IE8 Compatibility View"!
    supports_onhashchange = 'on' + str_hashchange in window && !is_old_ie;
  
  // Get location.hash (or what you'd expect location.hash to be) sans any
  // leading #. Thanks for making this necessary, Firefox!
  function get_fragment( url ) {
    url = url || window[ str_location ][ str_href ];
    return url.replace( /^[^#]*#?(.*)$/, '$1' );
  };
  
  // Property: jQuery.hashchangeDelay
  // 
  // The numeric interval (in milliseconds) at which the <hashchange event>
  // polling loop executes. Defaults to 100.
  
  $[ str_hashchange + 'Delay' ] = 100;
  
  // Event: hashchange event
  // 
  // Fired when location.hash changes. In browsers that support it, the native
  // window.onhashchange event is used (IE8, FF3.6), otherwise a polling loop is
  // initialized, running every <jQuery.hashchangeDelay> milliseconds to see if
  // the hash has changed. In IE 6 and 7, a hidden Iframe is created to allow
  // the back button and hash-based history to work.
  // 
  // Usage:
  // 
  // > $(window).bind( 'hashchange', function(e) {
  // >   var hash = location.hash;
  // >   ...
  // > });
  // 
  // Additional Notes:
  // 
  // * The polling loop and Iframe are not created until at least one callback
  //   is actually bound to 'hashchange'.
  // * If you need the bound callback(s) to execute immediately, in cases where
  //   the page 'state' exists on page load (via bookmark or page refresh, for
  //   example) use $(window).trigger( 'hashchange' );
  // * The event can be bound before DOM ready, but since it won't be usable
  //   before then in IE6/7 (due to the necessary Iframe), recommended usage is
  //   to bind it inside a $(document).ready() callback.
  
  jq_event_special[ str_hashchange ] = $.extend( jq_event_special[ str_hashchange ], {
    
    // Called only when the first 'hashchange' event is bound to window.
    setup: function() {
      // If window.onhashchange is supported natively, there's nothing to do..
      if ( supports_onhashchange ) { return false; }
      
      // Otherwise, we need to create our own. And we don't want to call this
      // until the user binds to the event, just in case they never do, since it
      // will create a polling loop and possibly even a hidden Iframe.
      $( fake_onhashchange.start );
    },
    
    // Called only when the last 'hashchange' event is unbound from window.
    teardown: function() {
      // If window.onhashchange is supported natively, there's nothing to do..
      if ( supports_onhashchange ) { return false; }
      
      // Otherwise, we need to stop ours (if possible).
      $( fake_onhashchange.stop );
    }
    
  });
  
  // fake_onhashchange does all the work of triggering the window.onhashchange
  // event for browsers that don't natively support it, including creating a
  // polling loop to watch for hash changes and in IE 6/7 creating a hidden
  // Iframe to enable back and forward.
  fake_onhashchange = (function(){
    var self = {},
      timeout_id,
      iframe,
      set_history,
      get_history;
    
    // Initialize. In IE 6/7, creates a hidden Iframe for history handling.
    function init(){
      // Most browsers don't need special methods here..
      set_history = get_history = function(val){ return val; };
      
      // But IE6/7 do!
      if ( is_old_ie ) {
        
        // Create hidden Iframe after the end of the body to prevent initial
        // page load from scrolling unnecessarily.
        iframe = $('<iframe src="javascript:0"/>').hide().insertAfter( 'body' )[0].contentWindow;
        
        // Get history by looking at the hidden Iframe's location.hash.
        get_history = function() {
          return get_fragment( iframe.document[ str_location ][ str_href ] );
        };
        
        // Set a new history item by opening and then closing the Iframe
        // document, *then* setting its location.hash.
        set_history = function( hash, history_hash ) {
          if ( hash !== history_hash ) {
            var doc = iframe.document;
            doc.open().close();
            doc[ str_location ].hash = '#' + hash;
          }
        };
        
        // Set initial history.
        set_history( get_fragment() );
      }
    };
    
    // Start the polling loop.
    self.start = function() {
      // Polling loop is already running!
      if ( timeout_id ) { return; }
      
      // Remember the initial hash so it doesn't get triggered immediately.
      var last_hash = get_fragment();
      
      // Initialize if not yet initialized.
      set_history || init();
      
      // This polling loop checks every $.hashchangeDelay milliseconds to see if
      // location.hash has changed, and triggers the 'hashchange' event on
      // window when necessary.
      (function loopy(){
        var hash = get_fragment(),
          history_hash = get_history( last_hash );
        
        if ( hash !== last_hash ) {
          set_history( last_hash = hash, history_hash );
          
          $(window).trigger( str_hashchange );
          
        } else if ( history_hash !== last_hash ) {
          window[ str_location ][ str_href ] = window[ str_location ][ str_href ].replace( /#.*/, '' ) + '#' + history_hash;
        }
        
        timeout_id = setTimeout( loopy, $[ str_hashchange + 'Delay' ] );
      })();
    };
    
    // Stop the polling loop, but only if an IE6/7 Iframe wasn't created. In
    // that case, even if there are no longer any bound event handlers, the
    // polling loop is still necessary for back/next to work at all!
    self.stop = function() {
      if ( !iframe ) {
        timeout_id && clearTimeout( timeout_id );
        timeout_id = 0;
      }
    };
    
    return self;
  })();
  
})(jQuery,this);

var TZ = {
	module : {},
	setCurrentMenuTab: function(el) {},	
	PRIVATE : 0,
	PUBLIC : 1,
	userNamePage : '', //without @
	accountIdPage : '', //with @
	//Deprecated: according to jc
	bindDefaultText: function( el, text) {
		el.data("exampleText",text);
		el.val( text);
		el.focus( function() {
			if (el.val() == text) el.val('');
			el.addClass('onfield');
		});
		el.blur( function() {
			if (el.val() == '') {
				el.val( text);
				el.removeClass('onfield');
			}
		});
	},
	getRealVal: function( el) {
		if (el.val() != el.data("exampleText")) return el.val();
		else return '';
	},
	
	random: function( count, offset) {
		if (!offset) offset = 0;
		return Math.floor(Math.random()*count)+offset;
	},
	
	reposition : function(id,appendElement) {
			$(appendElement).append($(id));
	},
	executeFunctionByName: function(functionName, context /*, args */) {
		var args=[arguments[2]];
		var namespaces = functionName.split(".");
		var func = namespaces.pop();
		var finalContext = context
		for(var i = 0; i < namespaces.length; i++) {
			finalContext = finalContext[namespaces[i]];	
		}
		return finalContext[func].apply(this, args);
	},
	getIdFromUrl: function() {
		return window.location.pathname.substring(window.location.pathname.lastIndexOf('/') + 1, window.location.pathname.indexOf('.'));
	}

};


TZ.request = {section:"js",fullPath:"/js",pageId:"shared.js",properties:{},rootPath:"/var/www/html",protocol:"http",baseUrl:"http://starstyler.gala.de",host:"starstyler.gala.de",relativePath:"/js",basePath:"",application:{dir:"",name:"taaz2",version:"V20120222020842",modifier:""},relativePathName:"/js/shared.js",fileName:"shared.js",url:"http://starstyler.gala.de/js/shared.js",preventNil:{}}; 
/* created by timm */
TZ.events = {
	callIdCount:0,
	eventListeners:{},
	addEventListener:function(event_string,callbackF){
		TZ.events.callIdCount++;
		var callOb = {event:event_string,callback:callbackF,id:TZ.events.callIdCount};
		if(!TZ.events.eventListeners[event_string]){
			TZ.events.eventListeners[event_string] = {};
		}
		(TZ.events.eventListeners[event_string])[callOb.id] = callOb;
		return callOb.id;
	},
	removeEventListener:function(event_string,id){
		if(!TZ.events.eventListeners[event_string]) return false ;
		delete (TZ.events.eventListeners[event_string])[id] ;
		return true;
	},
	dispatchEvent:function(event_string,object){
		if(!TZ.events.eventListeners[event_string]) return;
		setTimeout(function() {
			for (var id in TZ.events.eventListeners[event_string]){
				try{
					TZ.events.eventListeners[event_string][id].callback(object);	
				} catch(e){
					Debug.log(e);
				}
			}
		},1);
	}
};
/**
 * User
 */

TZ.user = {
		//validUserNamePattern: /^[a-zA-Z._-][a-zA-Z0-9._-]+$/,
		//validEmailPattern: /^[-@]+\@[a-zA-Z0-9.-_]+\.[a-zA-Z]+$/,
		
		/**
		 * @param params key to look up, or hash of the form: {key: 'findKey'}
		 */
		getUserState: function (keyOrParams, onSuccess) {
			Debug.assert(onSuccess);
			var key = (typeof keyOrParams === "string") ? keyOrParams : keyOrParams['key'];
			var userState = TZ.localStorage.get('userInfoState') || {};
			var val = userState[key];
			
			Debug.info('getUserState returning: ', 'brentf')
			Debug.log(val, 'brentf');
			
			onSuccess(val);
		},
		
		setUserState: function (paramsOrKey, onSuccessOrValue) {
			var params = (paramsOrKey && paramsOrKey.key) ? paramsOrKey : { key:paramsOrKey, value:onSuccessOrValue };
			var userState = TZ.localStorage.get('userInfoState') || {};
			userState[params.key] = params.value;
			TZ.localStorage.set('userInfoState', userState);
			
			(typeof onSuccessOrValue == 'function') && onSuccessOrValue(userState);
			Debug.info('setUserState('+paramsOrKey+', '+onSuccessOrValue + '): ' + ' --> ' + JSON.stringify(userState), 'brentf');
		},
		
		/**
		 * Ensure TZ.rpc has valid user token, etc, calling RefreshAccountToken once per page load
		 * in addition to Login/Logout.
		 */
		updateUser: function () {
			var self = TZ.user;
			var accountData = TZ.localStorage.get("user");
			
			function updateToken (accountData) {
				TZ.rpc.loggedIn = (accountData != null)
				var isLoggedOut = !TZ.rpc.loggedIn; 
				if (isLoggedOut) {	// no user logged in
					accountData = { accountId:'', accountToken:'', username:'' };
					TZ.localStorage.remove("user");
					Debug.note('User is Logged Out', 'brentf');
				}
				
				delete accountData.longExp;	// don't propagate to TZ.rpc
				$.extend(TZ.rpc, accountData); // accountId, accountToken, username
				TZ.rpc.visitorId = TZ.localStorage.get('visitorId');
				TZ.events.dispatchEvent("loginStatusChange", accountData.username);
				
				Debug.note('updateUser(' + JSON.stringify(accountData)+ ')', 'brentf', 'orange')
				if (!isLoggedOut)
					TZ.localStorage.set("user", TZ.rpc.getUserInfo());
			}
			
			if (!accountData)
				updateToken(null);
			else if (!self._refreshedToken) {
				Debug.note('Caling RefreshAccountToken!', 'brentf');
				$.extend(TZ.rpc, accountData); // accountId, accountToken, username
				
				var oneDayInSecs = 86400;
				var lifespanSecs = accountData.longExp ? oneDayInSecs : (30 * oneDayInSecs);
				//if (!Debug.isLive()) lifespanSecs = 30;	// for QA
				
				TZ.rpc.invoke("RefreshAccountToken", { lifespan:lifespanSecs }, function (newToken) {
						Debug.info("RefreshAccountToken succeeded: new token = " + newToken, 'brentf');
						if(jQuery.isEmptyObject(newToken)){
							updateToken(null);
						} else{
							updateToken({ accountToken:newToken });
						}
						
						self._refreshedToken = true;
					},
					function () { updateToken(null); }
				);
			}
			else 
				updateToken(accountData);
		},
		
		/**
		 * Ensure that self.visitorId is set.
		 * 1. Load from localStorage, if 'visitorId' key is there.
		 * 2. otherwise, request one asynchronously from the server, then send 'visitorIdChange' event when complete.
		 */
		_waitingForVisitorID: false,
		renewVisitorId: function (onSuccess) {
			var self = TZ.user;
			if (self._waitingForVisitorID)
				return ''; // we are already requesting an ID
			
			var id = TZ.localStorage.get("visitorId");
			if (id) {
				TZ.rpc.visitorId = id;
				onSuccess && onSuccess(id);
			}
			else {
				self._waitingForVisitorID = true; // otherwise set a check flag so this doesn't get called multiple times and send an RPC request.
				
				TZ.rpc.invoke("GetMyVisitorToken", {}, function (visitorToken) {
					if (visitorToken) {	// why does this need to be checked? If the server calls the onSuccess callback, isn't 'visitorToken' guaranteed to be valid?
						TZ.localStorage.set('visitorId', visitorToken);
						TZ.rpc.visitorId = visitorToken;	
						self._waitingForVisitorID = false;
						
						TZ.events.dispatchEvent("visitorIdChange", visitorToken);
						onSuccess && onSuccess(visitorToken);
					}
				});
			}
		},
		
		/**
		 * Called by both login and register to refresh the "user" state.
		 */
		loginSuccess: function (result, rememberMe, onSuccess, onError) {
			var self = TZ.user;
			
			Debug.note('user.loginSuccess called with: ', 'brentf');
			Debug.log(result, 'brentf');
			
			if (result && result.accountToken) {
				var userAccount = {
					accountId:'@'+result.accountData.name,
					accountToken:result.accountToken,
					username:result.accountData.name,
					longExp:rememberMe,
					creationTS:result.accountData.creationTS.utime
				};
		
				TZ.localStorage.set("user", userAccount);
				self._refreshedToken = true;
				self.updateUser();
				
				onSuccess && onSuccess();
			}
			else {
				onError && onError("Invalid username/password");
				TZ.localStorage.remove("user");
				self.updateUser();
			}
		},
		
		logout: function (onSuccess) {
			TZ.rpc.invoke("Logoff", {}, afterServerLogoff, afterServerLogoff);
			
			function afterServerLogoff (response) {
				TZ.localStorage.remove("user");
				TZ.rpc.clearUserInfo();
				TZ.user.updateUser();
				onSuccess && onSuccess();
			}
		}
};

/* 
 * JavaScript-only RPC proxy with asynchronous handler support.
 * 
 * @class rpc
 * @author Brent - New JavaScript-only RPC proxy (not dependent upon Flash)
 */
"use strict";

TZ.localStorage = {
	/**
	 * @return value stored in browser's localStorage, or 'defaultValue' if non-existent
	 * 			(if no defaultValue is specified or is null, then null is returned if requested key is not found)
	 */
    get: function (key, defaultValue) {
        var value = localStorage.getItem(key);
        Debug.note('get('+key+') = ' + value, 'brentf', '0x5588ff');
        if (value == null) {
        	if (defaultValue != undefined)
        		value = defaultValue;
        }
        else if (value[0] == "{")	// interpret as an encoded json string
            value = JSON.parse(value);

        return value;
    },

    set: function (key, value) {
        if (typeof value == "object")	// must convert to a string
            value = JSON.stringify(value);
        localStorage.setItem(key, value);
        Debug.note('set('+key+') = ' + value, 'brentf', '0xff6677');
    },
    
    remove: function (key) {
		localStorage.removeItem(key);
        Debug.note('removed('+key+')', 'brentf', '0xff6777');
    }
};

TZ.rpc = {
	CACHE_LOCAL_TIMEOFFSET_DURATION_MS: 1 * 3600 * 1000,	// cache server time for 1 hour
    _localTimeOffsetCacheExpires: 0,
    _localTimeOffsetSecs: 0,
	
	//
	// Account Fields - set by refreshLoginStatus()
	//
	// loggedIn, username, accountId, visitorId, accountToken
	//
	visitorId: null,
	loggedIn: false,
	
    clearUserInfo: function () {
    	var self = TZ.rpc;
		self.username = '',
		self.accountToken = "0",
		self.accountId = null
    },
    getUserInfo: function () {
    	var self = TZ.rpc,
    	    acctInfo = {
	    		accountId:self.accountId,
				accountToken:self.accountToken,
				username:self.username,
				visitorId:self.visitorId
			};
		Debug.note('getUserInfo returning:', 'brentf');
		Debug.log(acctInfo, 'brentf');
		
		return acctInfo;
    },
	
	ready:true,		// needed?  Now always ready -- used to be set once Flash RPC widget was loaded
	
	/**
	 * Call after TZ is loaded, and once before RPC.invoke
	 */
	setup: function () {
		TZ.rpc.clearUserInfo();
		TZ.user.renewVisitorId(function (visitorId) {
			TZ.events.dispatchEvent("RPCReady");
			TZ.user.updateUser();
		});
	},
	
	// Refresh login state fields (required after login/logout/registration RPCs
	refreshLoginStatus: function (onSuccess) {
		TZ.rpc.invoke("getLoginStatus", {}, function (userData) {
			Debug.info('getLoginStatus:', 'brentf');
			Debug.log(userData, 'brentf');
			
			$.extend(TZ.rpc, userData); // accountId, accountToken, loggedIn, username, visitorId
			TZ.events.dispatchEvent("loginStatusChange", userData.username);
			
			onSuccess && onSuccess();
		});
	},
	

	/**
	 * Call the given RPC by name 
	 */
	invoke: function (rpcName, params, onSuccess, onError) {
		var self = TZ.rpc;
		    
		//Adding Schema filter before RPC Call - only dev sites.
		var isDev = Debug.isDev();	// do not apply schema on QA or LIVE
		var isValid = true;
		if (params && isDev) {
			var schemaName = params['schemaName'];
			isValid = !TZ.schema || !TZ.schema.taazSchema || TZ.schema.validate(schemaName, params.properties);	//apply schema validation on dev
		}
		
		// 
		// Only run RPC on dev if passes schema validation
		//
		if (!isDev || isValid || !params) {
			//
			// Special Handling for CreateTopicEntry - why is this required?
			//
			if (rpcName === "createTopicEntry")
				params.properties = JSON.stringify(params.properties);
			
			//
			// Local function support (check locally, first)
			//
			switch (rpcName) {
				case "getLoginStatus":
					self.loggedIn || TZ.localStorage.remove('userInfoState');
					onSuccess && onSuccess({ loggedIn:self.loggedIn, accountToken:self.accountToken, username:self.userName,accountId:self.accountId, visitorId:self.visitorId });	
					break;
					
				case "forgottenPassword":
					//UserLoggedIn.getInstance().forgottenPassword(argsObject.email,argsObject.captchaResponse,onSuccessF,onErrorF);
					self.invoke("ForgottenPassword", {
							"email": params.email,
							"captchaChallenge": params.challenge,
							"captchaResponse" : params.captchaResponse
						},
						function (data) {
							onSuccess && onSuccess("A link to reset your password has been e-mailed to you.");
						},
						function (e) {
							onError && onError("Enter a valid email.");
						}
					);
					break;
					
				case "loginUser":
					Debug.log('loginUser called with: ' + JSON.stringify(params), 'brentf');
					
					params.lifespan = (params.rememberMe) ? 5*864000 : 86400;
					self.invoke("Logon", params, function (result) {
							// 
							// Because of inconsistent RPC return values (no 'error' field), AND because 
							// the success callback is called on failure, 
							// must check for the existence of 'accountToken' for success
							// BUT, because an empty object is returned on failure, must ensure that it's a string (and not empty for legacy reasons?)
							// 
							if (result && typeof result.accountToken == 'string' && result.accountToken.length > 0) {
								TZ.user.loginSuccess(result, params.rememberMe, onSuccess, onError);
								Debug.info('Logged in:'); Debug.log(self.getUserInfo(), 'brentf');
							}
							else 
								onError && onError("Invalid username/password");
						},
						onError
					);
					break;
					
				case "logoutUser":
					TZ.user.logout(onSuccess);
					break;
					
				case "register":
					self.register(params, onSuccess, onError);
					break;
					
				// 
				// Remote RPC
				// 
				default:
					var url = TZ.request.protocol + '://' + TZ.request.host + '/service2/';
					var postData = {
							service: rpcName,
							arguments: (params && JSON.stringify(params)) || '',
							pmApplicationInfo: pmApplicationInfo,	// global defined in shared_js.erb which reads pmApplicationInfo.js
							pmApplicationDeploymentInfo: pmApplicationDeploymentInfo,	// (required?) global defined in shared_js.erb which reads pmApplicationInfo.js
							accountToken: self.accountToken,
							accountId: self.accountId,
							visitorToken: self.visitorId,
							encoding: 'JSON'
					};
					
					Debug.log('Calling ' + rpcName + ' RCP at ' + url, 'brentf');
					//Debug.log(postData);
					 
					$.ajax({
						type: 'POST',
						url:  url,
						data: postData,
						dataType: 'json',
						success:  function(data) {
							Debug.log('RPC Succeeded: ' + rpcName + ' Result:', 'brentf');
							data && Debug.log(data, 'brentf');
							
							if (data && data.error)
								onError && onError(data);
							else
								onSuccess && onSuccess(data);
						},
						error: function(xhr, status, err) {
							Debug.log('RPC Failed' + rpcName + ' Status: "' + status + '" : ' + xhr.responseText);
							Debug.log('Error response below :');
							Debug.log(xhr);
							onError && onError(status)
						}
					});
					
			}

		}
	},
	
	/**
	 * Deprecated alias for createRPC
	 */
	createRPC: function(rpcName, params, onSuccess, onError) {
		TZ.rpc.invoke(rpcName, params, onSuccess, onError);
	},
	
	
	/**
	 * 
	 */
	register: function (params, onSuccess, onError) {
		var self = TZ.rpc;
		params.lifespan = 864000;
		self.invoke("Register", params, function (acctInfo) {
				TZ.localStorage.remove('visitorId');	
				TZ.user.renewVisitorId();		// get a new one
				
				if (params.subscribe)
					self.invoke("SubscribeToNewsletter");
				
				TZ.user.loginSuccess(acctInfo, true, onSuccess, onError);
			},
			onError
		);
	},
	
	serverTimeOffset: function (onSuccess, onError) {
		var self = TZ.rpc;
		
		function serverTimeOffsetSuccessCallback () {
			onSuccess(self._localTimeOffsetSecs);
		}
		
		function onGotServerTime (serverRpcTime) {
			serverRpcTime = serverRpcTime || {};
			//Debug.trace('GetServerTime Succeeded ' +JSON.stringify(serverRpcTime));
			var serverUtcTime = serverRpcTime.utime;
			var nowTS = new Date().getTime();
			var localUtcTime = nowTS / 1000;
			self._localTimeOffsetSecs  =  localUtcTime - serverUtcTime;
			
			//Debug.info('self._localTimeOffsetCacheExpires ' + self._localTimeOffsetCacheExpires + ' --> ' + (nowTS + self.CACHE_LOCAL_TIMEOFFSET_DURATION_MS));
			self._localTimeOffsetCacheExpires = nowTS + self.CACHE_LOCAL_TIMEOFFSET_DURATION_MS;
			
			serverTimeOffsetSuccessCallback();
		}
		
		if (new Date().getTime() > self._localTimeOffsetCacheExpires)	// FIX: add extra time support from RpcWidget
			self.invoke("GetServerTime", {}, onGotServerTime, onError);
		else
			serverTimeOffsetSuccessCallback();	// just return the cached time correction
	},

	// 
	//
	// These functions are being moved out of this file by Jarrod
	//
	//
	
	createPrivateMessage: function(recipientId,threadId,parentId,type,properties,success,error) {
		switch (arguments.length) {
			case 7:
				return TZ.rpc.invoke(
					'CreatePrivateMessage',
					{
						recipientAccountId : recipientId,
						threadId           : threadId,
						parentId           : parentId,
						type               : type,
						properties         : properties
					},
					success,
					error
				);
			//(accountId,threadId,parentId,properties,success,error)
			case 6: return TZ.rpc.createPrivateMessage(arguments[0],arguments[1],arguments[2],'private-message',arguments[3],arguments[4],arguments[5]);
			//(accountId,threadId,properties,success,error)
			case 5: return TZ.rpc.createPrivateMessage(arguments[0],arguments[1],'','private-message',arguments[2],arguments[3],arguments[4]);
			//(accountId,properties,success,error)
			case 4: return TZ.rpc.createPrivateMessage(arguments[0],'','','private-message',arguments[1],arguments[2],arguments[3]);
			default: throw TZ.rpc.ERROR_ARGUMENTS
		}
	},
	getMyMessageThreads: function(type,offset,maxEntries,success,error) {
		switch (arguments.length) {
			case 5: 
				return TZ.rpc.invoke(
						'GetMyMessageThreads', {
							type: type, 
							offset: offset, 
							maxEntries: maxEntries 
						},
						success,
						error
					);
			//(offset,maxEntries,success,error)
			case 4: return TZ.rpc.getMyMessageThreads('private-message',arguments[0],arguments[1],arguments[2],arguments[3]);
			// (maxEntries,success,error)
			case 3: return TZ.rpc.getMyMessageThreads('private-message',0,arguments[0],arguments[1],arguments[2]);
			// (success,error)
			case 2: return TZ.rpc.getMyMessageThreads('private-message',0,10,arguments[0],arguments[1]);
			// (success)
			case 1: return TZ.rpc.getMyMessageThreads('private-message',0,10,arguments[0],function(){});
			default: throw TZ.rpc.ERROR_ARGUMENTS
		}
	},
	getMyMessages: function(query,queryArguments,offset,maxEntries,success,error) {
		switch (arguments.length) {
			case 6: 
				if (!TZ.rpc.loggedIn){
					return error(["Not Logged in"]);
				}
				return TZ.rpc.invoke(
						'GetMyMessages', {
							query : query, 
							queryArguments : offset, 
							offset : offset,
							maxEntries : maxEntries
						},
						success,
						error
					);
			//(query,offset,maxEntries,success,error)
			case 5: return TZ.rpc.getMyMessages(arguments[0],'',arguments[1],arguments[2],arguments[3],arguments[3]);
			//(query,maxEntries,success,error)
			case 4: return TZ.rpc.getMyMessages(arguments[0],'',0,arguments[1],arguments[2],arguments[3]);
			// (query,success,error)
			case 3: return TZ.rpc.getMyMessages(arguments[0],'',0,10,arguments[1],arguments[2]);
			// (query,success)
			case 2: return TZ.rpc.getMyMessages(arguments[0],'',0,10,arguments[1],function(){});
			default: throw TZ.rpc.ERROR_ARGUMENTS
		}
	},
	getMyMessageThread: function(threadId,success,error) {
		return TZ.rpc.invoke('GetMyMessageThread',{threadId: threadId},success,error || function(){});
	},
	updatePrivateMessageMeta: function(messageId,metadata,success,error) {
		return TZ.rpc.invoke(
				'UpdatePrivateMessageMeta',
				{ messageId:messageId , metadata:metadata },
				success,
				error || function() {}
		);
	},
	updatePrivateMessageThreadMeta: function(threadId,metadata,success,error) {
		return TZ.rpc.invoke(
				'UpdatePrivateMessageThreadMeta',
				{ threadId:threadId , metadata:metadata },
				success,
				error || function() {}
		);
	},
	
	getMyFollows: function(returnHandles,success,error) {
		if (arguments.length < 3) {
			error = arguments[1] || function() {}
			success = arguments[0];
			returnHandles = true;
		}
		return TZ.rpc.invoke('GetMyFollows', {returnHandles: !!returnHandles}, success,error);
	},
	getMyFollowers: function(returnHandles,success,error) {
		if (arguments.length < 3) {
			error = arguments[1] || function() {}
			success = arguments[0];
			returnHandles = true;
		}
		return TZ.rpc.invoke('GetMyFollowers', {returnHandles: !!returnHandles}, success,error);
	},
	
	createTopicEntry: function(topicId,parentId,type,properties,success,error) {
		switch (arguments.length) {
			case 6: 
				return TZ.rpc.invoke(
						'CreateTopicEntry', {
							topicId    : topicId,
							parentId   : parentId,
							type       : type,
							properties : properties
						},
						success,
						error
					);
			// (topicId,type,properties,success,error)
			case 5: return TZ.rpc.createTopicEntry(arguments[0],'',arguments[1],arguments[2],arguments[3],arguments[4] || function(){})
			// (topicId,type,properties,success)
			case 4: return TZ.rpc.createTopicEntry(arguments[0],'',arguments[1],arguments[2],arguments[3],function(){})
			default: throw TZ.rpc.ERROR_ARGUMENTS
		}
	},
	ERROR_ARGUMENTS: new Error('Invalid number of arguments')	// not required? only used by specific function calls
};

/* Tim, renamed 'Box' to 'box' to match Flash call. Moved from ImageUploadPopup.erb, since it was not getting executed reliably */
	TZ.rpc.setupCropbox = function(){
		if(!TZ.rpc.getUserInfo) return; // we do this if no new rpc code in place.
		document.getElementById("crop_flash_div").onUserChange(TZ.rpc.getUserInfo());
		document.getElementById("crop_flash_div").onVisitorIdChange(TZ.rpc.getUserInfo().visitorId);
		
		TZ.events.removeEventListener('loginStatusChange',TZ.rpc.cropBoxEventId);
		TZ.events.removeEventListener('visitorIdChange',TZ.rpc.cropBoxVEventId);
		
		TZ.rpc.cropBoxVEventId = TZ.events.addEventListener('visitorIdChange',function(){
			if(!document.getElementById("crop_flash_div")) 
				TZ.events.removeEventListener('visitorIdChange',TZ.rpc.cropBoxVEventId);
			else document.getElementById("crop_flash_div").onVisitorIdChange(TZ.rpc.getUserInfo().visitorId);
		});
		
		TZ.rpc.cropBoxEventId = TZ.events.addEventListener('loginStatusChange',function(username){
			if(!document.getElementById("crop_flash_div")) {
				TZ.events.removeEventListener('loginStatusChange',TZ.rpc.cropBoxEventId);
				return;
			}
			document.getElementById("crop_flash_div").onUserChange(TZ.rpc.getUserInfo());
		});
	};	
TZ.filters = {
	getRawString:function(encodedString){
		var decoded = $("<div/>").html(encodedString).text();
		return decoded;
	},
	getLegacyString:function(encodedString){
		if(!encodedString) return undefined;
		var d = encodedString.replace(/\&\#xd\;/g,"") //remove \r chars
		d = d.replace(/\&\#xa\;/g,'<br/>') // replace \n chars with <br\>
		d = d.replace(/\&apos\;/g,'&#39;') // replace apos with the hex encoding for IE8 hack
		d = d.replace(/\&lt\;([iI])\&gt\;(.*?)&lt;\/([iI])&gt;/g,'<$1>$2</$3>') //replace <i> tags or ignore if not closed
		d = d.replace(/\&lt\;([bB])\&gt\;(.*?)&lt;\/([bB])&gt;/g,'<$1>$2</$3>') //replace <b> tags or ignore if not closed
		d = d.replace(/\&lt\;[bB][rR]\/\&gt\;/,'<br/>') //replace <br/> tags with literals 
		return d;
	},
	getURIString:function(encodedString){
		// if you need something more robust talk to tim
		var decoded = TZ.filters.getRawString(encodedString);
		decoded = unescape(decoded);
		return encodeURI(decoded);
	}
};
/*
 * Usage:
 *	DynamicContentLoader.createPopup("loginPopupWindow", "login_popup.html", true, 623, 225)
 * 	DynamicContentLoader.load('myDiv', "myContent.html")
 *	DynamicContentLoader.createDiv("myDiv","myContent.html")
 */
 // workaround IE8 to disable cache for now.  Will test real solution with Chandra --timm
//$.ajaxSetup({cache:false});
var DynamicContentLoader = {
    addMask: function(){
        $('body').append('<div id="modalPopupMask"></div>');
        var maskHeight = $(document).height();
        
        $('#modalPopupMask').css({
            'width': '100%',
            'height': maskHeight
        });
        $('#modalPopupMask').fadeTo("slow", 0.3);
    },
	
    removeMask: function(){
        $('#modalPopupMask').fadeOut(250, function(){
            $('#modalPopupMask').remove();
        });
    },
    
    /*
     * Load all dynamic content into element 'id'.
     * Note that non-dynamic content (not enclosed in the dynamic blocks) is ignored.
     *
     * The tags searched for here are arbitrary, but must match those inserted by the dynamic.X templates.
     */
    load: function(id, contentURL, type, callback){
        
		var params = {
            'id': id
        }; /* URL params (retrieved on the ERB side by $request) */
        
        $.get(contentURL, params, function(data) {
            /* CSS*/
            var cssBlocks = data.split(/<\/?CSS_BLOCK>/m);
            var combinedCss = "";
            for (i = 1; i < cssBlocks.length; i += 2) 
                combinedCss = combinedCss + cssBlocks[i];
            $("#" + id + "_css").remove();
            $("<style type='text/css' id='" + id + "_css'>\n" + combinedCss + "\n</style>\n").appendTo("head");
            
            /* Insert HTML*/
            var htmlBlocks = data.split(/<\/?HTML_BLOCK>/m);
            var combinedHtml = "";
            for (i = 1; i < htmlBlocks.length; i += 2){
				combinedHtml = combinedHtml + htmlBlocks[i];	
			} 
			 
			if(type == 'append'){
				$('#' + id).append(combinedHtml);
			}else if(type=='prepend'){
				$('#' + id).prepend(combinedHtml);
			}else{
				$('#' + id).html(combinedHtml);  	
			}
			
            /* Evaluate Embedded Javascript*/
            var jsBlocks = data.split(/<\/?JAVASCRIPT_BLOCK>/m);
            for (i = 1; i < jsBlocks.length; i += 2) 
                $.globalEval(jsBlocks[i]);
			
			//
			// Re-do initialization that was done for all components on the main page
			//
			TZ.social.parse();			// required to translate XFBML in dynamic content (already run on the main page)
			$('[tooltip]').tooltip();	// set up any Tool Tips (for elements with a tooltip= attribute)
            
            //call any callback provided 
            if(callback!=null) callback();
        });
    },
    
    createDiv: function(id, contentURL, callback){
        $('<div id="' + id + '"></div>').appendTo("body");
        DynamicContentLoader.load(id, contentURL, 'replace', callback);
    },
    
	appendToDiv: function(id, contentURL, callback){
        DynamicContentLoader.load(id, contentURL, 'append', callback);
    },
	
	prependToDiv: function(id, contentURL, callback){
        DynamicContentLoader.load(id, contentURL, 'prepend', callback);
    }, 
	
	
    /*
     * Called twice for each popup. Once for width and once for height to make sure it is centered
     * on the page according to where the scrollbars are.
     */
    calculateProperPosition: function(scrollPosish, windowMeasurement, popupMeasurement) {
    	var windowMidpoint = Math.floor(windowMeasurement/2);
    	
    	var popupMidpoint = Math.floor(popupMeasurement/2);
    	
    	if (windowMidpoint - popupMidpoint > 0) {
    		return windowMidpoint - popupMidpoint + scrollPosish;
    	} else {
    		return scrollPosish;
    	}
    },
    
    /*
     * Create a centered Popup on the page by appending a <DIV> tag to the body of the current page 
     * and dynamically loading it with the given URL.
     * 
     * afterLoadedCallback (optional) is called after the DIV has been loaded onto the page.
     *   Set up any desired event handlers here.
     */
    createPopup: function(id, contentURL, modal, width, height, draggable, afterLoadedCallback){
	if (draggable === undefined) draggable = false;	// off by default per request by Design Team, Kristine, Tim, and Satya -Brent 2010-10-02
        if (modal === undefined) 
            modal = false;
			
        var top = this.calculateProperPosition($(window).scrollTop(), $(window).height(), height);
        //375 px are added to avoid overlapping the ads with the popup
        var left = this.calculateProperPosition($(window).scrollLeft(), $(window).width(), width);
        
        var divParams = 'id="' + id + '" style="position:absolute; left:' + left + 'px; top:' + top + 'px; height:' + height + 'px; width:' + width + 'px;z-index:150;"';
        $popup = $('<div ' + divParams + '></div>');
        $popup.appendTo("body");
       
        DynamicContentLoader.load(id, contentURL, 'replace', function() {
        	//hide overlap stuff
       	 	TZ.popupHandler.hideOverlapStuff();
        
        	//make the popup draggable... sent as a callback to the load function
        	//so that the draggable call happens on the full size popup.
        	if(draggable == true) $popup.draggable();
	        if (modal) {
	            DynamicContentLoader.addMask();
	        }			
			// Call the completion callback so the caller can perform any desired initialization
			if (afterLoadedCallback) afterLoadedCallback($popup);
        });
    },
    
    /*
     * Remove a popup.
     */
    removePopup: function($popup){
        DynamicContentLoader.removeMask();
    	if ($popup.hasClass('basePopUp')) {
    		$popup.parent().remove();
    	} else {
    		$popup.remove();
    	}
        TZ.popupHandler.showOverlapStuff();
    }
};

/*
 * Share Bar Handler

$(function(){
    var $shareBar = $('.share_bar').eq(0).detach();
    $('[name=share_bar]').append($shareBar);
    $shareBar.show();
}); */

/*
 * CheckBoxes Handler
 */

$('.checkbox').live('click',function() {
	if ($(this).hasClass('checkbox_checked')) 
		$(this).removeClass('checkbox_checked');
	else 
		$(this).addClass('checkbox_checked');
});

/*
 * Text area max-char limit
$(function(){
 	$('[limit-chars]').keydown(function(){
		var maxChars=$(this).attr('limit-chars');
		if($(this).val().length > maxChars){
			$(this).val($(this).val().substr(0, maxChars));
	 	}
 	});
});
*/
 
/*
 * Make textareas grow Facebook style jQuery plugin
 */
(function(a){
    a.fn.autoResize = function(j){
        var b = a.extend({
            onResize: function(){
            },
            animate: true,
            animateDuration: 150,
            animateCallback: function(){
            },
            extraSpace: 0,
            limit: 500
        }, j);
        this.filter('textarea').each(function(){
            var c = a(this).css({
                resize: 'none',
                'overflow-y': 'hidden'
            }), k = c.height(), f = (function(){
                var l = ['height', 'width', 'lineHeight', 'textDecoration', 'letterSpacing'], h = {};
                a.each(l, function(d, e){
                    h[e] = c.css(e)
                });
                return c.clone().removeAttr('id').removeAttr('name').css({
                    position: 'absolute',
                    top: 0,
                    left: -9999
                }).css(h).attr('tabIndex', '-1').insertBefore(c)
            })(), i = null, g = function(){
                f.height(0).val(a(this).val()).scrollTop(10000);
                var d = Math.max(f.scrollTop(), k) + b.extraSpace, e = a(this).add(f);
                if (i === d) {
                    return
                }
                i = d;
                if (d >= b.limit) {
                    a(this).css('overflow-y', '');
                    return
                }
                b.onResize.call(this);
                b.animate && c.css('display') === 'block' ? e.stop().animate({
                    height: d
                }, b.animateDuration, b.animateCallback) : e.height(d)
            };
            c.unbind('.dynSiz').bind('keyup.dynSiz', g).bind('keydown.dynSiz', g).bind('change.dynSiz', g)
        });
        return this
    }
})(jQuery);
/*
 * Popup 
 */
TZ.popup = {
	addMask:function() {
		$('body').append('<div id="modalPopupMask"></div>');
		var maskHeight = $('body').height();
		var maskWidth = $('body').width();

		$('#modalPopupMask').css({'width':maskWidth,'height':maskHeight});
		$('#modalPopupMask').fadeTo("slow",0.3);
	},
	removeMask:function() {
		$('#modalPopupMask').fadeOut(1000,function(){$('#modalPopupMask').remove();});		
	}	
}
$('.validation_error').live("keydown click change",
		function(event){
				var $textContainer = $(event.currentTarget);
				if($textContainer.hasClass('radiobutton'))
				{	var name = '[name="'+$textContainer.attr('name')+'"]'; 
					$textContainer = $(name);
				}
				$textContainer.removeClass('validation_error').removeAttr('tooltip').tooltip('destroy');				
		}
);		
/*
 * Form Check Count
 */
TZ.checkCount = {
	setUp:function(){
		$('.countCheck').remove();
		$('.checkLength').each(function(index,element) {
			$(this).before('<div class="countCheck" >'+ ( $(element).data('max') - $(element).val().length) + ' chars left</div>');
		});
		$('.checkLength').bind('keyup onpaste',(function(event) {
			var $textContainer = $(event.currentTarget);
			var dif = $textContainer.data('max') - $textContainer.val().length;
			$textContainer.prev().text(dif + ' chars left');
			if (dif>=0)
			{
				$($textContainer).prev().removeClass('characterExceeded');
				$($textContainer).removeClass('validation_count_error').removeAttr('tooltip');
				TZ.tooltip.removeToolTip($textContainer);
				TZ.tooltip.outTooltip();
			}
			else
			{
				$($textContainer).prev().addClass('characterExceeded');
				$($textContainer).addClass('validation_count_error').attr('tooltip','<p class="exccededTitle">EXCEEDED CHARACTER LIMIT</p>Limit your '+ $textContainer.data('title') +' to '+ $textContainer.data('max') +' characters.');
				$($textContainer).tooltip();
			}
		}));
	}
};

function flushThis(id){
   var msie = 'Microsoft Internet Explorer';
   var tmp = 0;
   var elementOnShow = document.getElementById(id);
   if (navigator.appName == msie){
      tmp = elementOnShow.parentNode.offsetTop  +  'px';
   }else{
      tmp = elementOnShow.offsetTop;
   }
};



this.clean_url_fragment = function(url){
	url = url.toLowerCase();
	url = url.replace(/\&([^;]+)\;/,'');
	url = url.replace(/\s/g,'-');
	url = url.replace(/[^0-9a-z_-]/g,'');
	url = url.substring(0,40);
	return url;
}

String.prototype.format = function() {
    var formatted = this;
    for (var i = 0; i < arguments.length; i++) {
        var regexp = new RegExp('{'+i+'}', 'gi');
        formatted = formatted.replace(regexp, arguments[i]);
    }
    return formatted;
};

function objectEquals(x, y)
{
	
	for(p in y){
	    if(typeof(x[p])=='undefined') {return false;}
	}

	for(p in y) {
	    if (y[p]){
	        switch(typeof(y[p]))
	        {
	                case 'object':
	                        if (!objectEquals(y[p], x[p])) { return false }; break;
	                default:
	                        if (y[p] != x[p]) {return false; }
	        }
	    }
	    else{
	        if (x[p]){
	            return false;
	        }
	    }
	}

	for(p in x){
	    if(typeof(y[p])=='undefined') {return false;}
	}
	
	return true;
};

TZ.notification = { 
			show:function(title, text,expires,speed){
				$('#notification_container').notify("create","basic-template", {
				    	title: title,
				    	text: text 
				    }, 
				    {
				    	speed:speed,
				    	expires:expires
					}			    
				);
				var left = $(window).width()/2 - $('#notification_container').width()/2;
				var top = $(window).height()/2 - $('#notification_container').height()/2;
				$('#notification_container').css('left',left);
				$('#notification_container').css('top',top);
			}
	}
	
TZ.Form = {
	reset : function (id)
	{
		$(id).children().each(function(index,element){
			Debug.log(element,'jpdiaz');
			$(element).find('.textAreaComponent').textAreaReset();
			$(element).find('.textInputComponent').textInputReset();
			$(element).find('.simple_drop_down').dropDownReset();
			$(element).find('.attachItem').attachReset();			
			$(element).find('.validateVideoComponent').videoReset();
			$(element).find('.radiobutton_selected').radioButtonReset();
		});
	}
}

//RESET FUNCTIONS
$.fn.textAreaReset = function() {
	$(this).children('textarea').val('');
};

$.fn.textInputReset = function() {
	$(this).children('input').val('');
};

$.fn.dropDownReset = function() {
	if(TZ.simpleDropDown)
		TZ.simpleDropDown.reset(this);
};

$.fn.radioButtonReset = function() {
	if(TZ.RadioButton)
		TZ.RadioButton.reset(this);
};

$.fn.attachReset = function() {
	if(TZ.attach)
		 $.each(this, 
			 	function(index, value){ 
			 		TZ.attach.resetAttach($(value).parent());   
			 		});		
};

$.fn.videoReset = function() {
	if(TZ.videoInput)
	TZ.videoInput.resetPreview($(this));
};

//Get/Set Value from any Taaz Component.

$.fn.tzVal = function(value,properties){
	if($(this).hasClass('textAreaComponent'))
		return $(this).textAreaVal(value);
	if($(this).hasClass('textInputComponent'))
		return $(this).textInputVal(value);
	if($(this).hasClass('simple_drop_down'))
		return $(this).simpleDropDownVal(value);
	if($(this).hasClass('radiobutton'))
		return $(this).radioButtonVal(value);
	if($(this).children(0).hasClass('radiobutton') || $(this).children(0).hasClass('radio_button_image_row')) 
		return $(this).radioButtonGroupVal(value);
	if($(this).hasClass('checkbox_button'))
		return $(this).checkBoxVal(value);
	
	if($(this).find(':radio').size() > 0) {
		if (value) {
			var lookupItem = $(this).find("input[data='"+value+"']'")[0]
			if (!lookupItem) Debug.error('tzVal('+value+') called, but no value named ' + value + ' was found');
			return lookupItem && lookupItem.click();
		} else {
			return $(this).find("input:checked").attr('data');
		}
	}
	//For images, get the image Id from the name tag or
	// Replacing the current content for a new image using an id and an array properties
	if($(this).find('.attachItem').length > 0)
		if(value!=null)
		{
			$(this).find('.attachItem').children().remove();
			var image = TZ.getImageDiv(value,properties[0],properties[1],properties[2],properties[3],properties[4]);
			$(this).find('.attachItem').append(image);
		}
		else
		{
			return $(this).find('.inner_photo').attr('name');
		}
	if($(this).hasClass('.sized_image')){
		if(value!=null)
		{
			$(this).find('.sized_image').children().remove();
			var image = TZ.getImageDiv(value,properties[0],properties[1],properties[2],properties[3],properties[4]);
			$(this).find('.sized_image').append(image);
		}
		else
		{
			return $(this).find('.sized_image').attr('name');
		}
	}
}

//Add extra string wrap to save User account Data.

function escapeQuotes(string){
	string = string.replace(/'/gi,"\'");
	return "'"+string+"'"; 
}


//Escape/Unescape HTML

function unescapeHTML(html) {
	var str = $("<div />").html(html).text();
	return str
	}
function escapeHTML(html) {
	return $("<div />").text(html).html();
	}

String.prototype.capitalize = function() {
    return this.charAt(0).toUpperCase() + this.slice(1);
}

/*
 * @update Added Delete calls - Brent (2011-12)
 */

TZ.topics = {
	createTopic:function(schemaName,topicType,properties,success_function,error_function){
		var p = {schemaName:schemaName,topicType:topicType,properties:properties};
		TZ.rpc.createRPC("CreateTopic",p,success_function,error_function);
	},
	
	/**
	 * Delete given topic, if current user is the owner.
	 */
	deleteTopic: function (topicId, onSuccess, onFailure) {
		var p = { topicId:topicId };
		TZ.rpc.invoke("DeleteTopic", p, onSuccess, onFailure);
	},
	
	/**
	 * Delete given topic, if current user has content admin privileges, whether or not the owner.
	 */
	adminDeleteTopic: function (topicId, onSuccess, onFailure) {
		var p = { topicId:topicId };
		TZ.rpc.invoke("AdminDeleteTopic", p, onSuccess, onFailure);
	},
	
	/**
	 * Delete the given topic entry (e.g., comment).
	 */
	deleteTopicEntry: function (topicId, topicEntryId, onSuccess, onFailure) {
		var p = { topicId:topicId, topicEntryId:topicEntryId };
		TZ.rpc.invoke("AdminDeleteTopicEntry", p, onSuccess, onFailure);
	},
	
	readTopic:function(rpcName,topicType,properties,success_function,error_function){
		var p = {topicType:topicType,properties:properties};
		TZ.rpc.createRPC(rpcName,p,success_function,error_function);
	},
	createTrendHowTo:function(title,description,thumbData,category,subType,subtitle,steps,videoData, success_function,error_function){
		var p = {videoData:videoData, steps:steps, subtitle:subtitle};
		this.createTrend("howto",title,description,thumbData,category,subType, p,success_function,error_function);
	},
	createTrendArticle:function(title,description,thumbData,category,subType,subtitle,images, template, videoData,success_function,error_function){
		var p = {images:images,videoData:videoData,template:template,subtitle:subtitle};
		this.createTrend("article",title,description,thumbData,category,subType, p,success_function,error_function);
	},
	createTrendPhoto:function(title,description,thumbData,category,subType,images,success_function,error_function){
		var p = {images:images};
		this.createTrend("photo",title,description,thumbData,category,subType,p,success_function,error_function);
	},
	createTrendVideo:function(title,description,thumbData,category,subType,videoData,success_function,error_function){
		var p = {videoData:videoData};
		this.createTrend("video",title,description,thumbData,category,subType,p,success_function,error_function);
	},
	createTrend:function(schemaName,title,description,thumbData,category,subType,trendProperties,success_function,error_function){
		var p = {title:title,description:description,thumbData:thumbData,category:category,subType:subType};
		var mergedObject = $.extend({}, p, trendProperties);
		this.createTopic(schemaName,"trends",mergedObject,success_function,error_function);
	},
	createAdvice:function(title,description,thumbData,images,category,subType,success_function,error_function){
		var p = {title:title,description:description,thumbData:thumbData,images:images,category:category,subType:subType};
		this.createTopic("advice","advice",p,success_function,error_function);
	},
	createDeal:function(title,store,subType,couponCode,externalDealUrl,description,brand,expiration,thumbData,success_function,error_function){
		var p = {title:title,store:store,subType:subType,couponCode:couponCode,externalDealUrl:externalDealUrl,description:description,brand:brand,expiration:expiration,thumbData:thumbData};
		this.createTopic("deals","deals",p,success_function,error_function);
	},
	getGallerySize:function(type,subType,filterBy,orderBy,onSuccess,onError)
	{	var subTypeString = (subType != '')? subType+'/' : '';
		var galleryUrl = '/gallery/taaz2/'+type+'/'+ subTypeString +filterBy+'/'+orderBy+'/gallery.json' ;
		$.getJSON(galleryUrl, onSuccess,onError);
	}, 
	submitTopicToGallery:function(topicId,success_function,error_function){
			var p = {topicId:topicId}; 
			TZ.rpc.createRPC('SubmitTopicToGallery',p,success_function,error_function);
	} 
}
TZ.template = {
	createElement:function(templateString,data){
		var tc = templateString.slice(0);
		tc = TZ.template.processHash(tc,data);
		while(tc.search(/##.*?##/) != -1){
			tc = tc.replace(/##.*?##/,'');
		}
		var element = $(tc);
		element.find('[tooltip]').tooltip();
		return element;
	},
	processHash:function(tcString,data){
		for(var s in data){
			if(data[s] == undefined) continue;
			if( data[s].constructor == Array) continue;
			else if( data[s].constructor == String) {}
			else if( data[s].constructor == Number) {}
			else if( data[s].constructor == Boolean) {}
			else if( data[s].constructor == Object){ 
				tcString = TZ.template.processHash(tcString,data[s]);
				continue;
			}
			while(tcString.search("##"+s+"##") != -1){
				tcString = tcString.replace("##"+s+"##",data[s].toString());
			}
		}
		return tcString;
	}
 }
/**
 * Support for social login.
 * 
 * @class social
 * @namespace TZ
 * @author brent
 */
TZ.social = {
	/**
	 * Support for Facebook login/integration.
	 * 
	 * @class Facebook
	 * @namespace TZ.social
	 */
    'Facebook': {
		_uid: null,
		_auth: null,
		
		/**
		 * Call init to enable Facebook integration. Called on every page.
		 */
        init: function(appId){
		Debug.log('FB: setting up callbacks and calling init', 'brentf');
			
            /*
             * Facebook Event Handlers
             * 	auth.authResponseChange - user logged in or out
             * 	auth.statusChange  - a third state: connected, notConnected or unknown
             *	edge.create/remove - user clicked a Like button
             *
             * response:
             *	status: connected		(connected, notConnected or unknown)
             * 	id
             *	name
             *	authResponse:
             *		access_token:
             *		expires:
             *		sig:
             *		uid:
             *		base_domain?
             *	scope: (only for permissions call)
             */
            FB.Event.subscribe('authResponseChange', TZ.social.Facebook.handleStatusChange);
            FB.Event.subscribe('auth.login', TZ.social.Facebook.handleStatusChange);
            //FB.Event.subscribe('auth.statusChange', function(response){
            //    Debug.log('FB: -- statusChange -- : ' + response.status, "brentf");	// status: connected, notConnected, notAuthorized or unknown
            //});
            // Note: FB doesn't send this logout event when logged out using another Tab
            //		 only sent when logged out of current tab (such as on Page reload)
            FB.Event.subscribe('auth.logout', function (response){
                Debug.info('FB: Logged Out', "brentf");
				TZ.social.Facebook._auth = null;
                //document.location.reload() -- don't reload -- 
            });
            /**
             * Intercept Like / Unlike
             *	http://www.taaz.com/advice/skincare/how-do-you-get-rid-of-garlic-and-onion-b/1xCOrev2jHJcp9LwNmw-RD2hxEa9DdfF.html
             */
            FB.Event.subscribe('edge.create', function (targetUrl) {
				Analytics.like(targetUrl);
				
				if (targetUrl.endsWith('#')) 	// Footer Like button uses www.taaz.com# for Facebook count, but
					targetUrl = TZ.request.url;	// send full url to Analytics
				
            	var url = targetUrl.replace(TZ.request.baseUrl, '').replace(/^\//, '');	// remove host & leading slash
				Analytics.event('Social', 'FacebookLike-' + url);	// append s2 manually, so url doesn't get capitalized
            	
                Debug.log('FB: Liked: ' + url, "brentf");
            });
            FB.Event.subscribe('edge.remove', function (url){
            	url = url.replace(TZ.request.baseUrl, '').replace(/^\//, '');	// remove host & leading slash
				Analytics.event('Social', 'FacebookUnlike-' + url);	// append s2 manually, so url doesn't get capitalized
				
                Debug.log('FB: Un-liked: ' + url, "brentf");
            });
            FB.Event.subscribe('message.send', function (targetUrl){
            	//var url = targetUrl.replace(TZ.request.baseUrl, '').replace(/^\//, '');	// remove host & leading slash
				Analytics.social('facebook', 'send', targetUrl);
            });
            //FB.Event.subscribe('fb.log', function (response){
            //   Debug.log('FB: Log: ' + response);
            //});
            
            FB.init({
                appId  : appId,
                status : true,
                cookie : false,
                xfbml  : true,		// replace XFBML currently on page
                oauth  : true		// use OAuth2
            });
        },
		
		/*
		 * When any page is loaded:
		 * Store the Facebook Uid, if a user is currently logged into facebook on some other Tab within the Browser
		 * 
		 * Note: We DO NOT receive the Facebook User ID until access is granted to our application.
		 */
		handleStatusChange: function(response) {
			if (true) {	//TZ.social.Facebook._uid === null)
	            Debug.log('TZ.social.Facebook.handleStatusChange: ' + response.status, "brentf");
            	/*DEBUG*/ Debug.log('Facebook accessToken = ' + response.authResponse.accessToken, "brentf");
				
	            if (response.authResponse) { // user is known to our application
	            	TZ.social.Facebook._auth = response.authResponse;
					var fbUid = response.authResponse.userID;
					TZ.social.Facebook._uid = fbUid;
				}
				else {
					Debug.info('Not logged in to Facebook?', 'brentf');
	            	TZ.social.Facebook._auth = null;
					TZ.social.Facebook._uid = null;
				}
				Debug.log('exit handleStatusChange. _uid = ' + fbUid);
			}//if not yet called
        },
		
		accessToken : function () {
			var authResponse = TZ.social.Facebook._auth;
            Debug.log('Called TZ.social.Facebook.accessToken: authResponse.accessToken = ' + authResponse.accessToken, "brentf");
			var result = (authResponse !== null);
			if (authResponse)
				result = authResponse.accessToken;
				
			return result;
		},
		
		isLoggedIn: function() {
			return (TZ.social.Facebook._auth !== null);
		},
		
		// example opts={scope: 'email'}
		login: function (onSuccess, onFailure, opts) {
			Debug.log('Calling FB.login with permissions: ' + JSON.stringify(opts));
			
			if (TZ.social.Facebook._auth !== null)
				onSuccess(TZ.social.Facebook._auth);
			else
				FB.login(function(response) {
						//TZ.social.Facebook._auth = response.authResponse
						if (response.authResponse) {
							Debug.log('TZ.social.Facebook.login calling onSuccess');
							onSuccess(response.authResponse);
						}
						else {
							Debug.log('TZ.social.Facebook.login failed.');
							onFailure();
						}
					},
					opts
				);
		},
		
		//'registerUi': function () {
		//	Debug.log('Attempting to register using: ' + "https://www.facebook.com/dialog/oauth?client_id=99187993246&redirect_uri=http://starstyler.gala.de/facebook_registration_callback&scope=email", 'brentf')
		//	window.open("https://www.facebook.com/dialog/oauth?client_id=99187993246&redirect_uri=http://starstyler.gala.de/facebook_registration_callback&scope=email", 'Connect with Facebook', 'top=200,left=100,width=760,height=550')
		//},
		
		connect: function (connectSuccess, connectFailure) {
			Debug.log('enter TZ.social.Facebook.connect');
			
			TZ.social.Facebook.login(function() {
					Analytics.page('RegistrationPopup-RegisterFacebookAllow');
					Analytics.event('RegistrationPopup', 'RegisterFacebookAllow');	// user clicked 'Allow' permissions
					
					fbUserId = TZ.social.Facebook._uid;
					//$.getJSON('https://graph.facebook.com/' + fbUserId, function(fbProperties)
					FB.api('/me', function(fbProperties) {
						Debug.log('  Retrieved fbProperties: ', 'brentf');
						Debug.log(fbProperties, 'brentf');
						
						var firstName = fbProperties.first_name,
						    lastName = fbProperties.last_name,
						    email     = fbProperties.email,
						    birthDate = fbProperties.birthday,	// parse year below
						    birthYear = '';
						
						if (birthDate) {
							var bdayParts = birthDate.split('/');
							birthYear = (bdayParts.length > 2) ? bdayParts[2] : '';
						}
						fbProperties.birth_year = birthYear;		// save for later, in case registration isn't completed below
						
						// Create a link to FB and save the originally-returned facebook properties + 'birth_year'
						TZ.rpc.invoke("SaveOAuthAccount", { 'provider': 'facebook', 'oAuthUserId':  fbUserId, 'properties':  fbProperties }, 
							function(oAuthRec) {
								Debug.log("Facebook account authorized for use by Taaz Account (pending Taaz Account creation): " + JSON.stringify(oAuthRec));
							},
							function(e){
								//FIX: display error message that another user is already linked to this Facebook account
								Debug.warn('TZ.rpc.invoke of SaveOAuthAccount failed: ' + e.error);
								if (connectFailure) connectFailure(e);
							}
						);
						
						//
						// Change the Registration tab to show Now Connected To Facebook (still need to register with Taaz)
						//
						var photoUrl = 'https://graph.facebook.com/me/picture?access_token=' + TZ.social.Facebook.accessToken();
						connectSuccess(firstName, lastName, email, birthYear, photoUrl);
					});
				},
				function() {
					Analytics.page('RegistrationPopup-RegisterFacebookDontAllow');
					Analytics.event('RegistrationPopup', 'RegisterFacebookDontAllow');
				},
				{ scope: 'email,user_birthday' }
			);
		},
		
		hideLikeButtons : function(){
			$('.fb_iframe_widget').css('visibility','hidden');
		},
		showLikeButtons : function(){
			$('.fb_iframe_widget').css('visibility','visible');
		}	
    },
    
    //
    // Call to re-parse Facebook XFBML elements in popups (already done for main page by init)
    //
	parse: function(){
       FB.XFBML.parse();
   }
};


		
			
		
		$(function() {
			TZ.events.addEventListener("loginStatusChange",function(){
				if(TZ.rpc.loggedIn)
				{
					$('.block_btn_sm, .block_btn_md').each(function(index, element){
							TZ.profile.didIBlockYou($(element).attr('accountId'),
							function(flag) {
							if(flag)
								$(element).addClass('blocked').show();
							})
					})
				} 
				else
				{
					$('.block_btn_sm, .block_btn_md').hide().removeClass('blocked');
				}
			});
		
			$('.block_btn_sm, .block_btn_md').live('click', function (event) {
				var $blockBtn = $(this);
				var accountId = $blockBtn.attr('accountId');
				var isAdminFunction = event.altKey;
				
				// Handle Admin function for Alt-click
				if (isAdminFunction) {
					TZ.profile.deleteAccount(accountId, "false", function () {
							$blockBtn.addClass('deleted');
							$('#profile_box h1').append(' (DELETED)');	// show admin that successfully deleted
						},
						function(e){ Debug.warn('Error deleting account: ' + e.error, 'brentf'); }
					);
				}
				// Toggle blocked state
				else if ($blockBtn.hasClass('blocked'))
					TZ.profile.unblockUser(accountId, function () {
							$blockBtn.removeClass('blocked');
						}
					);
				else
					TZ.profile.blockUser(accountId, function () {
							$blockBtn.addClass('blocked');	
						}
					);
			});
		});
	
	
		
	
		
			
TZ.formValidation = {
	checkTxtLength:function (o, min, max) {
		if (o.val().length > max && max >0) {
			return "tooLong";
		}	
		if(o.val().length < min || o.val() == $(o).data('exampleText')) {
			return "tooShort";
		}
		return "";
	},
	checkRegexp:function(o, regexp) {
		if($.type(o) === "string")
			testString = o
			else
				testString = o.val()
        if (!(regexp.test(testString))) {
            return false;
        } else {
            return true;
        }
    },
	checkDate:function(o){
		var reDate = /(?:0?[1-9]|1[0-2])\/(?:0?[1-9]|[12][0-9]|3[01])\/(?:19|20\d{2})/;	/*allow single digits: 1/2/1975 - Brent 2011/2/21*/
		if (!(TZ.formValidation.checkRegexp(o,reDate))) {
            return false;
        } else {
            return true;
        }				
	},
	checkEmail:function(o) {
		if (!(TZ.formValidation.checkRegexp(o, /^[a-zA-Z0-9._+-]+@[a-zA-Z0-9._-]+\.[a-zA-Z]{2,4}$/))) {
            return false;
        } else {
            return true;
        }
    },
    checkNumber:function(o) {
		if (!(TZ.formValidation.checkRegexp(o, /^[0-9]+$/))) {
            return false;
        } else {
            return true;
        }
    },
	checkURL:function(o) {
		if (!(TZ.formValidation.checkRegexp(o, /^((http|https|ftp):\/\/)?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?$/))) {
            return false;
        } else {
            return true;
        }
    },
    setupValidateLength:function(characterLimit, field, title,displayCount){
		var thisTextArea = field;
		thisTextArea.data('max', characterLimit).data('title', title);
		if(displayCount===undefined) {displayCount = true}
		//Display Count
		if(displayCount){	
			thisTextArea.children('div.countCheck').remove();
			thisTextArea.before('<div class="countCheck" >'+ ( thisTextArea.data('max') - thisTextArea.val().length) + ' chars left</div>');
		}
		thisTextArea.bind('keyup onpaste',this.validateLength);
		this.validateLength(null, thisTextArea, displayCount);
		
	}, 
	validateLength : function(event, field, displayCount) {
		var $textContainer = (event == null)?field:$(event.currentTarget);
		var checkCount = $textContainer.parent().find('.countCheck'); 
		var textVal = $textContainer.parent().tzVal();
		var dif = $textContainer.data('max') - textVal.length;
		if(displayCount===undefined) {displayCount = true}
		if(displayCount){	
			$(checkCount).text(dif + ' chars left');
		}
		if (dif>=0)
		{
			$(checkCount).removeClass('characterExceeded');
			$($textContainer).removeClass('validation_count_error').removeAttr('tooltip');
			TZ.tooltip.removeToolTip($textContainer);
			TZ.tooltip.outTooltip();
		}
		else
		{
			$(checkCount).addClass('characterExceeded');
			$($textContainer).addClass('validation_count_error').attr('tooltip','<p class="exccededTitle">EXCEEDED CHARACTER LIMIT</p>Please limit your '+ $textContainer.data('title') +' to '+ $textContainer.data('max') +' characters.');
			$($textContainer).tooltip();

		}
	},
	validateRequiredFields:function($fieldContainer) {
		var $requeridFields=$fieldContainer.find('.requiredField');
		var errArray=[];
		$.each($requeridFields,
			function(){
				var validationLengthResult=TZ.formValidation.checkTxtLength($(this), 1, 0);
				if(validationLengthResult =='tooShort')
					errArray.push({
							field: $(this),
						errorMessage: TZ.formValidation.capitalize(this.name) + " is required"
					});
				}				
		);
		return errArray;
	},
	validateLengthFields:function($fieldContainer) {
		var $lengthFields=$fieldContainer.find('.checkLength');
		var errArray=[];
		$.each($lengthFields,
			function(element){
				var validationLengthResult=TZ.formValidation.checkTxtLength($(this), 1, $(this).data('max'));
				if(validationLengthResult =='tooLong')
				{
					errArray.push({
						field: $(this),
						errorMessage: TZ.formValidation.capitalize(this.name) + " is too long"
					});
			}
		}
		);
		return errArray;
	},
	validateEmail:function($fieldContainer) {
		var $emailFields=$($fieldContainer).find('.emailField');
		var errArray=[];
		$.each($emailFields,
			function(emailFieldIndex,emailField){
				var recipients = new Array();
				recipients = $(emailField).parent().tzVal().split(",");
				$.each(recipients,
					function(index, email) {
						email = $.trim(email);
						if (!TZ.formValidation.checkEmail(email)) {
							errArray.push({
								field:$(emailField),
								errorMessage:'Please check your entry for invalid characters. Valid characters for emails are alphanumeric, "@", or "." Emails can be joined with commas.'
							});		
							$(emailField).addClass('emailInvalid');
							$(emailField).addClass('validation_error').attr('tooltip','<p class="exccededTitle">Email Invalid</p><p class="errorContent">Please check your entry for invalid characters. </p><p class="errorContent">Valid characters for emails are alphanumeric, "@", or "." </p><p class="errorContent">Emails can be joined with commas.</p>');
							$(emailField).tooltip();
						}
					}
				); 
			}
		);
		return errArray;
	},
	validateNumbers:function($fieldContainer){
		var $fields=$fieldContainer.find('.numericField');
		var errArray=[];
		$.each($fields,
			function(fieldIndex){
				var field = $fields[fieldIndex];
				if (!TZ.formValidation.checkNumber($(this))) {
					errArray.push({
						field:$(this),
						errorMessage:"Invalid Number"
						});	
				}
		});
		return errArray;
	},
	//validate user age based on YYYY
	validateAge:function($fieldContainer){
		var $fields=$fieldContainer.find('.ageField');
		var errArray=[];
		$.each($fields,
			function(fieldIndex){
				var field = $fields[fieldIndex];
				if (TZ.formValidation.checkNumber($(this))) {
					d = new Date;
					var year = parseInt($(field).parent().tzVal());
					var currentYear = parseInt(d.getUTCFullYear());
					var age = currentYear - year;
					if (year < 1900 || year > currentYear) {
						errArray.push({
							field:$(this),
							errorMessage: "Invalid Year"
						})
					} else if(age < 13 ) {
						errArray.push({
							field:$(this),
							errorMessage:"Invalid Age"
							});
					}
				} else {
					errArray.push({
						field:$(this),
						errorMessage:"Invalid Number"
						});	
				}
			
		});
		return errArray;
	},
	validateURL:function($fieldContainer) {
		var $urlFields=$fieldContainer.find('.urlField');
		var errArray=[];
		$.each($urlFields,
			function(checkURL){
				if (!TZ.formValidation.checkURL($(this))) {
					errArray.push({
						field:$(this),
						errorMessage:"Invalid URL"
						});		
				}	
			}
		);
		return errArray;
	},
    validateVideoUrl: function($fieldContainer) {
		var $videoUrlFields=$fieldContainer.find('.videoUrlField');
		var errArray=[];
		$.each($videoUrlFields,
			function(checkVideoURL){
				if ($(this).textInputVal() !='' && !($(this).parent().hasClass('videoDataLoaded'))) {
					$(this).find('input').data('tooltip','Invalid video URL');
					errArray.push({
						field:$(this).find('input'),
						errorMessage:"Invalid video URL"
					});	
				}
			}
		);
		return errArray;
    },
	validateDate:function($fieldContainer) {
		var $dateFields=$fieldContainer.find('.dateField');
		var errArray=[];
		$.each($dateFields,
			function(checkDate){
				var $tempDateField=$(this);
				if ($tempDateField.val().length>0) {
					if (!TZ.formValidation.checkDate($tempDateField)) {
						errArray.push({
							field:$tempDateField,
							errorMessage:"Invalid Date"
							});		
					}							
				}	
			}
		);
		return errArray;
	},
	validateSelection:function($fieldContainer){
		var $selectionFields=$fieldContainer.find('.radiobutton');
		var count = 0;
		var errArray=[];
		$.each($selectionFields,
				function(selection){
					if($(this).hasClass('radiobutton_selected'))
						count += 1;
				});
		if(count==0)
		{
			$.each($selectionFields,function(){
				var $tmpSelection = $(this);
			errArray.push({
				field:$tmpSelection,
				errorMessage:"select one"
				});
			});
		}
		return errArray;
	},
	equalValue:function(field1,field2) {
		if (field1.val()==field2.val()){
			return true;
		}
		return false;
	},
	capitalize:function(word){
		return word.replace( /(^|\s)([a-z])/g , function(m,p1,p2){ return p1+p2.toUpperCase(); } );
	},
	validate:function($fieldContainer,otherErrorsArray){
		if (otherErrorsArray===undefined) {
			otherErrorsArray=[];
		}
		$fieldContainer.find('span.validation_error').remove();
		$fieldContainer.find('.validation_error').removeClass('validation_error');
		var textLengthRequiredErrors=TZ.formValidation.validateRequiredFields($fieldContainer);
		var textLengthFieldsErrors=TZ.formValidation.validateLengthFields($fieldContainer);
		var emailFieldsErrors=TZ.formValidation.validateEmail($fieldContainer);
		var urlFieldsErrors=TZ.formValidation.validateURL($fieldContainer);
		var videoUrlFieldsErrors = TZ.formValidation.validateVideoUrl($fieldContainer);
		var dateFieldsError=TZ.formValidation.validateDate($fieldContainer);
		var radioFieldError=TZ.formValidation.validateSelection($fieldContainer);
		var numericFieldError=TZ.formValidation.validateNumbers($fieldContainer);
		var ageFieldError=TZ.formValidation.validateAge($fieldContainer);
		var allErrorsArray= textLengthFieldsErrors.concat(emailFieldsErrors,dateFieldsError,otherErrorsArray,textLengthRequiredErrors,urlFieldsErrors,videoUrlFieldsErrors,radioFieldError,numericFieldError,ageFieldError);
		TZ.formValidation.showErrors(allErrorsArray);
		return (allErrorsArray.length==0);
	},
	showErrors:function(errorsArray){
		if(!errorsArray.length>0) {return;}
		for (var errorIndex in errorsArray) {
			var singleError = errorsArray[errorIndex];
			$(singleError.field).addClass('validation_error');
			var tt = $(singleError.field).attr('tooltip');
			if (singleError.useMyMsgAsTooltipText) {
				$(singleError.field).attr('tooltip','<p class="exccededTitle">REQUIRED FIELD</p>'+singleError.errorMessage);
			} else {
				if(!$(singleError.field).attr('tooltip'))
				{
					if($(singleError.field).data('title')) $(singleError.field).attr('tooltip','<p class="exccededTitle">REQUIRED FIELD</p>Please enter a '+$(singleError.field).data('title'));
					if($(singleError.field).data('tooltip')) {
						$(singleError.field).attr('tooltip','<p class="exccededTitle">REQUIRED FIELD</p>'+$(singleError.field).data('tooltip'));
					}
				}
			}				
			$(singleError.field).tooltip();
		}
 		//TZ.tooltip.tooltip();
	}		
};

		
	
		
			$.fn.textInputVal = function(value) {
	if (value != null) {
		$(this).children('input').val(unescapeHTML(value));
	} else {
		if($(this).children('input').val() == $(this).children('input').data('exampleText'))
			return '';
		else return $(this).children('input').val();
	}
};

$.fn.textAreaVal = function(value) {
	if (value != null) {
		$(this).children('textarea').val(unescapeHTML(value));
	} else {
		if($(this).children('textarea').val() == $(this).children('textarea').data('exampleText'))
			return '';
		return $(this).children('textarea').val();
	}
};

$.fn.textFieldRequired = function(value) {
	if (value) {
		$(this).children('input').addClass('requiredField');
	} else {
		$(this).children('input').removeClass('requiredField');
	}
};

$.fn.textFieldCheckLength = function(value) {
	if (value) {
		$(this).children('input').addClass('checkLength');
	} else {
		$(this).children('input').removeClass('checkLength');
	}
};

$.fn.textFieldUrlField = function(value) {
	if (value) {
		$(this).children('input').addClass('urlField');
	} else {
		$(this).children('input').removeClass('urlField');
	}
};

TZ.textFields = {
	hintText : function(el, text) {
		el.data("exampleText", text);
		el.val(text);
		el.focus(function() {
			if (el.val() == text)
				el.val('');
			el.addClass('onfield');
		});
		el.blur(function() {
			if (el.val() == '') {
				el.val(text);
				el.removeClass('onfield');
			}
		});
	},
	noHintText: function(el){
		el.addClass('onfield');
	}
};

$(function(){
	$('.textInputComponent input').change(function(){
		TZ.events.dispatchEvent('content_changed',this);
	});
	$('.textAreaComponent textarea').change(function(){
		TZ.events.dispatchEvent('content_changed',this);
	});
});
		
	
		
			TZ.ratings = {
	typeCallbacks:{},
	addRatingCallback:function(cssClass,f){
		TZ.ratings.typeCallbacks[cssClass] = f;
	},
	onRatingCallback:function(el){
		for (var cb in TZ.ratings.typeCallbacks){
			if($(el).hasClass(cb)){
				TZ.ratings.typeCallbacks[cb](el);
			}
		}	
	},	
	loadRatings:function(topic){
		//var stars_position = [0,21,35,50,65,90];
		var topicRPCq = [];
		$('.fetch_rating').each(function(index,el){
			topicRPCq.push({topic:$(el).attr('title'),objectID:$(el).attr('id')});	
			$(el).removeClass('fetch_rating');
		});
		//*comment this out once rpc is done
		var o = [];
		for(var x in topicRPCq){
			var t = topicRPCq[x];
			if (TZ.random(10) > 6) { 
				o.push({enum_value:TZ.random(5,1),objectID:t.objectID});	
			}
		}
		// comment end 
		//TZ.rpc.createRPC('GetMultiTopicVotes',{elements:topicRPCq},function(o){
			for(var ri in o){
				var rating = o[ri];
				var q ='.rating_object:[id*="'+ rating.objectID +'"]';
				$(q).attr('value',rating.enum_value);
				TZ.ratings.onRatingCallback(q);	
			}
 		//});	
	},
	setRating:function(topic,identifier,enum_value){
		//TZ.rpc.createRPC('rateItem',{topic:topic,id:identifier,value:enum_value});
	},
	starsInit:function(){
		var stars_position = [0,21,35,50,65,90];
		TZ.ratings.addRatingCallback('star_object',function(el){
			var r = $(el).attr('value');
			$(el).children('.stars_highlight').css('width',stars_position[r]);
		});
		$('.star_selector').live('mouseenter',function(event){
			$(event.currentTarget).parent().prev().css('width',stars_position[$(this).index()+1]);
		});
		$('.star_selector').live('mouseleave',function(event){
			var ix = $(event.currentTarget).parent().parent().attr('value');
			if(ix==null){
				$(event.currentTarget).parent().prev().css('width',0);	
				return;
			}
			$(event.currentTarget).parent().prev().css('width',stars_position[ix]);	
		});
		$('.star_selector').live('click',function(event){
			var star_index = $(this).index()+1;
			var p = $(event.currentTarget).parent().parent();
			p.attr('value',star_index);
			TZ.ratings.setRating(p.attr('title'),p.attr('id'),p.attr('value'));
		});
	}
};
		
	
		
			
		
		$(
			function(){
				TZ.help.titleShowMoreSetUp();
			}
		);
		TZ.help = {
			titleShowMoreSetUp:function() {
				$('.title_show_more .title_show_more_title').bind('click',function() {
						var current_title$=$(this);
						var current_title_component$=current_title$.parent();
						var current_description$=current_title_component$.find('.title_show_more_description');
						
						if (current_description$.is(":visible")) {
							current_title$.removeClass('title_show_more_title_selected');
							current_description$.removeClass('title_show_more_description_selected');
						} else {
							current_title$.addClass('title_show_more_title_selected');
							current_description$.addClass('title_show_more_description_selected');
						}	
					}
				);
			}		
		};
	
	
		
	
		
			/**
 * Support for Google Analytics
 * 
 * @class Analytics
 * 
 * Usage:
 * 	Analytics.page('my/page/label');
 *	Analytics.event('SigninPopUp', 'TopBarSignIn')
 */
Analytics = {
	DEBUG: true,			// false for normal operation, true to block calls to Google and instead display a brief pop-up
	DEBUG_USER: 'brentf|timm',
	
	pageFunction: function(item){}, // handler for Analytics callbacks, set with setPageCallback()
		
	init: function () {
		$('.analytics').live('click',function(){
			if(Analytics.pageFunction) Analytics.pageFunction(this);
		})
	},
	
	/**
	 * Call to track the loading of a page.
	 */
	page: function (page) {
		Analytics._postMsg(['_trackPageview', page]);
	},
	
	setPageCallback: function(callback_function){
		if(callback_function) Analytics.pageFunction = callback_function;
	},
	/** Takes in a data object and runs it through the pageFunction.  This allows central processing on straight data items
	 * that don't have a corresponding element.
	*/	
	pageEvent: function(hashObject){
		if(!hashObject) return;
		var p = $('<div></div>').data('analytics',hashObject);
		Analytics.pageFunction(p);
	},
	
	/**
	 * Call to track a generic event.
	 * 
	 * @param category String required
	 * @param s1 String required
	 * @param s2 String if given, s2 and s3 are combined into a single GA action field: s1-s2-s3
	 * @param label String
	 * @param intVal int optional numeric data to log
	 */
	event: function (category, s1, s2, s3, label, intVal) {
		if (!category) return Debug.error('ERROR: Analytics.event() missing required Category field!');
		if (!s1)       return Debug.error('ERROR: Analytics.event('+category+', <required S1 missing>)!');
		
		// Action = s1-s2-s3
		s1 = Util.capitalize(s1);
		s2 = Util.capitalize(s2);
		s3 = Util.capitalize(s3);
		var action = s1 + (s2?'-'+s2:'') + (s3?'-'+s3:'');
		
		Analytics._postMsg(['_trackEvent', category, action, label || undefined, intVal || undefined]);	// params must be undefined if not set
	},
	
	/**
	 * Called when Facebook "Like" button is clicked.
	 */
	like: function (url) {
		Analytics.social('facebook', 'like', url);
	},
	
	/**
	 * Call to track any social interaction (not just the FB Like button).
	 */
	social: function (network, action, url) {
		Analytics._postMsg(['_trackSocial', network, action, url]);
	},
	
	/**
	 * Helper function to post the messages OR display a debug message if DEBUG is turned on.
	 */
	_postMsg: function (dataArray) {
		if (!(Analytics.DEBUG && Debug.notify('Analytics', dataArray.join(', '), Analytics.DEBUG_USER, 7000)))
			_gaq.push(dataArray);
	}
};
		
	
		
			(function($)
{
	$.fn.vid 		= function() 
	{
    	var c 		= {
			// required
			type			: 'youtube',			// String 		- defines the service, possible values are: youtube, vimeo, dailymotion, 1click2fame; for anymore, just fork the project or email me.
			videoId			: '4wGR4-SeuJ0',		// String		- defines the unique video identifier
			// optional
			autoPlay		: false,				// Boolean		- defines whether to autoplay the video or not
			loop			: false,				// Boolean		- defines whether to loop the video or not
			hq				: true,					// Boolean		- defines whether to play the highest quality version, so HD or HQ, rather than SD
			chromeless		: false,				// Boolean		- defines whether to use the chromless player for youtube only
			resize			: true					// Boolean		- defines whether you want the player to resize dynamicly to its parent's size
		};
	
		if ( arguments[ 0 ] )
		{
			$.extend( c, arguments[ 0 ] );
		}

		this.each( function() 
		{
			var e	= $( this );
			var h	= '';
			var v	= { };
			
			switch ( c.type )
			{
				case 'youtube':
				h	+= 'youtube.com/' + ( c.chromeless ? 'apiplayer' : 'v/' + c.videoId ) + '?enablejsapi=1&version=3&autoplay=' + ( c.autoPlay ? '1' : '0' ) + '&loop=' + ( c.loop ? '1' : '0' ) + ( c.hq ? '&hd=1' : '' );
				
				break;
				
				case 'vimeo':
				h	+= 'vimeo.com/moogaloop.swf?clip_id=' + c.videoId + '&js_api=1' + ( c.hq ? '' : '&hd_off=1' );
				
				break;
				
				case 'dailymotion':
				h	+= 'dailymotion.com/swf/' + c.videoId + '?enablejsapi=1&autoplay=' + ( c.autoPlay ? '1' : '0' );
				
				break;
				
				case '1click2fame':
				h	+= '1click2fame.com/flash/Player.swf?videoID=' + c.videoId + '&autoPlay=' + c.autoPlay;
				
				break;
			}
			
			e.flash({
				swf			: 'http://' + h,
				height		: e.height(),
				width		: e.width(),
				flashvars	: v
			});
			
			if ( c.resize )
			{
				$( this ).parent().resize( function()
				{
					var ts	= [ $( 'object', e ), $( 'embed', e ) ];
					
					$( ts ).each( function()
					{
						$( this ).attr( 'height', e.height() );
						$( this ).attr( 'width', e.width() );
					});
				})
			}
		});
		
		return this;
	};
})(jQuery);

// Including the jQuery SWFObject plugin by Jonathan Neal: http://jquery.thewikies.com/swfobject/
(function ($, flash) {
	var createAttrs = function (obj) {
		var aEach,
		aArray = [];

		for (aEach in obj) {
			aArray.push(aEach + '="' + obj[aEach] + '"');
		}

		return aArray.join('');
	};
	createParams = function (obj) {
		var aEach,
		bEach,
		aArray = [],
		bArray;

		for (aEach in obj) {
			if (typeof obj[aEach] == 'object') {
				bArray = [];

				for (bEach in obj[aEach]) {
					bArray.push([bEach, '=', encodeURIComponent(obj[aEach][bEach])].join(''));
				}

				obj[aEach] = bArray.join('&amp;');
			}

			aArray.push(['<param name="', aEach, '" value="', obj[aEach], '" />'].join(''));
		}

		return aArray.join('');
	};
	expressInstallIsActive = false;

	var Plugin = navigator.plugins['Shockwave Flash'] || window.ActiveXObject;
	if (!Plugin)
		$[flash] = function () { return {
			available: false,
			version: 0,
			hasVersion: function () { return false; },
			create: function () {}
		}};
	else
		$[flash] = (function () {
			var flashVersion = '0,0,0',
	
			flashVersion = Plugin.description || (function () {
				try {
					return (new Plugin('ShockwaveFlash.ShockwaveFlash')).GetVariable('$version');
				}
				catch (eIE) {}
			}());
	
			flashVersion = flashVersion.match(/^[A-Za-z\s]*?(\d+)[\.|,](\d+)(?:\s+r|,)(\d+)/);
	
			return {
				available: flashVersion[1] > 0,
	
				activeX: !Plugin.name,
	
				version: {
					major: flashVersion[1] * 1,
					minor: flashVersion[2] * 1, 
					release: flashVersion[3] * 1
				},
	
				hasVersion: function (version) {
					var versionCompare = this.version,
					major = 'major',
					minor = 'minor',
					release = 'release';
	
					version = (/string|number/.test(typeof version)) ? version.toString().split('.') : version || [0, 0, 0];
	
					version = [
						version[major] || version[0] || versionCompare[major],
						version[minor] || version[1] || versionCompare[minor],
						version[release] || version[2] || versionCompare[release]
					];
	
					return (version[0] < versionCompare[major]) || (version[0] == versionCompare[major] && version[1] < versionCompare[minor]) || (version[0] == versionCompare[major] && version[1] == versionCompare[minor] && version[2] <= versionCompare[release]);
				},
	
				expressInstall: 'expressInstall.swf',
	
				create: function (obj) {
					if (!$[flash].available || expressInstallIsActive || !typeof obj == 'object' || !obj.swf) {
						return false;
					}
	
					if (obj.hasVersion && !$[flash].hasVersion(obj.hasVersion)) {
						obj = {
							swf: obj.expressInstall || $[flash].expressInstall,
							attrs: {
								id: 'SWFObjectExprInst',
								height: Math.max(obj.height || 137),
								width: Math.max(obj.width || 214)
							},
							params: {
								flashvars: {
									MMredirectURL: location.href,
									MMplayerType: ($[flash].activeX) ? 'ActiveX': 'PlugIn',
									MMdoctitle: document.title.slice(0, 47) + ' - Flash Player Installation'
								}
							}
						};
	
						expressInstallIsActive = true;
					}
					else {
						obj = $.extend(
							true,
							{
								attrs: {
									height: obj.height || 180,
									width: obj.width || 320
								},
								params: {
									wmode: obj.wmode || 'opaque',
									flashvars: obj.flashvars
								}
							},
							obj
						);
					}
	
					return '<object ' + (
						createAttrs(obj.attrs)
					) + (
						$[flash].activeX ? ' classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000">' + '<param name="movie" value="' + obj.swf + '" />' : ' type="application/x-shockwave-flash" data="' + obj.swf + '">'
					) + (
						createParams(obj.params)
					) + '</object>';
				}
			};
		}());

	$.fn[flash] = function (args) {
		if (typeof args == 'object') { 
			this.each(
				function () {
					var test = document.createElement(flash);

					test.innerHTML = $[flash].create(args);

					if (test.childNodes[0]) {
						this.appendChild(test.childNodes[0]);
					}
				}
			);
		}
		else if (typeof args == 'function') {
			this.find('object').andSelf().filter('object').each(
				function () {
					var elem = this,
					jsInteractionTimeoutMs = 'jsInteractionTimeoutMs';

					elem[jsInteractionTimeoutMs] = elem[jsInteractionTimeoutMs] || 0;

					if (elem[jsInteractionTimeoutMs] < 660) {
						if (elem.clientWidth || elem.clientHeight) {
							args.call(this);
						}
						else {
							setTimeout(
								function () {
									$(elem)[flash](args);
								},
								elem[jsInteractionTimeoutMs] + 66
							);
						}
					}
				}
			);
		}

		return this;
	};
}(jQuery, 'flash'));
		
	
		
			
		
		$('.sized_image').live('hover',function(){
			$('[tooltip]').tooltip();
		});
		TZ.getImageDiv = function(imageId,divClass,dimension,format,scaleSize,alt){
			var profile = '';
			var dim = dimension;
			if(dimension === undefined) dim = 2500;
			if(format === undefined) format = "jpg";
			if(scaleSize === undefined) scaleSize = false;
			if(alt === undefined) alt = 'IMG';
			var rdim = dim;
			if (dim < 150){ 
				rdim = 25 * (Math.floor(dim/25) + 1)
			} else{
				rdim =  50 * (Math.floor(dim/50) + 1)	
			}
			var url;
			if(imageId[0] == '@') profile = 'profile/';
			if(rdim > 1000) url = "http://starstyler.gala.de/images/"+ profile + imageId + ".jpg";
			else url = "http://starstyler.gala.de/images/" + profile + "R"+ rdim + "H"+ rdim + "W/" + imageId + "." + format;
			var style;
			if(dimension === undefined || scaleSize == true) style = '';
			else style = "style='width:" + dim + "px;height:" + dim + "px;'";
			var x = "<div class='sized_image " + divClass + "'" + style + ">";
				x += "<img name='" + imageId + "' class='inner_photo' alt='"+ alt +"' src='" + url + "' style='width:100%;height:100%;border:0;'/>";
			x += "</div>";
			return $(x);
		};
		TZ.getImageToolTip = function(imageId){
			var profile = '';
			var dim = 500;
			var format = "jpg";
			var scaleSize = false;
			var alt = 'IMG';
			var rdim = dim;
			if (dim < 150){ 
				rdim = 25 * (Math.floor(dim/25) + 1)
			} else{
				rdim =  50 * (Math.floor(dim/50) + 1)	
			}
			var url;
			if(imageId[0] == '@') profile = 'profile/';
			if(rdim > 1000) url = "http://starstyler.gala.de/images/"+ profile + imageId + ".jpg";
			else url = "http://starstyler.gala.de/images/" + profile + "R"+ rdim + "H"+ rdim + "W/" + imageId + "." + format;
			
			return $("<div data-applytimeout='250' data-isimagetooltip='true' style='width: 500px; padding: 3px 0px'><img class='medium' src='" + url + "' width='500px' style='float:left'></div>");
		};
	
	
		
	
		
			TZ.needsFlash = {
	show: function() {
		$('body').prepend($("#needFlashAdProtector, .needFlash").slideDown("slow"));
	},
	hide: function() {
		$('.needFlash, #needFlashAdProtector').slideUp('slow');
	}
};
		
	
		
			/*
 * TimeSince
 * 	Converts a JS date into a string like:
 * 	- 7 seconds ago
 * 	- about a minute ago
 * 	- 12 minutes ago
 * 	- about 25 minutes ago
 * 	- 2 days and 14 hours ago
 * 	- 2 months ago
 * 	- 25 minutes from now
 * 	- nearly a year from now
 *	
 * Brent (2011-04-07)
 */

TZ.time = {
	ABOUT_STR : "",
	YESTERDAY: "yesterday",		// set to null to turn off yesterday display
	
	UNITS_MINUTES:1,
	UNITS_HOURS:  2,
	UNITS_DAYS:   3,
	UNITS_WEEKS:  4,
	UNITS_MONTHS: 5,
	UNITS_YEARS:  6,
	UNITS_MAX:    6,
	unitStr:  ["second", "minute", "hour", "day", "week", "month", "year"],
	unitSecs: [1, 60, 3600, 24 * 3600, 24 * 3600 * 7, 30 * 24 * 3600, 365.25 * 24 * 3600],
	
	monthNameAbbr: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
	
	percentAbout : .10,			// used to find initial units, as well as to label as "about"
	_localUtcCorrection : 0,	// updated when available after RPCReady
	
	localDate: function(serverTimestamp){
		return new Date((serverTimestamp + TZ.time._localUtcCorrection) * 1000);
	},
	serverTimestamp: function(date){
		return Math.floor(date.getTime()/1000 - TZ.time._localUtcCorrection);
	},
	
	toUnitsStr: function (val, units) {
		var pl = ''
		if (val != 1)
			pl = 's'
		else
			val = (units == TZ.time.UNITS_HOURS) ? "an" : "a"
		
		return val + " " + TZ.time.unitStr[units] + pl
	},
	
	toString: function (val, units, extra, isFuture) {
		var self = TZ.time;
		var agoStr = '';
		
		if (units == self.UNITS_DAYS && val == 1 && self.YESTERDAY)
			agoStr = self.YESTERDAY;
		else
			agoStr = self.toUnitsStr(val, units) + extra + (isFuture ? " from now" : " ago")
		
		return agoStr
	}
}

/** 
 * Local clock correction (if not synchronized exactly with a time server).
 */
TZ.events.addEventListener("RPCReady", function(){
	function updateTimeCorrection () {
		TZ.rpc.serverTimeOffset(function (localUtcCorrection) { 
			TZ.time._localUtcCorrection = localUtcCorrection 
		});
	}
	
	setInterval(updateTimeCorrection, 60000);	/* sync #secs offset with server every minute */
	updateTimeCorrection();		// sync now
})

/**
 * timeSince
 * 
 * @param pPartialThreshold - a unit threshold: 0=disable, all units >= threshold may be split into their next-lower units
 * 								(3="X days and Y minutes", as well as, "X years and Y months") 
 * @param pDisplayDateThreshold - display date, instead of "time ago" if > 0 && <= UNITS_MAX
 */
TZ.time.timeSince = function (pDate, pRoundMinutesGreaterThan, pDisplayApprox, pPartialThreshold, pDisplayDateThreshold) {
	Debug.assert(pDate, "missing TimeSince timestamp!", "brentf")
	if (pPartialThreshold == 0 || typeof(pPartialThreshold)  == 'undefined')
		pPartialThreshold = TZ.time.UNITS_MAX + 1;
	else
		pPartialThreshold = Math.max(pPartialThreshold, TZ.time.UNITS_MINUTES)
	var doRoundMinutes = typeof(pRoundMinutesGreaterThan) != "undefined"
	if (typeof(pDisplayDateThreshold)  == 'undefined' || pDisplayDateThreshold == 0)
		pDisplayDateThreshold = TZ.time.UNITS_MAX + 1
	
	//
	// Delta from original UTC timestamp
	//
	var now = new Date()
	var utcOffset = now.getTimezoneOffset() * 60	// offset in seconds
	var secs = ((now - pDate) / 1000.0) | 0
	if (!pDate)
		return ""	// not ready, yet, for some reason
	//Debug.log("Time Zone Offset = {0}, timestamp={1}".format(utcOffset/3600, pDate.getTime()));
	//Debug.log('TimeSince: pDate = ' + pDate + ', minutes = ' + (secs/60));
	
	//
	// Future
	//
	var isFuture = false
	if (secs < 0)	// account for truncation above, so don't get "1 second from now" when should be "0 secs"
	{
		secs *= -1
		utcOffset *= -1
		isFuture = true
		//Debug.error("Future Value in Comments! secs=" + secs + " utcOffset=" + utcOffset + " delta=" + (secs - utcOffset), "brentf")
	}
	//secs -= utcOffset
	
	// secs = (2*24*3600) + (14*3600)	// DEBUG
	
	//
	// Units - find largest units that fit
	//
	var units = TZ.time.unitSecs.length - 1
	var m = (1- TZ.time.percentAbout)		// use next unit value, if close
	while (secs < m*TZ.time.unitSecs[units] && units > 0)
		--units
	var exactVal = secs / TZ.time.unitSecs[units]
	var val = Math.round(exactVal)
	
	//
	// Date - convert to date if > threshold
	//	20 May
	//	20 May 2010 (show year if not this year)
	//
	if (units >= pDisplayDateThreshold)
	{
		var sep = ' '
		var dateStr = pDate.getDate() + sep + TZ.time.monthNameAbbr[pDate.getMonth()]
		if (pDate.getFullYear() != now.getFullYear())
			dateStr = dateStr + sep + pDate.getFullYear()
		return dateStr;
	}
	
	// Minute Rounding (if > 15 mintes)
	if (units == TZ.time.UNITS_MINUTES && doRoundMinutes && exactVal > pRoundMinutesGreaterThan) {
		val = Math.floor((4 + exactVal) / 5) * 5
		if (val >= 60) {
			val = 1
			++units
		}
	}
	
	// Split/Inexact ("X days and Y hours" or "about X minutes")
	var approx = '',
	    extra = '',
	    frac = exactVal - (exactVal|0),
	    fracAbs = Math.abs(exactVal - val);	// dist up or down, depending upon Round of val above
	
	if (fracAbs >= TZ.time.percentAbout) {
		if (fracAbs > .20 && units >= pPartialThreshold) {	// split
			val = exactVal | 0; 	// truncate, rather than use rounded value
			var extraVal = Math.round(frac * TZ.time.unitSecs[units] / TZ.time.unitSecs[units - 1]);
			extra = " and " + TZ.time.toUnitsStr(extraVal, units - 1);
		}
		else if (pDisplayApprox)
			approx = TZ.time.ABOUT_STR;
	}
	
	var agoStr = TZ.time.toString(val, units, extra, isFuture);
	if (agoStr != TZ.time.YESTERDAY)
		agoStr = approx + agoStr
	//Debug.info(agoStr + " <-- " + secs, "brentf")
	
	return agoStr;
}
	

		
	
		
			
		
		//
		// Setup
		//
		listenerId = TZ.events.addEventListener ("RPCReady", function () {
			TZ.events.removeEventListener("RPCReady", listenerId);
			Debug.note("widget.TimeSince SETUP", "brentf", "green");
			
            setInterval(function(){ timesince_update15('timesince-15') }, 15000);	// 15 sec updates
            setInterval(timesince_update5m, 5*60000);								// 5 min updates
            setInterval(timesince_update1hr, 60*60000);								// every hour
		});
		
		//
		// Initialization
		//
		function timesince_init () {
			var srcClass = 'timesince-init';
			$('.'+srcClass).each$(function(i, e) {
	        	var $e = this,
	        	    date = timesince_getDate($e),
	        	    secs = ((new Date() - date) / 1000.0) | 0,
	        	    mins = secs / 60;
				
				// Set initial content before timer kicks in
				$e.html(TZ.time.timeSince(date, 15, false, 0, TZ.time.UNITS_MONTHS));
				
				// Remove init, add update every X seconds
				var dstClass = 'timesince-15';
				if (mins >= 60)
					dstClass = 'timesince-1hr';
				else if (mins >= 5)
					dstClass = 'timesince-5m';	
				$e.switchClass(srcClass, dstClass);	// remove init, add update every X seconds
			});
		};
		
		//
		// Updates
		//	The following three should use a common function. Got them close to becoming one.
		//
		
		function timesince_update15 (srcClass) {
	        timesince_init();	// pick up any new ones, if this function wasn't called explicitly by user of timesince tags
	        
	        $('.'+srcClass).each$(function(index, e) {
	        	var $e = $(e),
	        	    date = timesince_getDate($e),
	        	    secs = ((new Date() - date) / 1000.0) | 0,
	        	    mins = secs / 60;
	        	 
				$e.html(TZ.time.timeSince(date, 15, false, 0, TZ.time.UNITS_MONTHS));
				
				// Adjust update time by changing classes	
				if (mins >= 60)
					$e.switchClass(srcClass, 'timesince-1hr');
				else if (mins >= 5)
					$e.switchClass(srcClass, 'timesince-5m');	// remove init, add update every 15 seconds
			});
        };
		function timesince_update5m () {
	        $('span.timesince-5m').each$(function(index, e) {
	        	var $e = $(e),
	        	    date = timesince_getDate($e),
	        	    secs = ((new Date() - date) / 1000.0) | 0,
	        	    mins = secs / 60;
	        	 
				$e.html(TZ.time.timeSince(date, 15, false, 0, TZ.time.UNITS_MONTHS));
				
				// Adjust update time by changing classes	
				if (mins >= 60)
					$e.switchClass('timesince-5m', 'timesince-1hr');
			});
		};
		function timesince_update1hr () {
	        $('span.timesince-1hr').each$(function(index, e) {
	        	var $e = $(e),
	        	    date = timesince_getDate($e);
	        	 
				$e.html(TZ.time.timeSince(date, 15, false, 0, TZ.time.UNITS_MONTHS));
			});
		};
		
		/**
		 * Date accessor caches date in a jQuery data element for faster access
		 */
		function timesince_getDate ($e) {
			var date = $e.data('timestamp');
			if (!date) {	// cache initially, and re-cache if disappeared (can happen if element is moved out of the DOM temporarily)
				date = TZ.time.localDate(parseInt($e.data('utime')));
				$e.data('timestamp', date);
			}
			return date;
		};
		
	
	
		
	
		
			
		
	$.fn.checkBoxVal = function(value) {
		if (value != null) {
			if(value)
				$(this).addClass('checkbox_button_selected');
			else
				$(this).removeClass('checkbox_button_selected');
		} else {
			return $(this).hasClass('checkbox_button_selected');
		}
	};
	
	$('.checkbox_button').live('click',function() {
		if ($(this).hasClass('checkbox_button_selected')) {$(this).removeClass('checkbox_button_selected');$(this).removeClass('checkbox_button_selected_hover');}
		else $(this).addClass('checkbox_button_selected');
		TZ.events.dispatchEvent('content_changed',this);
	});	
	
	$('.checkbox_button').live('mouseenter',function() {
			if ($(this).hasClass('checkbox_button_selected')) $(this).addClass('checkbox_button_selected_hover');
			else $(this).addClass('checkbox_button_hover');
	});
	$('.checkbox_button').live('mouseleave',function() {
			$(this).removeClass('checkbox_button_selected_hover');
			$(this).removeClass('checkbox_button_hover');
	});

	
		
	
		
			
		
		$(function () {
			TZ.rpc.setup();
		});
	
	
		
	
		
			/*
 * TZTrendsRotator 
 *	This class has a corresponding erb called TrendBar, styling applies to both
 */
TZTrendsRotator = function(itemLength) {
  this.init(itemLength);
}

$.extend(TZTrendsRotator.prototype, {
	itemLength:0,
	count:0,
	allTrends:[],
	spinTimer:null,
	init:function (itemLength) {
		var instance = this;
		this.itemLength = itemLength;
		this.count = Math.floor( 1000 / this.itemLength);
		var sortPos = 0;
		var trend_count = $('.trend_item').length;
		for(var i=0;i<trend_count;i++){	
			$('.trend_item,' + '#trend_scroll_section [style*="left: '+ sortPos + 'px;"]' ).each( function( index, el) {
				instance.allTrends.push(el);		
				$(el).detach();
			});
			sortPos += this.itemLength;
		}
		$('#trending_bar .right_btn').click( function() { instance.resetSpinner(); instance.scrollTrends(1); });
		$('#trending_bar .left_btn').click( function() { instance.resetSpinner(); instance.scrollTrends(-1); });
		this.scrollTrends(1);
		this.resetSpinner();
	},
	getPositions:function() {
		var rtn = { 
			min:this.itemLength * this.count,
			max:-this.itemLength
		};
		$('.trend_item').each( function( index, el) {
			$(el).stop();
			var left = $(el).position().left;
			if (left < rtn.min) rtn.min =left;
			if (left > rtn.max) rtn.max = left;
		});
		return rtn;
	},
	removeExtras:function() {
		var instance = this;
		$('.trend_item').stop( false, false);	
		$('.trend_item').each(function( item, el) {
			var pos = $(el).position();
			if (pos.left < 0 || pos.left > (instance.itemLength * (instance.count))) {
				$(el).remove();
			}
		});
	},
	addTopics:function(amount){
		var el;
		var pos;
		var i;
		if(amount < 0){
			for(i=0;i<this.count*2;i++){
				el = this.allTrends.pop();
				if(el) this.allTrends.unshift(el);
			}
		}	
		p = this.getPositions();
		for(i=1;i<=this.count;i++){
			el = this.allTrends.shift();
			if(!el) return;
			this.allTrends.push(el); 
			pos = (amount > 0) ? p.max+this.itemLength*i : p.min-this.itemLength*(this.count-i+1);
			$(el).css('left',pos+"px");
			var cel = $(el).clone();
			$(cel).appendTo("#trend_scroll_section");
			$(cel).click(function() { 
				//$(cel).animate({ 'backgroundColor':'#d3eaf0' },50).animate({ 'backgroundColor':'#000' },650);
			});
		}
	},
	scrollTrends:function( amount) {
		this.removeExtras();
		this.addTopics(amount);	
		/* first build a proper offset list*/
		var dist = (amount > 0) ? (this.getPositions().max - (this.itemLength * (this.count-1))) : this.getPositions().min;
		$('.trend_item,.trend_item_show').animate({left:"-=" + dist +"px"},1000);
	},
	spinIt:function() {
		$('#trending_bar .right_btn').click();
	},
	resetSpinner:function() {
		try { clearTimeout(this.spinTimer); } catch (arg) {}
		this.spinTimer = setTimeout(this.spinIt ,30000);
	}
});
		
	
		
			
		
	$(function(){
		TZ.trendsR = new TZTrendsRotator(210);
	});

	
		
	
		
			TZ.follow = {
	// This manager works against objects with class 'follow_elem'
	// Each such element should have data-account_id='@foo' defined, where foo is the accountId of the user to follow
	// TODO: split out the element view code from the model logic here
	// timm
	isSetup:false,
	following:null,//hash of followed names, key is the account name 
	callingRPC:false,
	setup: function(){
		if(TZ.follow.following) TZ.follow.setupEach();//use cache if available
		else {
			if(TZ.rpc.loggedIn) {
				TZ.follow.callingRPC= true; // otherwise run RPC and cache results
				TZ.rpc.createRPC("GetMyFollows",{},function(followedAccountIds) {
					TZ.follow.callingRPC= false;
					TZ.follow.following = {};
					$.each(followedAccountIds,function(index,value){
						TZ.follow.following[value] = true;
					})
					TZ.follow.setupEach(); // got results now process
				}, function(errorObject) {
					TZ.follow.callingRPC= false;
				});
			}			
		}
		if(TZ.follow.isSetup) return;
		TZ.follow.isSetup = true;
		TZ.events.addEventListener("loginStatusChange",function(){
			if(TZ.rpc.accountId == '') {
			  TZ.follow.following = {}; //we are logged out so no one is followed
			  TZ.follow.setup();
			}
			else {
				TZ.follow.following = null;// setting to null forces the rpc to rerun
				TZ.follow.setup();
			}
		});
	},
	setupEach: function(){
		$('.follow_elem').each(function(element) {
			TZ.follow.prepare($(this));
		});
	},
	prepare: function(followButton) {
		var accountId = TZ.follow.getId(followButton);
		if(!accountId) return;
		if (accountId != TZ.rpc.accountId) {
			TZ.follow.checkFollowing(followButton);
		} else {
			$(followButton).hide();
			$(followButton).unbind();
		}
	},
	getId: function(followButton){
		var accountId = $(followButton).data("account_id");
		if (accountId == undefined) {
			Debug.log("error cannot follow without an accountId", "jonchristensen");
			return '';
		}
		return accountId;
	},
	checkFollowing: function(followButton) {
		if(TZ.follow.isFollowing(followButton)) TZ.follow.showUnfollowButton(followButton);
		else TZ.follow.showFollowButton(followButton);
	},
	isFollowing:function(followButton){
		var accountId = TZ.follow.getId(followButton);
		return (TZ.follow.following[accountId] == true)? true: false;
	},
	showFollowButton: function(followButton) {
		$(followButton).text("");
		$(followButton).removeClass("following");
		$(followButton).show();
		$(followButton).unbind();
		$(followButton).click(function() {
			TZ.follow.checkSignedIn(this); return false;
		});
	}, 
	showUnfollowButton: function(followButton) {
		$(followButton).addClass("following");
		$(followButton).show();
		$(followButton).unbind();
		$(followButton).click(function() {
			TZ.follow.unfollow(followButton); return false;
		});
	},
	checkSignedIn: function(followButton) {
		if (TZ.rpc.username != "") TZ.follow.follow(followButton);
		else TZ.popupHandler.loadSignInRequiredPopup();
	},
	follow: function(followButton) {
		if(TZ.rpc.loggedIn) {
			var accountId = TZ.follow.getId(followButton);
			TZ.rpc.createRPC("FollowAccount",{accountId:accountId},function(responseObj) {
				TZ.follow.showUnfollowButton(followButton);
				TZ.follow.following[accountId] = true;
				TZ.follow.setup();//force refresh
			}, function(errorObject) {
			});
		} else
		{
			TZ.popupHandler.loadSignInRequiredPopup();
		}
	}, 
	unfollow: function(followButton) {
		var accountId = TZ.follow.getId(followButton);
		TZ.rpc.createRPC("UnfollowAccount",{accountId:accountId},function(responseObj) {
			TZ.follow.showFollowButton(followButton);
			delete TZ.follow.following[accountId];
			TZ.follow.setup();//force refresh
			}, function(errorObject) {
		});
	}
};
TZ.follow.setup();
		
	
		
			
		
		$('.gallery_row_item').live('click',function(e){
												if(e.altKey) {
													var currentTopicId = $(this).data('topicid');
													TZ.popupHandler.loadTopicManagementPopup(currentTopicId);
												}
											}
									);
	
	
		
	
		
			
		

$.fn.simpleDropDownVal = function(value) {
	if (value != null) {
		//$(this).find('p').text(value);
		//$(this).find('p').attr('name',value);
	} else {
		var val = $(this).find('p').text();
		if (val.toUpperCase() == 'SELECT ONE')
			val = "";
		return val;
	}
};

if (!TZ.simpleDropDown)
{
	TZ.simpleDropDown = {
		setUp : function() {
			$('div.simple_drop_down').live('click',
				function() {
					$(this).toggleClass("ssd_selected");
					$(this).toggleClass("dropdown_shadow");	// breaks in IE - Brent & Satya 2011-04-17
					if ($(this).hasClass("ssd_selected")) 
						$('.options_selected', this).show();
					else 
						$('.options_selected', this).hide();
					
					$(this).addClass("ssd_hover");
				}
			);
			
			
			$('div.simple_drop_down').live('mouseenter',
				function() {
					$(this).addClass("ssd_hover");
				}	
			);
			
			$('div.simple_drop_down').live('mouseleave',
				function() {
					$(this).removeClass("ssd_hover");
					$('div.simple_drop_down').removeClass("ssd_selected");
					$('div.simple_drop_down').removeClass("dropdown_shadow");
					$('.options_selected', this).hide();
				}
			);
			
		},
		
		value : function (id) {
			var val = $(id + ' p').text();
			if (val.toUpperCase() == 'SELECT ONE')
				{	
					val = "";
				}
			return val;
		},
		name : function (id) {
			var val = $(id + ' p').attr('name');
			return val;
		},
		reset: function(id){
			$(id).find('p').text('SELECT ONE');
			$(id).find('p').attr('name','');
			TZ.events.dispatchEvent($(id).data('changeHandler'),$(id).find('p'));
		},
		changeValue: function(id, value) {
			$(id + ' p').text($("#dropdown_" + value).text());
			$(id + ' p').attr('name',value);
		}
	}
	
	TZ.simpleDropDown.setUp();
}

	
		
	
		
			
		
		/*
		 * taaz.messageBubble
		 *
		 * Depends:
		 *	jquery.ui.core.js
		 *	jquery.ui.widget.js
		 */
		(function( $ ) {
		'use strict';
		$.widget( "taaz.messageBubble", {
			options: {
				direction: 'left',
				bodyClass: ''
			},
			_create: function() {
				this.body = $("<div class='taaz-ui-messageBubble-body'></div>").addClass(this.options.bodyClass);
				this.body.append(this.element.contents());
				this.element.append("<div class='arrow'></div>")
				            .append("<div class='taaz-ui-messageBubble-head'><div></div></div>")
				            .append(this.body)
				            .append("<div class='taaz-ui-messageBubble-foot'><div></div></div>");
				this.element.addClass('taaz-ui-messageBubble');
				this._setOption( "direction", this.options.direction );
			},
		
			widget: function() {
				return this.element;
			},
		
			destroy: function() {
				var originalContents = this.body.contents();
				this.element.empty().append(originalContents);
				$.Widget.prototype.destroy.call( this );
			},
		
			_setOption: function( key, value ) {
				$.Widget.prototype._setOption.apply( this, arguments );
				if ( key === "direction" ) {
					this.element.removeClass('taaz-ui-messageBubble-direction-left taaz-ui-messageBubble-direction-right').addClass('taaz-ui-messageBubble-direction-'+value);
				}
			}
		});
		
		}( jQuery ) );
	
	
		
	
		
			
		
		(function ($) {
			$.fn.checked = function() {
				
			return $(this).is(":checked")
		  };
		})(jQuery);		
		
		(function($){
		$.widget("taaz.checkbox", {
			options : {
				checkboxChecked: "check",
				checkboxUnchecked: "empty",
				radioChecked: "radioCheck",
				radioUnchecked: "radioEmpty"
			},
			
			_create: function() {
				var self = this;
		
				if (!self.element.is(":radio,:checkbox")) return false;
		
				if (self.element.is(":radio")) self.radioGroup = $("input:radio[name='"+self.element.attr('name')+"']")
				else self.radioGroup = false;
			
				self.element.wrap('<span></span>');		// put hidden input element and following label in here
				self.wrapper = self.element.parent();
				self.wrapper.addClass('taaz-ui-state-default').addClass("taaz-ui-checkbox");
		
				var e = self.element, w = self.wrapper;
				function onMouseOut()  { if (!e.is(':disabled')) w.removeClass("taaz-ui-state-hover")  }
				function onMouseOver() { if (!e.is(':disabled')) w.addClass("taaz-ui-state-hover")     }
				function onMouseDown() { if (!e.is(':disabled')) w.addClass("taaz-ui-state-active")    }
				function onMouseUp()   { if (!e.is(':disabled')) w.removeClass("taaz-ui-state-active") }
				function onBlur()      { if (!e.is(':disabled')) w.removeClass("taaz-ui-state-focus")  }
				function onFocus() {
					if (!e.is(':disabled')) {
						if (self.radioGroup) self.radioGroup.not(self.element).removeClass("taaz-ui-state-focus");
						w.addClass("taaz-ui-state-focus");
					}
				}
				
				function updateState(evt) {
					self.wrapper[self.element.is(':focus') ? 'addClass' : 'removeClass']('taaz-ui-state-active');
					if (self.radioGroup) {
						self.radioGroup.each(function(i,el) {
							var checkState = el.checked;
							$(el).parent().addClass(self.iconClass(checkState)).removeClass(self.iconClass(!checkState));
						});
					} else {
						var checkState = self.element[0].checked;
						self.wrapper.addClass(self.iconClass(checkState)).removeClass(self.iconClass(!checkState));
					}
					if (evt) evt.stopPropagation();
				}
				
				function initState() {
					self.wrapper[self.element.is(':focus') ? 'addClass' : 'removeClass']('taaz-ui-state-active');
					var checkState = self.element[0].checked;
					self.wrapper.addClass(self.iconClass(checkState)).removeClass(self.iconClass(!checkState));
				}
				
				function iconClick() {
					if (!e.is(':disabled')) {
						self.element[0].checked = !self.element[0].checked;
						self.element.triggerHandler('click');
					}
				}
				
				initState();
				
				self.wrapper.click(iconClick).hover(onMouseOver,onMouseOut).mousedown(onMouseDown).mouseup(onMouseUp);
				self.element.click(updateState).focus(onFocus).blur(onBlur);
				self._setOption('disabled',self.element.is(':disabled'));
			},
			check: function() {
				if (!this.element[0].checked) this.wrapper.click();
			},
			uncheck: function() {
				if (this.element[0].checked) this.wrapper.click();
			},
			checked: function(bool) {
				if (bool === undefined) return this.element[0].checked
				bool ? this.check() : this.uncheck();
			},
			destroy: function() {
				//unbind events?
				this.wrapper.replaceWith(this.element);
				$.Widget.prototype.destroy.apply(this, arguments);
			},
		
			_setOption: function(key, value) {
				$.Widget.prototype._setOption.apply(this, arguments);
		
				if (key === "disabled") {
					if (value) {
						this.element.attr("disabled","disabled");
						this.wrapper.removeClass("taaz-ui-state-focus taaz-ui-state-hover taaz-ui-state-active");
						this.wrapper.addClass('taaz-ui-checkbox-disabled taaz-ui-state-disabled')
					} else {
						this.element.removeAttr("disabled");
						this.wrapper.removeClass('taaz-ui-checkbox-disabled taaz-ui-state-disabled')
					}
				}
			},
		
			iconClass: function(state) {
				return this.radioGroup ?
					"taaz-ui-icon-" + this.options[state?"radioChecked":"radioUnchecked"] :
					"taaz-ui-icon-" + this.options[state?"checkboxChecked":"checkboxUnchecked"];
			}
		});
		
		})(jQuery);
	
	
		
	
		
			
			
		/*
		 * taaz.spinner
		 *
		 * Depends:
		 *	jquery.ui.core.js
		 *	jquery.ui.widget.js
		 */
		(function( $ ) {
		'use strict';
		$.widget( "taaz.spinner", {
			options: {
				message         : '',
				duration        : -1,
				overlay         : true,
				eventName       : undefined,
				callback        : function(){},
				timeoutCallback : function(){},
				progressMessageInterval : 6000,
				progressMessages: [
				   'Waiting...',
				   'Still waiting...',
				   'Tick tock...',
				   'Please check your internet connection.'
				]
			},
			//Init
			_create: function() {
				this._enable();
			},
			//Public
			
			isSpinning : function() {
				return this.options.spinning;
			},
			stop:function(message,callback) {
				if (message) {
					this._setOption('message',message);
					this._setOption('callback',callback || this.options.callback);
					this._setOption('duration',1500);
				} else {
					this.destroy();
				}
			},
			success:function(message,callback) {
				this.stop(message,callback);
				return this._trackTime('success');
			},
			failure:function(message,callback) {
				this.stop(message,callback);
				return this._trackTime('failure');
			},
			widget: function() {
				return this.element;
			},
		
			destroy: function() {
				this._disable();
				$.Widget.prototype.destroy.call( this );
			},
		
			// Private;
			_enable: function() {
				this.progressMessageIndex = -1;
				this.timeStart = +new Date();
				
				this._setOption('spinning',true);
				this.spinnerElement = $('<div class="taaz-ui-spinner"><div class="taaz-ui-spinner-wheel"><div></div></div></div>');
				if (this.options.overlay) this.spinnerElement.prepend('<div class="taaz-ui-spinner-overlay"></div>');
				this.messageElement = $('<div></div>').addClass('taaz-ui-spinner-message');
				this.spinnerElement.append(this.messageElement);
				this.element.data('taaz-ui-spinner-oldPosition',this.element.css('position'));
				this.element.addClass('taaz-ui-spinner-container');
				this.element.append(this.spinnerElement);
				this._setOption( "duration", this.options.duration );
				this._setOption( "message", this.options.message );
				this._setOption( "eventName", this.options.eventName );
				this._progressMessage();
			},
			_disable: function() {
				this._setOption('spinning',false);
				this.spinnerElement.remove();
				this.element.removeClass('taaz-ui-spinner-container');
				this._callback();
			},
			_trackTime: function(label) {
				this.timeEnd = +new Date();
				this.timeSpent = this.timeEnd - this.timeStart;
				return this.timeSpent;
			},
			_progressMessage: function() {
				var self = this, o = self.options;
				setTimeout(function(){
					// If we are no longer spinning, return
					if (!self.isSpinning()) return
					// If our message was set by the user, not by this method, return
					if (o.message instanceof jQuery ||  
							o.message &&
							o.message.length > 0 &&
							o.message != o.progressMessages[self.progressMessageIndex]
					) return;
					// If we are out of progress messages, call our timeoutCallback
					if (!o.progressMessages[++self.progressMessageIndex]) return self._timeoutCallback();
		   
					self._setOption('message', o.progressMessages[self.progressMessageIndex]);
					self._progressMessage()
				},o.progressMessageInterval)
			},
			_callback: function() {
				return this.options.callback.apply(this.element,[this._trackTime()]);
			},
			_timeoutCallback: function() {
				return this.options.timeoutCallback.apply(this.element,[this._trackTime('timeout')]);
			},
			_setOption: function( key, value ) {
				var self = this;
				if ( key === "duration" ) {
					if (value > 0) setTimeout(function(){self._disable();self._callback},value);
				} else if ( key === "message" ) {
					if (value === '' || value == undefined) {
						this.messageElement.html('');
					} else if (typeof value === "string") {
						this.messageElement.html("<span class='taaz-ui-spinner-defaultMessage'>"+value+"</span>");
					} else {
						//jQuery object, hopefully. Otherwise, boom!
						this.messageElement.empty().append(value);
					}
				} else if ( key === 'eventName') {
					value = value || this.element.attr('id') || 'untitled';
				}
				$.Widget.prototype._setOption.apply( this, [key,value] );
			}
		});
		
		}( jQuery ) );
	
	
		
	
		
			
		
		$('.email_share').live('click',function(){		
			email_button$=$(this);
			var email_url="email_popup.html?type="+ email_button$.data('type') + "&topicId=" + email_button$.data('topicid') + "&shareUrl=" + email_button$.data('shareurl');
			if (email_button$.data('subject').length>0) {
				email_url=email_url + "&subject=" + email_button$.data('subject');
			}			
			if (email_button$.data('subcategory')!='') {
				var email_url=email_url + "&subcategory="+email_button$.data('subcategory');
			}

			if (email_button$.data('message')!='') {
				var email_url=email_url + "&message="+email_button$.data('message');
			}
 			DynamicContentLoader.createPopup("emailPopup",email_url , true, 410, 440);
		});
		$('#print_share').click(function(){
			DynamicContentLoader.createPopup("print_undefined_popup", "print_popup.html?type=undefined&topicId=&width=700&minHeight=400&maxHeight=800", true, 700, 650);
		});
		/*
		if ('true' == $('div.left_share_bar div.share_bar').attr('scrollFixed')) {
			$(document).scroll(function(){
				var top = $(document).scrollTop();
				(top >= 280)? $('.left_share_bar').addClass('fixed_at_28x80') : $('.left_share_bar').removeClass('fixed_at_28x80');
			});
		}
		*/
	
	
		
	
		
			

TZ.attach = { 
	maxCaption:60,
	container: null,
	clickToAttach:function(container){
		//Debug.log("clickToAttach: " + $(container).attr("id"), 'igortz');
		TZ.attach.container = container;
		var group = $('[name="'+$(container).attr('name')+'"]');
		var currentContainerIndex = $(group).index($(container));
		var slots = this.getEmptySlots(group,currentContainerIndex);
		var imageHeight = $(container).height();

		TZ.popupHandler.loadAttachImagePopup(function(url,dataObject) {
			//Debug.log("loadAttachImagePopup", 'igortz');
			var currentContainer = slots[0];
			$(currentContainer).find('.attachItemDefault').hide();
			$(currentContainer).find('.attachItem').show();
			var image = TZ.getImageDiv(dataObject.objectId,'image_double_border',imageHeight,'jpg');
			$(currentContainer).find('.mainPhoto').remove();
			if ($(currentContainer).find('.mainPic').length > 0) {
				divImage = $("<div class='mainPhoto'></div>").tooltip({data:'Click to Change'});
				if ($(currentContainer).hasClass('attachContainer_large')) {
					divImage.css("right", "-2px");
				} else {
					divImage.css("right", "-5px");
				}
				image.find('img').before(divImage);
			}
			image.tooltip({data:"Click to Change"});
			if ($(container).hasClass('attachContainer_large')) {
				$(container).find('.attachItem').css('margin-right', "1px");
			} else {
				$(container).find('.attachItem').css('margin-right', "3px");
			}
			$(currentContainer).find('.attachItem .sized_image').remove();
			var imageContainer = $(currentContainer).find('.attachItem');
			image.appendTo(imageContainer);
			removePhotoLink = $("<a class='removePhoto'></a>").tooltip({data:"Click to Remove"});
			image.after(removePhotoLink);
			$(currentContainer).find('.removePhoto').show();
			$(currentContainer).data('dataObject',dataObject); 
			TZ.events.dispatchEvent('content_changed',this);
		});
	},
	
	
		clickToRemove:function(container){
			TZ.attach.resetAttach(container);
		},
		isMakeover: function(imageData) {
			if (imageData.imageId != imageData.objectId) {
				return true;
			} else {
				return false;
			}
		},
		resetAttach:function(container){
			$(container).removeData('dataObject');
			$(container).find('.inner_photo').remove();
			$(container).find('.attachItemDefault').show();
			$(container).find('.mainPhoto').remove();
			$(container).find('.removePhoto').remove();
			$(container).find('.attachItem').attr('style', null);
			$(container).find('.sized_image').remove();
			if ($(container).find('.mainPic').length > 0) {
				mainPhotoDiv = $("<div class='mainPhoto'></div>").tooltip({data:"Click to attach a photo or makeover"});
				$(container).find('.attachItem').append(mainPhotoDiv);
			}
		},
		
		resetGroupAttach: function(group){
			 $.each($('div.' + group), 
			 	function(index, value){
			 		TZ.attach.resetAttach(value);   
			 		});
		},
		getEmptySlots:function(group,offset)
		{
		 if(offset == undefined) offset = 0;
		 var slots = [];
		 if(typeof(group)=='string') group = 'div.' + group;
		 $.each($(group), 
		 	function(index, value){
		 	try{
			 	var imageId = $(value).data('dataObject').imageId;
			 	}
			 	catch(err){
			 		var imageId = undefined;
			 	}
			 	if((imageId == undefined  && index > offset ) || index == offset)
			 	{
			 		slots.push(value);
			 	}
		 	}
		 );
		 return slots;
		},
		count:function(group,offset){
			var count = 0;
			if(offset == undefined) offset = 0;
			if(typeof(group)=='string') group = 'div.' + group;
			var aGroup = $(group).slice(offset);
			$.each( aGroup, 
				 	function(index, value){
			 		try{
						var imageId = $(value).data('dataObject').imageId;
						}	catch(err){
							var imageId = undefined;
					 	}
						var caption = $(value).find('.textAreaComponent').textAreaVal();
						if(imageId != undefined || caption != '' && caption != undefined ){
							count += 1;
						} 
			 		}
			 		);
			 return count;
		},
		validateAttach:function(group,min){
			if (min == undefined) min = 1 
			var lengthErrors     = [],
				validationErrors = [];
			
			group = typeof group === 'string' ? $('div.' + group) : $(group);
			group.each(function(index, value){
				var el         = $(value),
					imageId    = el.data('dataObject') && el.data('dataObject').imageId,
					caption    = $.trim(el.find('.textAreaComponent').textAreaVal());
				
				if (!imageId && !caption) {
					validationErrors.push({
						field                 : el.find('.attachItemDefault'),
						errorMessage          : 'Please attach a caption or photo.',
						useMyMsgAsTooltipText : true
					},{
						field                 : el.find('textarea'),
						errorMessage          : 'Please attach a caption or photo.',
						useMyMsgAsTooltipText : true
					});
				}
				
				if (caption.length > TZ.attach.maxCaption) {
					lengthErrors.push({
						field        : el.find('textarea'),
						errorMessage : 'Please limit your caption to 60 characters. You have ' + caption.length + ' characters.',
						useMyMsgAsTooltipText : true
					});
				}
		 	});
			// 2 validation errors tracked per component
			var numOk = group.length - (validationErrors.length / 2);
			if (numOk < min) {
				return $.merge(validationErrors.slice(0,(min - numOk)*2),lengthErrors)
			} else {
				return lengthErrors
			} 
		 },
		value : function (id) {
			var val = $(id).data('dataObject');
			return val;
		},
		getGroupImages : function (group) {
			if(typeof(group)=='string') {
				group = 'div.' + group;
			}
			var ids = [];
			 $.each($(group), 
			 	function(index, value){
			 	try{
			 		var dataObject = $(value).data('dataObject')
				 	var imageId = dataObject.imageId;
				 	}
				 	catch(err){
				 		var imageId = undefined;
				 	}
				 	var caption = $(value).find('.textAreaComponent').textAreaVal();
				 	if(imageId != undefined || caption != '' && caption != undefined )
				 	{
				 		ids.push({index:index,dataObject:dataObject,caption:caption});
				 	}
			 	}
			 );
		 	return ids;
		},
		setFirstImage: function(group,image){
			container = $('div.' + group).eq(0);
			var dataObject = $(image).data('dataObject');
			if(dataObject != undefined){
				$(container).find('.attachItemDefault').hide();
				$(container).find('.attachItem').show();
				if ($(container).hasClass('attachContainer_large')) {
					$(container).find('.attachItem').css('margin-right', "1px");
				} else {
					$(container).find('.attachItem').css('margin-right', "3px");
				}
				Debug.log($(container).height(),'jpdiaz');
				var image = TZ.getImageDiv(dataObject.objectId,'',$(container).height(),'jpg');
				$(container).find('.attachItem .sized_image').remove();
				image.appendTo($(container).find('.attachItem'));
				removePhotoLink = $("<a class='removePhoto'></a>").tooltip({data:"Click to Remove"});
				image.after(removePhotoLink);
				$(container).data('dataObject',dataObject);
				$(container).find('.attachItem .removePhoto').show();
				image.addClass('image_double_border');
				$(container).find('.mainPhoto').remove();
				if ($(container).find('.mainPic').length > 0) {
					mainPhotoDiv = $("<div class='mainPhoto'></div>").tooltip({data:'Click to Change'});
					if ($(container).hasClass('attachContainer_large')) {
						mainPhotoDiv.css('style','right:-2px');
					} else {
						mainPhotoDiv.css('style','right:-8px');
					}
					image.find('img').before(mainPhotoDiv);
				}

			} else {	
				this.resetAttach(container);
			}
		},
		setGroupImages : function(group,images){
			TZ.attach.resetGroupAttach(group);
			if(typeof(group)=='string') group = 'div.' + group;
			$.each($(images), 
				function(index, value){
					var currentContainer = $(group).eq(index);
					$(currentContainer).find('.attachItemDefault').hide();
					$(currentContainer).find('.attachItem').show();
					var imageHeight = $(currentContainer).height();
					var image = TZ.getImageDiv(value.dataObject.objectId,'image_double_border',imageHeight,'jpg');
					$(currentContainer).find('.mainPhoto').remove();
					if ($(currentContainer).find('.mainPic').length > 0) {
						divImage = $("<div class='mainPhoto'></div>").tooltip({data:'Click to Change'});
						
						if ($(currentContainer).hasClass('attachContainer_large')) {
							divImage.css("right", "-2px");
						} else {
							divImage.css("right", "-5px");
						}
						image.find('img').before(divImage);
					}
					image.tooltip({data:"Click to Change"});
					if ($(currentContainer).hasClass('attachContainer_large')) {
						$(currentContainer).find('.attachItem').css('margin-right', "1px");
					} else {
						$(currentContainer).find('.attachItem').css('margin-right', "3px");
					}
					$(currentContainer).find('.attachItem .sized_image').remove();
					var imageContainer = $(currentContainer).find('.attachItem');
					image.appendTo(imageContainer);
					removePhotoLink = $("<a class='removePhoto'></a>").tooltip({data:"Click to Remove"});
					image.after(removePhotoLink);
					$(currentContainer).find('.removePhoto').show();
					$(currentContainer).data('dataObject',value.dataObject);
				})
		},
		setImage : function(currentContainer, dataObject, divClass){
			if(! dataObject.hasOwnProperty('objectId')) return; //Image Not Found.
			
			if (divClass == undefined) divClass = ''
			$(currentContainer).find('.attachItemDefault').hide();
			$(currentContainer).find('.attachItem').show();
			var imageHeight = $(currentContainer).height();
			var image = TZ.getImageDiv(dataObject.objectId,'image_double_border',imageHeight,'jpg');
			$(currentContainer).find('.mainPhoto').remove();
			if ($(currentContainer).find('.mainPic').length > 0) {
				divImage = $("<div class='mainPhoto'></div>").tooltip({data:'Click to Change'});
				
				if ($(currentContainer).hasClass('attachContainer_large')) {
					divImage.css("right", "-2px");
				} else {
					divImage.css("right", "-5px");
				}
				image.find('img').before(divImage);
			}
			image.tooltip({data:"Click to Change"});
			if ($(currentContainer).hasClass('attachContainer_large')) {
				$(currentContainer).find('.attachItem').css('margin-right', "1px");
			} else {
				$(currentContainer).find('.attachItem').css('margin-right', "3px");
			}
			$(currentContainer).find('.attachItem .sized_image').remove();
			var imageContainer = $(currentContainer).find('.attachItem');
			image.appendTo(imageContainer);
			removePhotoLink = $("<a class='removePhoto'></a>").tooltip({data:"Click to Remove"});
			image.after(removePhotoLink);
			$(currentContainer).find('.removePhoto').show();
			$(currentContainer).data('dataObject',dataObject);
		},
		setThumbImage :  function(currentContainer, thumbData, divClass){
			if(!thumbData.hasOwnProperty('value')) return; //Image Not Found.
			
			if (divClass == undefined) divClass = ''
			$(currentContainer).find('.attachItemDefault').hide();
			$(currentContainer).find('.attachItem').show();
			var imageHeight = $(currentContainer).height();
			var image = TZ.getImageDiv(thumbData.value,'image_double_border',imageHeight,'jpg');
			$(currentContainer).find('.mainPhoto').remove();
			if ($(currentContainer).find('.mainPic').length > 0) {
				divImage = $("<div class='mainPhoto'></div>").tooltip({data:'Click to Change'});
				
				if ($(currentContainer).hasClass('attachContainer_large')) {
					divImage.css("right", "-2px");
				} else {
					divImage.css("right", "-5px");
				}
				image.find('img').before(divImage); 
			}
			image.tooltip({data:"Click to Change"});
			if ($(currentContainer).hasClass('attachContainer_large')) {
				$(currentContainer).find('.attachItem').css('margin-right', "1px");
			} else {
				$(currentContainer).find('.attachItem').css('margin-right', "3px");
			}
			$(currentContainer).find('.attachItem .sized_image').remove();
			var imageContainer = $(currentContainer).find('.attachItem');
			image.appendTo(imageContainer);
			removePhotoLink = $("<a class='removePhoto'></a>").tooltip({data:"Click to Remove"});
			image.after(removePhotoLink);
			$(currentContainer).find('.removePhoto').show();
			$(currentContainer).data('dataObject',thumbData.dataObject);
		}
	}
		
	
		
			
		

	$('.bindAttach').live('click',
		function(){
			TZ.attach.clickToAttach($(this).parent(),parseInt($(this).parent().attr('max_images_allowed')));
		});

	$('.attachItem .removePhoto').live('click',
		function(){
			TZ.attach.clickToRemove($(this).parent().parent());
			$(this).hide();
		});
	
	$('.attachItem .image_double_border, .attachItem .mainPhoto').live('click',
		function(){
			TZ.attach.clickToAttach($(this).parent().parent(),parseInt($(this).parent().parent().attr('max_images_allowed')));
		});
	


	
		
	
		
			/**
 * Controls the Login / Registration link text in the status bar at the top of the window.
 * 
 * Re-displays the Registration dialog if user previously Connected to Facebook, but did not complete the Registration process.
 */
$(function() {
	$("#loginLink").click(TZ.LoginLinks.login);		
	$("#registerLink").click(TZ.LoginLinks.register);
	
	/**
	 * 1. Reveal the login/registration links once the Flash rpcWidget has loaded.
	 * 2. Set the link text depending upon the user's name, if logged in.
	 * 3. Display the Registration dialog, if the user connected with Facebook, but didn't complete registration.
	 */
	TZ.events.addEventListener("loginStatusChange",function() {
		$("#loginLink").removeAttr('href');
		$('#sign_in').css('visibility', 'visible');	/* show Registration links, now that the flash rpcWidget is loaded */
		
		//Debug.info('login_links: EVENT loginStatusChange intercepted.')
		if (TZ.rpc.username) {
			$("#loginLink").html(TZ.rpc.username);
			$("#loginLink").attr('href','profile/home/private/'+TZ.rpc.username+'.html');
			$("#registerLink").html("SIGN OUT");
		}
		else {
			$("#loginLink").html("SIGN IN");
			$("#registerLink").html("REGISTER");
			
			//
			// If the Registration process is incomplete, bring up the Registration dialog
			//
			if (!TZ.LoginLinks._performedRegistrationCheck) {
				TZ.LoginLinks._performedRegistrationCheck = true;
				
				TZ.LoginLinks.completeRegistrationCheck();
			}
		}
		//Debug.log('Exit login status event change.')
	});
});

TZ.LoginLinks = {
	login: function() {
		if (TZ.rpc.username)
			return;
		
		Analytics.event('SigninPopup', 'TopBarSignIn', TZ.request.section)
		TZ.LoginLinks.displayPopup(225, "showRegistration=0");
	},
	goToMyProfile: function() {
		if (TZ.rpc.username)
			{
				Analytics.event('goToMyProfile', 'goToMyProfile', TZ.request.section)
				window.location.replace('profile/home/private/' + TZ.rpc.username +'.html');
			}
	},
	register: function(extraParams) {
		if (TZ.rpc.username)
		{
			Analytics.event('SigninPopup', 'TopBarSignout', TZ.request.section);	// removed 2011-10-17 (event not wanted by Karina); put back 2012-01-06 - Karina requested a logout event
			TZ.rpc.invoke("logoutUser", null, function(o) {
				TZ.rpc.refreshLoginStatus();
			});
		}
		else
		{
			Analytics.event('RegistrationPopup', 'TopBarRegister', TZ.request.section)
			var urlParams = "showRegistration=1"
			if (extraParams)
				urlParams = urlParams + "&" + extraParams
			TZ.LoginLinks.displayPopup(315, urlParams);
		}	
	},
	
	/**
	 * Redisplay registration dialog if connected to Facebook, not logged in as another user, and not already registered.
	 * 
	 * @author Brent
	 */
	completeRegistrationCheck: function() {
		// If current (logged-in) Facebook user gave permission to Taaz, but did not complete the registration process
		//	(AND is not currently logged in as another user),
		// THEN display the Registration dialog
		//
    	if (TZ.social.Facebook._uid && !TZ.rpc.username) {	// not logged in
			Debug.log("completeRegistrationCheck: oAuthUserId = " + TZ.social.Facebook._uid );
					
			TZ.rpc.invoke("FindOAuthAccount", { provider: 'facebook', oAuthUserId: TZ.social.Facebook._uid }, function (oAuthRec) {
					oAuthRec.registered = (oAuthRec.taazAccount && oAuthRec.taazAccount.name);	// FIX: remove this line once Chandra adds it
					
					// Connected to Facebook account
					if (oAuthRec.registered) {	// returned hash is not empty if registration complete
						//User registered with Taaz and linked to Facebook. (user must click link to log in)
						Debug.log('	User registered with Taaz and linked to Facebook. (user must click link to log in). taazAccount =')
						Debug.log(oAuthRec.taazAccount)
					}
					else if (oAuthRec.taazAccount) {
		            	Debug.log('	User connected to facebook, but not registered with Taaz. oAuthRec = ', "brentf");		// DEBUG
		            	Debug.log(oAuthRec, "brentf");
						
						// Pop-up the Registration dialog to complete registration
						TZ.LoginLinks.register("completeRegistration=1")
					}
				},
				function() {
					Debug.log("  Didn't find OAuthAccount.", 'brentf')
				}
			);//FindOAuthAccount
    	}
	},
	
	displayPopup: function (height, queryStr) {
		if (!queryStr) queryStr = ''
		DynamicContentLoader.createPopup("loginPopupWindow", "login_popup.html?" + queryStr, true, 623, height);
	}
};

		
	
		
			
		
			
			TZ.ImageReflected = {
			setUp : function(id,options)
				{
					//Reflection parameters
					var opt = options?options:{ opacity: 0.4,height: 20};						
					$('.reflected_image img[name='+id+']').reflect(opt);
				}
			};
	
	
		
	
		
			TZ.emailVerification = {
	email: null,
	needsVerification: function() {
		if (TZ.rpc.loggedIn) {
			TZ.rpc.invoke("GetAccountDetails", {accountId:TZ.rpc.accountId}, function (responseObject) {
				if (responseObject.emailVerified != undefined && !responseObject.emailVerified) {
					TZ.emailVerification.email = responseObject.email;
					TZ.user.getUserState('verificationBarHidden', function(value) {
							if (!value)
								TZ.emailVerification.showVerificationBar();
						},
						function (e) {	// key not found
							TZ.emailVerification.showVerificationBar();
						}
					);
				}
			}, function(error) {
				Debug.log("error getting account details", "jonchristensen");
			});
		} else {
			$('.emailVerification, #adProtector').slideUp('slow');
		}
		//TZ.rpc.refreshLoginStatus(TZ.emailVerification.showVerificationDiv);
		
	},
	showVerificationBar: function() {
		$('#emailVerificationUsername').text(TZ.rpc.username);
		$('#emailVerificationEmail').html(TZ.emailVerification.getEmailAddy());
		$('#emailVerificationChange').unbind('click');
		$('#emailVerificationResend').unbind('click');
		$('#emailVerificationRemind').unbind('click');
		$('#emailVerificationChange').click(TZ.emailVerification.changeEmail);
		$('#emailVerificationResend').click(TZ.emailVerification.resendVerification);
		$('#emailVerificationRemind').click(TZ.emailVerification.remindMeLater);

		$('body').prepend($("#adProtector, .emailVerification").slideDown("slow"));
	},
	getEmailAddy: function() {
		addy = TZ.emailVerification.email;
		
		if (addy.indexOf("gmail") > 0) {
			return '<a href="http://gmail.com">' + addy + '</a>';
		} else if (addy.indexOf("hotmail") > 0) {
			return '<a href="http://hotmail.com">' + addy + '</a>';
		} else if (addy.indexOf("yahoo") > 0) {
			return '<a href="http://mail.yahoo.com">' + addy + '</a>';
		}
		return addy;
	},
	changeEmail: function() {
		TZ.popupHandler.loadChangeEmailPopup();
		return false;
	},
	resendVerification: function() {
		Debug.log("resending verification","jonchristensen");
		TZ.rpc.invoke("TriggerMyEmailVerification", {"template":"EMailVerificationResend"},function(responseObject) {
			Debug.log("TriggerMyEmailVerification returned successfully", "jonchristensen");
			$('.emailVerification, #adProtector').slideUp('slow');
			TZ.notification.show("An email has been resent to " + TZ.emailVerification.email + ". It should appear in a minute.  If not, check your spam filter.","",20000, 500);
		}, function(error) {
			TZ.notification.show("We were unable to resend the email verification. Please try again.","",20000, 500);
			Debug.log("error calling TriggerMyEmailVerification", "jonchristensen");
		});
		return false;
	},
	remindMeLater: function() {
		Debug.log("remind me later clicked", "jonchristensen");
		TZ.user.setUserState('verificationBarHidden', true);
		$('.emailVerification, #adProtector').slideUp('slow');
		return false;
	}
}
		
	
		
			
		
	
		
	
		
			
		
		$(".profileImagePopup").live('click', function(){
			var photoPath=$(this).find("img").attr('src');
			//var orgPhotoPath =  photoPath.slice(0, photoPath.indexOf("/images/")) + "/images" + photoPath.slice(photoPath.lastIndexOf("/"));
			var orgPhotoPath =  photoPath.replace(/\/R\d+H\d+W\//, "/");
			var photoId = photoPath.match(/.*\/(.*).jpg/)[1]; 
			DynamicContentLoader.createPopup("profilePhotoPopupWindow", "profile/profile_photo_popup.html?imgSrc="+orgPhotoPath+"&loggedAccountId="+TZ.rpc.accountId+"&imageId="+photoId, true, 608, 600, true);
		});
		$('#profile_photo_popup_delete_photo').live('click',
			function() {
				TZ.profilePhotoPopup.deleteProfilePhoto();
			}
		);
		$('#profile_photo_popup_save_changes').live('click',
			function() {
				TZ.profilePhotoPopup.saveImageInfo();
			}
		);
		TZ.profilePhotoPopup = {
			currentImage : {},
			imageSetup : function(isPhotoOwner,imageInfo){
				TZ.profilePhotoPopup.currentImage = imageInfo;
				if (isPhotoOwner) {				
					$('#profile_photo_popup_public_options').hide();
					$('#profile_photo_popup_private_options').show();
					$('#profile_photo_popup_start_makeover').attr('href',"makeover/virtual-makeover.html#userName="+TZ.rpc.username+"&imageId="+TZ.profilePhotoPopup.currentImage.imageId);
					if (imageInfo.imageName.length>0) {					
						$('#profile_photo_popup_photo_name_input').tzVal(TZ.profilePhotoPopup.currentImage.imageName);
					} else {
					
					}
					if (imageInfo.publicItem==1) {					
						$('#publicRadioButton').addClass('radiobutton_selected');
						$('#privateRadioButton').removeClass('radiobutton_selected');
					} else {
						$('#privateRadioButton').addClass('radiobutton_selected');
						$('#publicRadioButton').removeClass('radiobutton_selected');					
					}
				} else {
					if (imageInfo.imageName.length>0) {					
						$('#profile_photo_popup_public_image_name').html(imageInfo.imageName);
					} else {
						$('#profile_photo_popup_public_image_name').html('');
					}
				}
			},
			deleteProfilePhoto : function() {	
			 	TZ.popupHandler.loadConfirmCancelPopUp('Delete Photo?', 'Once you delete a photo, any makeovers that were done on the photo will also be deleted.  Neither item can be restored. Are you sure you want to delete this photo?', 'confirm', 'cancel', TZ.profilePhotoPopup.deletePhoto);	
			},
			deletePhoto : function() {
				TZ.imageManager.deleteImage(TZ.profilePhotoPopup.currentImage,onSuccessF);
				function onSuccessF(images){
					TZ.profile.updatePhotos(images);
					TZ.notification.show('Your photo was removed','', 3000, 500);
					TZ.profile_gallery.modeSetup();
					DynamicContentLoader.removePopup($("#profilePhotoPopup"));
				}
			},
			saveImageInfo : function() {		
				radioButtonSelectedId = $('#profile_photo_popup_private_options .radiobutton_selected').attr('id');
				//default value 
				var visibility = 0;
				if (radioButtonSelectedId == 'publicRadioButton') {
					visibility = 1;
				}
				TZ.profilePhotoPopup.currentImage.imageName = $('#profile_photo_popup_photo_name_input').tzVal();
				TZ.imageManager.editImage(TZ.profilePhotoPopup.currentImage,visibility,onSuccessF);
				function onSuccessF(images){
					TZ.profile.loadImages(images);
					TZ.notification.show('Your changes have been saved','',3000, 500);
					TZ.profile_gallery.changeMosaicInfoById(TZ.profilePhotoPopup.currentImage.imageId);
				}
			}
		
		}
	
	
		
	
		
			
		
		// please add class 'imagePopup' to the widget.Image component if you want to popup the original image
		$(".imagePopup").live('click', function(){
			var photoPath=$(this).find("img").attr('src');
			var photoId = photoPath.match(/.*\/(.*).jpg/)[1]; 
			
			DynamicContentLoader.createPopup("photoPopupWindow", "photo_popup.html?imageId="+photoId, true, 300, 300, true);
			
		});
	
	
		
	
		
			
		
		$(".profileMakeoverPopup").live('click', function(){
			var photoPath=$(this).find("img").attr('src');
			var orgPhotoPath =  photoPath.replace(/\/R\d+H\d+W\//, "/");
			var photoId = photoPath.match(/.*\/(.*).jpg/)[1]; 
			var makeoverObj = TZ.profile.getMakeoverByObjectId(photoId);
			if (!makeoverObj.makeoverTopicId && TZ.HorizontalPicsDisplayer) {
				makeoverObj = TZ.HorizontalPicsDisplayer.getMakeoverById(photoId);
				TZ.profileMakeoverPopup.currentImage = 	makeoverObj;			
			}
			if (makeoverObj.makeoverTopicId == undefined) {
				makeoverObj.makeoverTopicId = photoId;
			}
			DynamicContentLoader.createPopup("profileMakeoverPopupWindow", "profile/profile_makeover_popup.html?imgSrc="+orgPhotoPath+"&loggedAccountId="+TZ.rpc.accountId+"&imageId="+photoId+"&topicId="+makeoverObj.makeoverTopicId+"&username="+TZ.userNamePage, true, 608, 600, true);
		});
		$('#profile_makeover_popup_delete_photo').live('click',
			function() {
				TZ.profileMakeoverPopup.deleteProfilePhoto();
			}
		);
		$('#profile_makeover_popup_save_changes').live('click',
			function() {
				TZ.profileMakeoverPopup.saveImageInfo();
			}
		);
		$('#profile_makeover_popup_private_go_to_makeover').live('click',
			function() {
				TZ.profileMakeoverPopup.goToMakeover();
			}
		);
		$('#profile_makeover_popup_public_go_to_makeover').live('click',
			function() {
				TZ.profileMakeoverPopup.goToMakeover();
			}
		);		
		TZ.profileMakeoverPopup = {
			currentImage : {},
			username : '',
			makeoverTopicId : '',
			imageSetup : function(isMakeoverOwner,imageInfo){
				TZ.profileMakeoverPopup.currentImage = imageInfo;
				if (isMakeoverOwner) {				
					$('#profile_makeover_popup_public_options').hide();
					$('#profile_makeover_popup_private_options').show();
					if (imageInfo!=undefined) {
						if (imageInfo.imageName && imageInfo.imageName.length>0) {					
							$('#profile_makeover_popup_makeover_name_input').tzVal(imageInfo.imageName);
						} 
						if (imageInfo.summary.length>0) {					
							$('#profile_makeover_popup_makeover_summary_input').tzVal(imageInfo.summary);
						} 
					}
					if (imageInfo!=undefined && imageInfo.publicItem==1) {					
						$('#profile_makeover_public_radio_button').addClass('radiobutton_selected');
						$('#profile_makeover_private_radio_button').removeClass('radiobutton_selected');
					} else {
						$('#profile_makeover_private_radio_button').addClass('radiobutton_selected');
						$('#profile_makeover_public_radio_button').removeClass('radiobutton_selected');					
					}
				} else {
					if (imageInfo!=undefined && imageInfo.imageName && imageInfo.imageName.length>0) {					
						$('#profile_makeover_popup_public_image_name').html(imageInfo.imageName);
					} else {
						$('#profile_makeover_popup_public_image_name').html('No Photo Title');
					}
					if (imageInfo!=undefined && imageInfo.summary.length>0) {					
						$('#profile_makeover_popup_public_image_summary').html(imageinfo.summary);
					} else {
						$('#profile_makeover_popup_public_image_summary').html('');
					}
				}
			},
			goToMakeover : function() {
				//replace method commented since it removes the history
				//window.location.replace('http://starstyler.gala.de/makeover/'+clean_url_fragment(TZ.profileMakeoverPopup.currentImage.imageName)+'/' + TZ.profileMakeoverPopup.makeoverTopicId + '.html');
				window.location.href = 'http://starstyler.gala.de/makeover/'+clean_url_fragment(TZ.profileMakeoverPopup.currentImage.imageName)+'/' + TZ.profileMakeoverPopup.makeoverTopicId + '.html'; 
			},
			deleteProfilePhoto : function() {	
			 	TZ.popupHandler.loadConfirmCancelPopUp('Delete Makeover?', 'Once you delete a makeover, it cannot be restored.  Are you sure you want to delete this makeover?', 'Confirm', 'Cancel', TZ.profileMakeoverPopup.deleteMakeover);	
			},
			deleteMakeover : function() {
				TZ.imageManager.deleteImage(TZ.profileMakeoverPopup.currentImage,onSuccessF);
				function onSuccessF(images){
					TZ.profile.updateMakeovers(images);
					TZ.profile_my_makeovers.modeSetup();
					TZ.notification.show('Your makeover was removed','', 3000, 500);
					DynamicContentLoader.removePopup($("#profileMakeoverPopup"));
				}
			},
			saveImageInfo : function() {		
				radioButtonSelectedId = $('#profile_makeover_popup_private_options .radiobutton_selected').attr('id');
				//default value 
				var visibility = 0;
				if (radioButtonSelectedId == 'profile_makeover_public_radio_button') {
					visibility = 1;
				}
				TZ.profileMakeoverPopup.currentImage.imageName = $('#profile_makeover_popup_makeover_name_input').tzVal();
				TZ.profileMakeoverPopup.currentImage.summary = $('#profile_makeover_popup_makeover_summary_input').tzVal();
				TZ.imageManager.editImage(TZ.profileMakeoverPopup.currentImage,visibility,onSuccessF);
				function onSuccessF(images){
					TZ.profile.loadMakeovers(images);
					TZ.notification.show('Your makeover changes have been saved','',3000, 500);
					TZ.profile_my_makeovers.changeMosaicInfoById(TZ.profileMakeoverPopup.currentImage.objectId);
				}
			}
		
		}
	
	
		
	
		
			
		
		TZ.features = {
			'advice' :  'advice',
			'deals' :    'deals',
			'trends' :   'trends',
			'hair' :     'hair',
			'profile' :  'profile',
			'makeover' : 'makeover',
			'undefined' : 'undefined'
		} ;
	
	
		
	



	TZ.AdSlot = {
		hideAds : function(){
			$('.ad').hide();
		},
		showAds : function(){
			$('.ad').show();
		}
	}
TZ.paginator = function(target, itemsPerPage, currentPage, displaySize, onPageChange) {
	if (currentPage == null) {
		currentPage = 0;
	}
	this.init(target, itemsPerPage, currentPage, displaySize, onPageChange);
};

$.extend(TZ.paginator.prototype, {
	target:'',
	displaySize:5,
	itemsPerPage:10,	
	count:0,
	numPages:0,
	saveStateName:'pg',
	pageOn:0, // 0 based
	onPageChange:null,
	init:function (target, itemsPerPage, currentPage, displaySize, onPageChange) {
		this.target = target;
		this.pageOn = currentPage;
		this.onPageChange = onPageChange;
		if (itemsPerPage) this.itemsPerPage = itemsPerPage;
		if (displaySize) this.displaySize = displaySize;
		if (!$(target).children().hasClass('paginator'))
			$(target).append('<div class="paginator"></div>');
	},
	updateCounts:function( count) {
		this.count = count;
		this.numPages = Math.floor(count / this.itemsPerPage);
		if (count > this.numPages * this.itemsPerPage) this.numPages++;
		this.render();
	},
	gotoPage:function(page) {
		if (page < 0) page = 0;
		if (page >= this.numPages) page = this.numPages - 1;
		if(page != this.pageOn) //don't execute pagination on the same page
		{
			this.pageOn = page;
			if (this.onPageChange) this.onPageChange(page);
			//save state on pagination
			var state = new Object;
			state[this.saveStateName] = this.pageOn;
			$.bbq.pushState(state);
			this.render();
		}
	},
	render:function() {
		var self = this;
		$(this.target).find('div.paginator').empty();
		if (!this.count) return;
		var startPage = Math.max(this.pageOn - (Math.floor(this.displaySize/2) - 1),0);
		var endPage = Math.min(startPage + (this.displaySize - 1), this.numPages - 1);
		if (endPage == this.numPages - 1) startPage = Math.max(this.numPages - (Math.floor(this.displaySize)),0);
		var body = "<div class='paginator_nav'>" +
			"<div class='pag_btn'><a class='first_page'></a></div>" +
			"<div class='pag_btn'><a class='prev_page'></a></div>";
		for (var i = startPage; i <= endPage; i++) {
			if (i != startPage) body += "<div class='paginator_separator'></div>";
			if (i == this.pageOn) {
				body += "<div class='paginator_index paginator_selected_index'>" + (i+1) + "</div>";
			} else {
				body += "<div class='paginator_index'><a class='page_link'>" + (i+1) + "</a></div>";
			}
		}
		body += "<div class='paginator_index paginator_index_of'>of</div>" +
			"<div class='paginator_index'><a class='last_page'>" + this.numPages + "</a></div>" +
			"<div class='pag_btn'><a class='next_page'></a></div></div>";
		$(this.target).find('div.paginator').append(body);
		
		$(this.target).find('div.paginator .pag_btn').click( function() { 
			var firstPage =  $(this).find("a.first_page");
			if (firstPage.length>0) {
				self.gotoPage(0);
				return;
			}
			var lastPage =  $(this).find("a.last_page");
			if (lastPage.length>0) {
				self.gotoPage(self.numPages - 1);
				return;
			}
			var prevPage =  $(this).find("a.prev_page");
			if (prevPage.length>0) {
				self.gotoPage(self.pageOn - 1);
				return;
			}
			var nextPage =  $(this).find("a.next_page");
			if (nextPage.length>0) {
				self.gotoPage(self.pageOn + 1);
				return;
			}
		});

		$(this.target).find('div.paginator .pag_btn').hover(function() { 
			$(this).find("a").addClass("paginatorButtonHover");
		}, function() { 
			$(this).find("a").removeClass("paginatorButtonHover");
		});
		
		$(this.target).find('div.paginator_index').click( function() {
			var paginatorIndexAChildren = $(this).find("a");
			if (paginatorIndexAChildren.length>0) {
				self.gotoPage(parseInt(paginatorIndexAChildren.text()) - 1);
			}	
		});
	}
});
/*
 * TZTrendsRotator 
 *	This class has a corresponding erb called TrendBar, styling applies to both
 */
TZTrendsRotator = function(itemLength) {
  this.init(itemLength);
}

$.extend(TZTrendsRotator.prototype, {
	itemLength:0,
	count:0,
	allTrends:[],
	spinTimer:null,
	init:function (itemLength) {
		var instance = this;
		this.itemLength = itemLength;
		this.count = Math.floor( 1000 / this.itemLength);
		var sortPos = 0;
		var trend_count = $('.trend_item').length;
		for(var i=0;i<trend_count;i++){	
			$('.trend_item,' + '#trend_scroll_section [style*="left: '+ sortPos + 'px;"]' ).each( function( index, el) {
				instance.allTrends.push(el);		
				$(el).detach();
			});
			sortPos += this.itemLength;
		}
		$('#trending_bar .right_btn').click( function() { instance.resetSpinner(); instance.scrollTrends(1); });
		$('#trending_bar .left_btn').click( function() { instance.resetSpinner(); instance.scrollTrends(-1); });
		this.scrollTrends(1);
		this.resetSpinner();
	},
	getPositions:function() {
		var rtn = { 
			min:this.itemLength * this.count,
			max:-this.itemLength
		};
		$('.trend_item').each( function( index, el) {
			$(el).stop();
			var left = $(el).position().left;
			if (left < rtn.min) rtn.min =left;
			if (left > rtn.max) rtn.max = left;
		});
		return rtn;
	},
	removeExtras:function() {
		var instance = this;
		$('.trend_item').stop( false, false);	
		$('.trend_item').each(function( item, el) {
			var pos = $(el).position();
			if (pos.left < 0 || pos.left > (instance.itemLength * (instance.count))) {
				$(el).remove();
			}
		});
	},
	addTopics:function(amount){
		var el;
		var pos;
		var i;
		if(amount < 0){
			for(i=0;i<this.count*2;i++){
				el = this.allTrends.pop();
				if(el) this.allTrends.unshift(el);
			}
		}	
		p = this.getPositions();
		for(i=1;i<=this.count;i++){
			el = this.allTrends.shift();
			if(!el) return;
			this.allTrends.push(el); 
			pos = (amount > 0) ? p.max+this.itemLength*i : p.min-this.itemLength*(this.count-i+1);
			$(el).css('left',pos+"px");
			var cel = $(el).clone();
			$(cel).appendTo("#trend_scroll_section");
			$(cel).click(function() { 
				//$(cel).animate({ 'backgroundColor':'#d3eaf0' },50).animate({ 'backgroundColor':'#000' },650);
			});
		}
	},
	scrollTrends:function( amount) {
		this.removeExtras();
		this.addTopics(amount);	
		/* first build a proper offset list*/
		var dist = (amount > 0) ? (this.getPositions().max - (this.itemLength * (this.count-1))) : this.getPositions().min;
		$('.trend_item,.trend_item_show').animate({left:"-=" + dist +"px"},1000);
	},
	spinIt:function() {
		$('#trending_bar .right_btn').click();
	},
	resetSpinner:function() {
		try { clearTimeout(this.spinTimer); } catch (arg) {}
		this.spinTimer = setTimeout(this.spinIt ,30000);
	}
});
TZ.vote = {
	targets : {},
	castVote:function(controllingId,topicId,bucket,type,success_function,error_function) {
		tempOb = {targetId:topicId,bucket:bucket,type:type,controllingId:controllingId};
		TZ.rpc.createRPC("CastVote",tempOb,success_function,error_function);
	},
	getNetVotesForTargets:function(targetIds,success_function,error_function) {
		tempOb = {targetIds:targetIds};
		TZ.rpc.createRPC("GetNetVotesForTargets",tempOb,success_function,error_function);
	},
	getNetVotesForTargetsWithControllingId:function(controllingId,targetIds,success_function,error_function) {
		tempOb = {"controllingId": controllingId, "targetIds":targetIds};
		TZ.rpc.createRPC("GetNetVotesForTargets",tempOb,function(votesObject) {
			success_function(votesObject, controllingId);
		},error_function);
	},
	addTargets:function(controllingId,targetIds){
		var target = TZ.vote.targets[controllingId];
		if (!target) target = [];
		target.concat(targetIds);	
	},
	getMyVotes:function(targetId,types,success_function,error_function) {
		tempOb = {targetId:targetId,types:types};
		TZ.rpc.createRPC("GetMyVotes",tempOb,success_function,error_function);
	},	
	calcVotesResults:function(totalVotesUp,totalVotesDown) {
		totalVotesUp = totalVotesUp || 0;
        totalVotesDown = totalVotesDown || 0;
        	
		var totalVotes=totalVotesUp+totalVotesDown;
		var successRate=0;
		if (totalVotes>0) {
			successRate=(totalVotesUp/totalVotes)*100;
		}
		return successRate;
	},
    totalVotes: function(votes) {
    	var totalVotes = 0;
    	for (var bucket in votes) {
    		totalVotes = totalVotes + votes[bucket];
    	}
    	return totalVotes;
    },
    getMostVoted: function(votes) {
     	var maxVotes = 0;
     	var mostPopularOption;
    	for (var indexOption in votes) {
    		if (votes[indexOption]>maxVotes) {
    			mostPopularOption=indexOption;
    			maxVotes=votes[indexOption];
    		}
    	}
    	return mostPopularOption;   	
    },
	processVotesForPage:function() {
		for (var controllingId in TZ.vote.targets) {
			TZ.vote.getNetVotesForTargetsWithControllingId(
					controllingId, 
					TZ.vote.targets[controllingId], 
					function(votesObject, returnedControllingId){
						if (votesCallbacks[returnedControllingId] != null) {	
							var callbacksArray = votesCallbacks[returnedControllingId];
					    	for (var callbackIndex in callbacksArray) {
						 		 TZ.executeFunctionByName(callbacksArray[callbackIndex], window, votesObject);
					    	}				
						}
				 	}, 
				 	function(errorObject){
				 	}
			);
		}
	},
	processOwnVotesForPage:function() {
		TZ.vote.getMyVotes("",typesToQuery,function(votesObject) {
			for (var callback in ownVoteCallbacks) {
		 		 TZ.executeFunctionByName(ownVoteCallbacks[callback], window, votesObject);
	    	}
		},
		function(errorObject) {
		});
	}
    
}

TZ.popupHandler = {
	//TZ.popupHandler.loadSignInRequiredPopup
	loadSignInRequiredPopup:function() {
		DynamicContentLoader.createPopup("signInRequiredPopup","sign_in_required_popup.html",true, 416, 178);
	},
	closeSignInRequiredPopup:function() {
		DynamicContentLoader.removePopup($("#signInRequiredPopup"));
	},
	loadLogInPopup:function() {
		DynamicContentLoader.createPopup("loginPopupWindow", "login_popup.html", true, 623, 225);	
	},
	closeLogInPopup:function() {
		DynamicContentLoader.removePopup($("#loginPopupWindow"));
	},
	loadRegisterPopup:function() {
		DynamicContentLoader.createPopup("loginPopupWindow", "login_popup.html?showRegistration=1", true, 623, 315);
	},
	closeRegisterPopup:function() {
		DynamicContentLoader.removePopup($("#loginPopupWindow"));
	},
	loadOkAlertPopup:function(message) {
		DynamicContentLoader.createPopup("okAlertPopup", "ok_alert_popup.html?message="+ message, true, 400, 155);	
	},	
	closeOkAlertPopup:function() {
		DynamicContentLoader.removePopup($("#okAlertPopup"));
	},
	loadChangeEmailPopup:function(message) {
		DynamicContentLoader.createPopup("changeEmailPopup", "change_email_popup.html", true, 400, 155);	
	},	
	closeChangeEmailPopup:function() {
		DynamicContentLoader.removePopup($("#changeEmailPopup"));
	},
	loadInvitePopup:function() {
		var inviteUrl = "invite_popup.html.html";
		DynamicContentLoader.createPopup("invitePopup", inviteUrl, true, 400, 155);	
	},	
	closeInvitePopup:function() {
		DynamicContentLoader.removePopup($("#invitePopup"));
	},
	loadFlagConfirmationPopup:function(message) {
		DynamicContentLoader.createPopup("flagConfirmationPopup", "flag_confirmation_popup.html", true, 400, 155);	
	},	
	closeFlagConfirmationPopup:function() {
		DynamicContentLoader.removePopup($("#flagConfirmationPopup"));
	},
	closeUploadImagePopup:function(){
		TZ.popupHandler.showOverlapStuff();
		DynamicContentLoader.removePopup($("#uploadImagePopup"));
		TZ.imageManager.imagesLoaded = false;
		TZ.imageManager.loadMyImages();
	},
	closeAttachImagePopup:function(){
		DynamicContentLoader.removePopup($("#attachImagePopup"));
	},
	loadAttachImagePopup:function(callback){
		//Debug.log("loadAttachImagePopup from popuphandler.js", 'igortz');
		this.closeAttachImagePopup();
		if(!TZ.rpc.loggedIn) {
			TZ.popupHandler.loadSignInRequiredPopup();	//make sure it is consistent with other functionalities which require sign in. -Hui
			return;
		}
		TZ.imageAttached = function(imageData){
			if(callback === undefined) {
				//Debug.log("loadAttachImagePopup: callback is undefined", "igortz");
				return;
			}
			if (imageData.imageId != imageData.objectId) {
				var url = "http://" + pmHost + "/images/" + imageData.objectId +".jpg";
			} else {
				var url = "http://" + pmHost + "/images/" + imageData.imageId+".jpg";
			}
			callback(url,imageData);
			TZ.events.dispatchEvent('content_changed',this);
			delete TZ.imageAttached;
		}
		
		TZ.imageUploadImage = callback;
		
		DynamicContentLoader.createPopup("attachImagePopup", "attach_image_popup.html", true, 568, 542,false);	
	},
	loadUpdateProfilePicture:function(callback,forceUpload){
		var onSuccess = function(a,b,c,d){
				if(callback) callback(url,imageData);		
			};
		var onError = function(a,b,c,d){
				var i = 0;
			};
		var imageChangeF = function(url,imageData){
			TZ.rpc.createRPC('SaveAccountJSONData',
				{dataSpec:{type:'profile',typeId:'',accountId:'',visibility:1},dottedPath:'profileImage',jsonData:imageData.imageId},
				onSuccess,onError);
		};
		if (forceUpload) {
			TZ.popupHandler.loadUploadImagePopup(imageChangeF,1);
		} else {
			//TZ.popupHandler.loadAttachOptionsPopup(imageChangeF,1);
			TZ.popupHandler.loadAttachImagePopup(imageChangeF, 1);
		}
	},
	loadAttachOptionsPopup:function(callback,max_images_allowed){
		this.closeAttachOptionsPopup();
		if(!TZ.rpc.loggedIn) {
			TZ.popupHandler.loadSignInRequiredPopup();	//make sure it is consistent with other functionalities which require sign in. -Hui
			return;
		}
		if(max_images_allowed) TZ.maxAttachImagesAllowed = max_images_allowed;
		else TZ.maxAttachImagesAllowed = 1;
		TZ.imageAttachOptions = callback;
		DynamicContentLoader.createPopup("attachOptionsPopup", "attach_options_popup.html", true, 470, 162,false);	
	},
	closeAttachOptionsPopup:function(){
		DynamicContentLoader.removePopup($("#attachOptionsPopup"));
	},
	loadUploadImagePopup:function(callback,max_images_allowed) {
		//Debug.log("loadUploadImagePopup", 'igortz');
		if(!TZ.rpc.loggedIn) {
			TZ.popupHandler.loadLogInPopup();
			return;
		}
		if(max_images_allowed) TZ.maxAttachImagesAllowed = max_images_allowed;
		else TZ.maxAttachImagesAllowed = 1;
		TZ.imageUploaded = function(imageData){
			//DynamicContentLoader.removePopup($("#uploadImagePopup"));
			if(callback === undefined) {
				//Debug.log("loadUploadImagePopup: callback is undefined", "igortz");
				return;
			}
			var url = "http://"+ pmHost + "/images/" + imageData.imageId +".jpg";
			callback(url,imageData);
		}
		DynamicContentLoader.createPopup("uploadImagePopup", "image_upload_popup.html", true, 775, 474,false);	
	},
	loadConfirmCancelPopUp: function(title, summary, confirmButtonText, cancelButtonText, confirmCallbackFunction, cancelCallbackFunction){
		DynamicContentLoader.createPopup("confirmCancelPopUp","confirm_cancel.html?title="+title+"&summary="+summary+"&confirmBtnText="+confirmButtonText+"&cancelBtnText="+cancelButtonText,true, 406, 252,true,
				function(){
					$('#confirmButton').click(function(){
						if (confirmCallbackFunction) confirmCallbackFunction();
						DynamicContentLoader.removePopup($("#confirmCancelPopUp"));
					});
					$('#cancelButton').click(function(){
						if(cancelCallbackFunction) cancelCallbackFunction();
						DynamicContentLoader.removePopup($("#confirmCancelPopUp"));
					});
			});
	},
	loadTopicManagementPopup:function(topicId,topicTitle) {
		DynamicContentLoader.createPopup("manageTopicPopup","management/topic_management.html?topicId="+topicId,true, 416, 178);
	},
	closeTopicManagementPopup:function() {
		DynamicContentLoader.removePopup($("#manageTopicPopup"));
	},
	hideOverlapStuff : function() {
		TZ.AdSlot.hideAds();
		TZ.social.Facebook.hideLikeButtons();
	},
	showOverlapStuff : function() {
		TZ.AdSlot.showAds();
		TZ.social.Facebook.showLikeButtons();
	}
};
TZ.agreement = {
	agreeOrDisagree: function(jsItem, topicId, topicEntryId, type, callback, changeText) {
		changeText = typeof(changeText) != 'undefined' ? changeText : false;
		if ($(jsItem).hasClass("agreed")) {
			$(jsItem).removeClass('agreed');
			if (changeText) {
				$(jsItem).text('Agree');
			}
			//then we want to remove the vote.
			TZ.vote.castVote(topicId, topicEntryId,"unselected", type, function(votesObject){
				Debug.log("TZ.vote.castVote success", 'jonchristensen');
				Debug.log(votesObject, 'jonchristensen');
				callback(votesObject, topicEntryId);
			},
			function(error){
				Debug.log("Agree vote error " + error.error , 'jonchristensen');
				$(jsItem).removeClass('agreed');
				if (changeText) {
					$(jsItem).text('Agreed');
				}
			});
		} else {
			//then we want to cast the vote.
			$(jsItem).addClass('agreed');
			if (changeText) {
				$(jsItem).text('Agreed');
			}
			TZ.vote.castVote(topicId, topicEntryId,"selected", type, function(votesObject){
				Debug.log("TZ.vote.castVote success", 'jonchristensen');
				Debug.log(votesObject, 'jonchristensen');
				callback(votesObject, topicEntryId);
			},
			function(error){
				Debug.log("Up vote error " + error.error , 'jonchristensen');
				$(jsItem).removeClass('agreed');
				if (changeText) {
					$(jsItem).text('Agree');
				}
			});
		}
	}
}
TZ.loveIt = {
	topicId: null,
	prepare: function(identifier) {
		var loveItButton = $('#' + identifier + ' a');
		loveItButton.click(function() {
			Debug.log("Love It button clicked", "jonchristensen");
			TZ.loveIt.loveItOrUnloveIt(this);
		});
	},
	loveItOrUnloveIt: function(loveItLink) {
		
		var loveItButton = $(loveItLink).parent();
		var loveItComponentId;
		if (loveItButton.attr('id').indexOf('loveIt') == 0) {
			var loveItComponentId = $(loveItButton).attr('id');
			if (loveItComponentId.indexOf('second') > 0) {
				loveItComponentId = loveItComponentId.substring(0, loveItComponentId.length - 6);
			}
			TZ.loveIt.topicId = loveItComponentId.substring(6, loveItComponentId.length);
		} else {
			TZ.loveIt.topicId = TZ.getIdFromUrl();
		}
		// Now cast the vote to loveIt or unloveIt accordingly
		if ($(loveItLink).hasClass("loved_it")) {
			$('.' + loveItComponentId + ' .loveit_btn_sm').removeClass('loved_it');
			
			// then we want to remove the vote.
			TZ.vote.castVote(TZ.loveIt.topicId, TZ.loveIt.topicId, "unselected","LoveIt",
			function(votesObject){
				Debug.log("TZ.vote.castVote success", 'jonchristensen');
				Debug.log(votesObject, 'jonchristensen');
				TZ.loveIt.updateVotes(votesObject, loveItComponentId);
			},
			function(error){
				$('.' + loveItComponentId + ' .loveit_btn_sm').addClass('loved_it');
				
				Debug.log("Agree vote error " + error.error , 'jonchristensen');
			});
		} else {
			// then we want to cast the vote.
			$('.' + loveItComponentId + ' .loveit_btn_sm').addClass('loved_it');
			
			TZ.vote.castVote(TZ.loveIt.topicId, TZ.loveIt.topicId, "selected","LoveIt",
			function(votesObject){
				Debug.log("TZ.vote.castVote success", 'jonchristensen');
				Debug.log(votesObject, 'jonchristensen');
				TZ.loveIt.updateVotes(votesObject, loveItComponentId);
			},
			function(error){
				$('.' + loveItComponentId + ' .loveit_btn_sm').removeClass('loved_it');
				
				Debug.log("loveIt vote error " + error.error , 'jonchristensen');
			});
		}
	},
	updateVotes: function(votesObject, loveItComponentId) {
		if (TZ.loveIt.topicId == null) {
			TZ.loveIt.topicId = TZ.getIdFromUrl();
		}
    	if (votesObject[TZ.loveIt.topicId] !=undefined && votesObject[TZ.loveIt.topicId]['LoveIt'] !=undefined) {
    		$('.love_icon_container .bubble_count').text(votesObject[TZ.loveIt.topicId]['LoveIt']['selected']);
    	} else if (votesObject['selected'] !=undefined) {
    		if (loveItComponentId != undefined) {
    			var loveItComponent = $('.' + loveItComponentId);
    			
    			var bubbleCountHolder = $('.' + loveItComponentId).parent().find('.bubble_count');
    			if (bubbleCountHolder === undefined || bubbleCountHolder.length == 0) {
    				bubbleCountHolder = $('.' + loveItComponentId).parent().parent().find('.bubble_count');
    			}
    			
    			bubbleCountHolder.text(votesObject['selected']);
    		} else {
    			$('.love_icon_container .bubble_count').text(votesObject['selected']);
    		}
    	} else {
			for (var topicId in votesObject) {
				if (votesObject[topicId]['LoveIt'] != undefined) {
					var targetItem = $('#loveIt' + topicId);
					if (targetItem != null) {
						targetItem.parents('.love_icon_container').find('.bubble_count').text(votesObject[topicId]['LoveIt']['selected']);
					}
				}
			}
    	}
    	$('.love_icon_container .bubble_count').show();
	},
	updateOwnVotes: function(votesObject) {
		$('.loveit_btn_sm').removeClass('loved_it');
		for (var topicId in votesObject) {
			if (votesObject[topicId]['LoveIt'] != undefined) {
				if (votesObject[topicId]['LoveIt'] == 'selected') {
					$('.loveIt' + topicId + ' .loveit_btn_sm').addClass('loved_it');
				} else {
					$('.loveIt' + topicId + ' .loveit_btn_sm').removeClass('loved_it');
				}
			}
		}
	}
};
TZ.topicmanagement = {
	setOffTopic:function(topicId,topicProperties,success_function,error_function) {
		topicProperties.category = 'offtopic';
		TZ.rpc.createRPC('AdminCreateOrUpdateTopic', {'topicId' : topicId, 'properties': topicProperties},
		function(successObject) {
			var tempOb = {topicId:topicId}; 
			TZ.rpc.createRPC('AdminSubmitTopicToGallery',tempOb,success_function,error_function);
		},
		function(errorObject) {error_function(errorObject);});
	},
	setEditorPickTopic:function(topicId,topicProperties,success_function,error_function) {
		topicProperties.isPickedByAnEditor = 1;
		TZ.rpc.createRPC('AdminCreateOrUpdateTopic', {'topicId' : topicId, 'properties': topicProperties},
		function(successObject) {
			var tempOb = {topicId:topicId}; 
			TZ.rpc.createRPC('AdminSubmitTopicToGallery',tempOb,success_function,error_function);
		},
		function(errorObject) {error_function(errorObject);});		
	},
	removeEditorPickTopic:function(topicId,topicProperties,success_function,error_function) {
		topicProperties.isPickedByAnEditor = 0;
		TZ.rpc.createRPC('AdminCreateOrUpdateTopic', {'topicId' : topicId, 'properties': topicProperties},
		function(successObject) {
			var tempOb = {topicId:topicId}; 
			TZ.rpc.createRPC('AdminSubmitTopicToGallery',tempOb,success_function,error_function);
		},
		function(errorObject) {error_function(errorObject);});		
	}
}

TZ.flag = {
	/**
	 * Set up click handler.
	 */
	prepare: function (flagButton) {
		$(flagButton).click(function(event) { TZ.flag.confirmFlag(this, event); return false; });
	},
	
	flagTopic: function(topicId, success_function, error_function) {
		tempOb = {topicId:topicId};
		TZ.rpc.createRPC("FlagTopic",tempOb,success_function, error_function);
	},
	
	flagTopicEntry: function(topicId, topicEntryId, success_function, error_function) {
		tempOb = {topicId:topicId, topicEntryId:topicEntryId};
		TZ.rpc.createRPC("FlagTopicEntry", tempOb, success_function, error_function);
	},
	
	flagImage: function(imageId, success_function, error_function) {
		tempOb = {imageId:imageId};
		TZ.rpc.createRPC("FlagImage", tempOb, success_function, error_function);
	},
	
	/**
	 * Called when Flag button is clicked.
	 */
	confirmFlag: function (flagButton, event) {
		if (TZ.rpc.username) {
			var $flagButton = $(flagButton);
			if (event && event.altKey) {	// Admin function (delete)
				var topicId = $flagButton.data('topicId');
				var topicEntryId = $flagButton.data('topicEntryId');
				if (topicEntryId)	// this is a Flag comment button
					TZ.topics.deleteTopicEntry(topicId, topicEntryId, function () {
						$flagButton.append(' DELETED');
						TZ.notification.show('Comment Deleted', 'It may take up to 5 minutes for this page to reflect the change.', 5000, 200);
					});
				else	// this is a Flag topic button
					TZ.topics.adminDeleteTopic(topicId, function () {
						$flagButton.append(' DELETED');
						TZ.notification.show('Topic Deleted', 'It may take up to 5 minutes for this page to reflect the change.', 5000, 200);
					}, function (e) {
						TZ.rpc.invoke("DoIHaveAllOfRoles", {"roles":['content-admin']}, function () {
							TZ.notification.show('Error Deleting Topic', e.error, 12000, 200);
						})
					});
			}
			// Regular Flag operation
			else {
				$flagButton.addClass('to_be_flagged');
				TZ.popupHandler.loadFlagConfirmationPopup();
			}
		}
		else {	// user not logged in
			TZ.popupHandler.loadSignInRequiredPopup();
		}
	},
	
	doNotFlagIt: function() {
		$('.to_be_flagged').removeClass('to_be_flagged');
	},
	
	flagIt: function () {
		var $flagButton = $('.to_be_flagged');
		
		Debug.log("Topic Id being flagged: " + $flagButton.data("topicId"), "brentf");
		Debug.log("Topic Entry Id being flagged: " + $flagButton.data("topicEntryId"), "brentf");
		Debug.log("Image Id being flagged: " + $flagButton.data("imageId"), "brentf");
		
		var imageId = $flagButton.data("imageId");
		
		var topicId = $flagButton.data("topicId");
		if ((topicId == null || topicId == "") && (imageId == null || imageId == "")) {
			Debug.log("error cannot flag without a topic id or image id", "jonchristensen");
			return;
		}
		
		var topicEntryId = $flagButton.data("topicEntryId");
		if (topicEntryId != null && topicEntryId != "") {
			TZ.flag.flagTopicEntry(topicId, topicEntryId, function(remainingFlagsForThisUser) {
				TZ.notification.show('Your flag has been counted.','')
				Debug.log(TZ.rpc.username + " has " + remainingFlagsForThisUser + " flags remaining", 'jonchristensen');
			}, function(errorObject) {
				Debug.log(errorObject, 'jonchristensen');
			});
		} else if (imageId != null && imageId != "") {
			TZ.flag.flagImage(imageId, function(remainingFlagsForThisUser) {
				TZ.notification.show('Your flag has been counted.','')
				Debug.log(TZ.rpc.username + " has " + remainingFlagsForThisUser + " flags remaining", 'jonchristensen');
			}, function(errorObject) {
				Debug.log(errorObject, 'jonchristensen');
			});
		} else {
			TZ.flag.flagTopic(topicId, function(remainingFlagsForThisUser) {
				TZ.notification.show('Your flag has been counted.','')
				Debug.log(TZ.rpc.username + " has " + remainingFlagsForThisUser + " flags remaining", 'jonchristensen');
			}, function(errorObject) {
				Debug.log(errorObject, 'jonchristensen');
			});
		}
		
		$('.to_be_flagged').removeClass('to_be_flagged');
	}
};
TZ.accountDetails = {
	changeEmail :function(newEmail) {
		Debug.log("changing email to " + newEmail, "jonchristensen");
		TZ.rpc.createRPC("UpdateMyAccount",{newName: null, newEmail: newEmail},function(responseObject) {
			TZ.notification.show("An email has been sent to " + newEmail + ". Please check your inbox in the next few minutes","", 6000, 500);
			TZ.popupHandler.closeChangeEmailPopup();
			$('.emailVerification, #adProtector').slideUp('slow');
		}, function(error) {
			TZ.notification.show("Email address already in use.", "", 5000, 500);
		});
	
	}, 
	setStatus : function (statusMessage, onSuccess, onError){
		TZ.accountDetails.createPublicTopicEntry(TZ.rpc.accountId,'status',{status:statusMessage},onSuccess,onError);
	}, 
	sendPublicMessage : function (recieverAccountId, message, onSuccess,onError){
		TZ.accountDetails.createPublicTopicEntry(recieverAccountId,'message',{message:message},onSuccess,onError);
	},
	createPublicTopicEntry : function(accountId, type, properties, onSuccess, onError){
		TZ.rpc.createRPC(
			"CreatePublicTopicEntry",
			{
    			accountId : accountId,
   				type :type,
    			properties : properties
 			}, 
			function(success){
				if(onSuccess) onSuccess(success);
			},
			function(error){
				Debug.log("error setting status", "spmallick");
				if(onError) onError(eror);
			},
			function(error){
				Debug.log("error getting status", "spmallick");
				if(onError) onError(eror);
			}	
		)
	},
	getStatus : function(accountId, onSuccess, onError){
		TZ.rpc.createRPC(
			"GetPublicTopicEntries",
			{
    			accountId : accountId,
   				type 	  : "status",
    			offset 	  : 0,
				maxEntries : 1
 			}, 
			function(success){
				if(onSuccess) onSuccess(success);
			},
			function(error){
				Debug.log("error getting status", "spmallick");
				if(onError) onError(eror);
			},
			function(error){
				Debug.log("error getting status", "spmallick");
				if(onError) onError(eror);
			}	
		)
	}
}
TZ.videoInput = {
	maxTimeOut: 6000,
	lastTimeUrlChanged : '',
	setupParseVideoUrl: function(field) {
		field.find('input').bind('keyup onpaste onchange', this.parseVideoUrl);
		
	},
	
	setVideoInputAndPreview: function(field, site, id) {
		
		var videoUrl = "";
		if(site == "youtube") {
		 	videoUrl = "http://www.youtube.com/watch?v=" + id;
		}
		else if(site == "vimeo") {
		 	videoUrl = "http://vimeo.com/" + id;
		}
		$(field).find('input').val(videoUrl);
		if(videoUrl){
			TZ.videoInput.parseVideoUrl(null, field);
		}
		else {
			TZ.videoInput.resetPreview($(field));
		}			
	},
	
	parseVideoUrl: function(event, field) {
		var $videoInputComponent = (event == null)?$(field):$(event.currentTarget).parent().parent();
	
		// Check in t seconds if the URL doesn't get a video.
		TZ.videoInput.lastTimeUrlChanged = new Date().getTime();
		window.setTimeout(function(){ 
				if(TZ.videoInput.lastTimeUrlChanged + (TZ.videoInput.maxTimeOut-100) < new Date().getTime() )
				{
					 if(!$videoInputComponent.find('.videoPreview').data('unlocked'))
					 {
						 //$videoInputComponent.find('.videoPreview .spinningColorWheel').remove();
						 $videoInputComponent.find('.videoPreview .scwContainer').spinner('stop');
						 $videoInputComponent.find('.videoPreview').find('#errorUrl').text('Can not upload a video from this url.  Try a different url.');
					 }
				}
		}, TZ.videoInput.maxTimeOut);
		// Check in t seconds if the URL doesn't get a video.
		
	    var id;
	    //$videoInputComponent.find('.videoPreview').html('<div class="scwContainer"><img class="spinningColorWheel" src="resources/images/common/spinning_color_wheel.gif"/></div><span id="errorUrl"></span>');	    
	    $videoInputComponent.find('.videoPreview').html('<div class="scwContainer"></div><span id="errorUrl"></span>');
	    $videoInputComponent.find('.videoPreview .scwContainer').spinner();
	    $videoInputComponent.find('.videoPreview').slideDown('medium'); 
	    $videoInputComponent.data('thumb', null);
	    $videoInputComponent.data('videoTitle', null);
	    $videoInputComponent.data('id', null);
	    $videoInputComponent.data('site', null);
	    $videoInputComponent.find('.videoPreview').data('haveThumb', false);
	    $videoInputComponent.find('.videoPreview').data('unlocked',false);
	    $videoInputComponent.removeClass('videoDataLoaded');
	    $videoInputComponent.find('input').val($.trim($videoInputComponent.find('input').val()));
	    var url = $videoInputComponent.find('input').val();	
	    if (url.indexOf('http://') == 0) {
	    	url = url.substring(7,url.length);
	    }
		
	    if (url.indexOf('youtube.com') > -1) {
	    	var firstPart = url.split('/')[1];
	    	var secondPart = firstPart.split('v=')[1];
	        id = secondPart.split('&')[0];
	        TZ.videoInput.youtubeAjax($videoInputComponent, id);
	        return;
	    } else if (url.indexOf('youtu.be') > -1) {
	        id = url.split('/')[1];
	        TZ.videoInput.youtubeAjax($videoInputComponent, id);
	        return;
	    } else if (url.indexOf('vimeo.com') > -1) {
	        if (url.match(/^vimeo.com\/[0-9]+/)) {
	            id = url.split('/')[1];
	        } else if (url.match(/^vimeo.com\/channels\/[\d\w]+#[0-9]+/)) {
	            id = url.split('#')[1];
	        } else if (url.match(/vimeo.com\/groups\/[\d\w]+\/videos\/[0-9]+/)) {
	            id = url.split('/')[4];
	        } else {
	            throw new Error('Unsupported Vimeo URL');
	        }
	        TZ.videoInput.vimeoAjax($videoInputComponent, id);
	    } else if ($videoInputComponent.find('input').val() == ''){
	    	TZ.videoInput.resetPreview($videoInputComponent);
	    } 
	},
	vimeoAjax: function(field, id) {
		$.ajax({
            url: 'http://vimeo.com/api/v2/video/' + id + '.json',
            dataType: 'jsonp',
            success: function(data) {
        		TZ.videoInput.postProcess(field, 'vimeo', id, data[0].thumbnail_large, data[0].title);
            }
        });
	},
	youtubeAjax: function(field, id) {
	try{
		$.ajax({
            url: 'http://gdata.youtube.com/feeds/api/videos/' + id + '?alt=json',
            dataType: 'jsonp',
            success: function(data) {
				if(data.entry.media$group.media$thumbnail[0].url!= undefined){
					var index = data.entry.id.$t.lastIndexOf("/")+1;
					id = data.entry.id.$t.substr(index);
					TZ.videoInput.postProcess(field,'youtube', id, data.entry.media$group.media$thumbnail[0].url, data.entry.title.$t);
				} 
	       }
		})} catch(err){
		}
	},
    postProcess: function(field, site, id, thumb, videoTitle) {
		field.data('thumb', thumb);
    	field.data('videoTitle', videoTitle);
    	field.data('id', id);
		field.data('site', site);
		field.find('.videoPreview').data('haveThumb', true);
		field.find('.videoPreview').data('unlocked',true);
		TZ.videoInput.showPreview(field);
    },
    unlock: function() {
    	$(this).data('unlocked',true);
    	if ($(this).data('haveThumb')) {
    		TZ.videoInput.showPreview($(this).parent());
    	}
    },
    showPreview: function(field) {
    	field.find('.videoPreview').css('border', 'none');
    	field.find('.videoPreview').css('width', 'auto');
    	field.find('.videoPreview').html('<div class="videoInfo"><img src="' + field.data('thumb') + '" height="75"/></div>' + 
    			'<div class="videoTitle videoInfo">' + field.data('videoTitle') + '</div><div style="clear:both"></div>');
    	field.addClass('videoDataLoaded');
    	field.find('.videoPreview').slideDown('medium'); 
    },
    resetPreview: function(field) {
    	//field.find('.videoPreview .scwContainer').html('<img class="spinningColorWheel"  src="resources/images/common/spinning_color_wheel.gif"/>');
    	field.find('.videoPreview .scwContainer').spinner();
    	field.data('thumb', null);
    	field.data('videoTitle', null);
    	field.data('id', null);
    	field.data('site', null);
    	field.find('.videoPreview').data('haveThumb', false);
    	field.find('.videoPreview').data('unlocked',false);
    	field.find('.videoPreview').slideUp('medium');
		field.find('.videoPreview').css('display','none'); 
		field.removeClass('videoDataLoaded');
    }
};
TZ.adviceItem = {
	updateVotes:function(votesObject){
		var voteTotal = 0;
		for (var targetVotes in votesObject) {
			if (votesObject[targetVotes]['SelectIt'] != undefined) {
				voteTotal = TZ.vote.totalVotes(votesObject[targetVotes]['SelectIt']);
			
				$("#" + targetVotes + " .total_answers").text(voteTotal + " Votes");
			}
		}
		$(".total_answers").show();
	}
};
/*
 * jQuery Notify UI Widget 1.4
 * Copyright (c) 2010 Eric Hynds
 *
 * http://www.erichynds.com/jquery/a-jquery-ui-growl-ubuntu-notification-widget/
 *
 * Depends:
 *   - jQuery 1.4
 *   - jQuery UI 1.8 widget factory
 *
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 *
*/
(function($){

$.widget("ech.notify", {
	options: {
		speed: 500,
		expires: 3000,
		stack: 'below',
		custom: false
	},
	_create: function(){
		var self = this;
		this.templates = {};
		this.keys = [];
		
		// build and save templates
		this.element.addClass("ui-notify").children().addClass("ui-notify-message ui-notify-message-style").each(function(i){
			var key = this.id || i;
			self.keys.push(key);
			self.templates[key] = $(this).removeAttr("id").wrap("<div></div>").parent().html(); // because $(this).andSelf().html() no workie
		}).end().empty().show();
	},
	create: function(template, msg, opts){
		if(typeof template === "object"){
			opts = msg;
			msg = template;
			template = null;
		}
		
		var tpl = this.templates[ template || this.keys[0]];
		
		// remove default styling class if rolling w/ custom classes
		if(opts && opts.custom){
			tpl = $(tpl).removeClass("ui-notify-message-style").wrap("<div></div>").parent().html();
		}
		
		// return a new notification instance
		return new $.ech.notify.instance(this)._create(msg, $.extend({}, this.options, opts), tpl);
	}
});

// instance constructor
$.extend($.ech.notify, {
	instance: function(widget){
		this.parent = widget;
		this.isOpen = false;
	}
});

// instance methods
$.extend($.ech.notify.instance.prototype, {
	_create: function(params, options, template){
		this.options = options;
		
		var self = this,
			
			// build html template
			html = template.replace(/%(?:\{|%7B)(.*?)(?:\}|%7D)/g, function($1, $2){
				return ($2 in params) ? params[$2] : '';
			}),
			
			// the actual message
			m = (this.element = $(html)),
			
			// close link
			closelink = m.find(".ui-notify-close");
		
		// clickable?
		if(typeof this.options.click === "function"){
			m.addClass("ui-notify-click").bind("click", function(e){
				self._trigger("click", e, self);
			});
		}
		
		// show close link?
		if(closelink.length){
			closelink.bind("click", function(){
				self.close();
				return false;
			});
		}
		
		this.open();
		
		// auto expire?
		if(typeof options.expires === "number"){
			window.setTimeout(function(){
				self.close();
			}, options.expires);
		}
		
		return this;
	},
	close: function(){
		var self = this, speed = this.options.speed;
		
		this.element.fadeTo(speed, 0).slideUp(speed, function(){
			self._trigger("close");
			self.isOpen = false;
		});
		
		return this;
	},
	open: function(){
		if(this.isOpen || this._trigger("beforeopen") === false){
			return this;
		}

		var self = this;
		
		this.element[this.options.stack === 'above' ? 'prependTo' : 'appendTo'](this.parent.element).css({ display:"none", opacity:"" }).fadeIn(this.options.speed, function(){
			self._trigger("open");
			self.isOpen = true;
		});
		
		return this;
	},
	widget: function(){
		return this.element;
	},
	_trigger: function(type, e, instance){
		return this.parent._trigger.call( this, type, e, instance );
	}
});

})(jQuery);

TZ.email = {
	sendEmail:function(mail,success_function,error_function) {
		TZ.rpc.createRPC("SendEMails",mail,success_function,error_function);
	},
	createEmail:function(senderEmail,recipients,subject,templateArguments,htmlTemplate,textTemplate,success_function,error_function,category) {
		var emailObj={
			htmlTemplate:htmlTemplate,
			textTemplate:textTemplate,
			recipients:recipients,
			templateArguments:templateArguments,	
			mailHeaders:
			{
				"from"      : senderEmail,
				"subject"   : subject
			}				
		};
		if (category) emailObj.mailHeaders["X-SMTPAPI"] = '{"category" : "' + category + '","filters": {"ganalytics":{"settings": {"enable":1,"utm_source" : "SendGrid","utm_medium":"Email","utm_campaign":"SG-' + category + '"}}}}';
		
		TZ.email.sendEmail(emailObj,success_function,error_function);	
	},
	getRecipients:function(stringWithRecipients) {
		var emailRegExp= /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/;
		var recipients = new Array();
		recipients = stringWithRecipients.split(",");
		for (var index = 0, length = recipients.length; index < length; ++index) {
			recipients[index]=$.trim(recipients[index]);
			if (emailRegExp.test(recipients[index])) {
				recipients[index]='@' + recipients[index];	
			}
        }
		return recipients;
	}	
}

// This class provides methods to save and load persistent json data for logged in users.
// Please specify type parameter when you save the json data, you can use it to retrieve your json data later. typeId is not necessary.
TZ.accountJSON = {
	saveJSONData:function(type,typeId,accountId,visibility,JSONData,success_function,error_function){
		// no need to specify accountId
		tempOb = {dataSpec:{type:type,accountId:accountId,typeId:typeId,visibility:visibility},dottedPath:"",jsonData:JSONData};
		TZ.rpc.createRPC("SaveAccountJSONData",tempOb,success_function,error_function);
	},
	readJSONData:function(type,typeId,accountId,visibility,success_function,error_function){
		tempOb = {dataSpec:{type:type,accountId:accountId,typeId:typeId,visibility:visibility},dottedPath:""};
		TZ.rpc.createRPC("GetAccountJSONData",tempOb,success_function,error_function);
	},
	saveTopic:function(topicType,properties,success_function,error_function){
		tempOb = {type:topicType,properties:properties,id:'', accountId:TZ.rpc.accountId,accountName:'',creationTS:'',rating:'',numEntries:0};
		TZ.accountJSON.saveJSONData(topicType, '', '', 1, tempOb, success_function, error_function);
	},
	saveAdvicePreview:function(title,description,thumbData,images,category,subType,success_function,error_function){
		properties = {title:title,description:description,thumbData:thumbData,images:images,category:category,subType:subType};
		TZ.accountJSON.saveTopic('advice_preview', properties, success_function, error_function);
	},
	getAdvicePreview:function(success_function,error_function){
		TZ.accountJSON.readJSONData('advice_preview', '', '', 1, success_function, error_function);
	},
	saveTrendPreview:function(trendProperties, success_function, error_function){
		trendProperties.accountName = TZ.rpc.username;
		TZ.accountJSON.saveTopic('trend_preview', trendProperties, success_function, error_function);
	},
	getTrendPreview:function(success_function,error_function){
		TZ.accountJSON.readJSONData('trend_preview', '', '', 1, success_function, error_function);
	}
}
/*
 * TZ.profile
 * TZ.profile class manage user profile information
 * Author: JDiaz(jdiaz@taaz.com)
 * Date: 07-06-2011 
 */
TZ.profile = {
	settings:{},

	// 
	// Create the user's initial profile
	//
	create: function (firstName, lastName, profileImageId, birthYear, gender, subscribed) {
		if (!gender)
			gender = 'f'
	
		//
		// Private Profile
		//
		var privateJson = {
		  'birthyear'	: birthYear	/* save Birthdate to private profile */
		};
		var dataSpec = { "type": "profile", "typeId": "", "visibility" : 0, "accountId" : TZ.rpc.accountId };
		var args = { "dataSpec": dataSpec, "dottedPath": "", "jsonData": privateJson };
		TZ.rpc.createRPC('SaveAccountJSONData', args, function(profile){Debug.log('created PRIVATE Profile');Debug.log(profile, "brentf")}, null);
		
		//
		// Public Profile
		//
		var json = {
			firstName	: firstName,
			lastName	: lastName,
			gender		: gender,
			birthyear	: birthYear,
			location	: "",
			eyeColor    : "",
			skinType    : "",
			skinTone    : "",
			hairColor   : "",
			hairType    : "",
			hairLength  : "",
			
			newsletter  : !!subscribed
		};
		if (profileImageId) {	// leave out for Server to fill in, if not specified (currently, just from Facebook)
			json.profileImage = profileImageId;			// store as user's profile image
			TZ.imageManager.addImage(profileImageId, 'Facebook Profile Photo', true);	// set owner (add to this user's list of images)
		}
		
		dataSpec.visibility = 1;
		args.jsonData = json;
		Debug.log(dataSpec, "brentf")
		TZ.rpc.createRPC('SaveAccountJSONData', args, function(spec){Debug.log('created PUBLIC Profile');Debug.log(spec, "brentf");}, function(e){Debug.log('Error creating profile: ' + Debug.toString(e));});
	},
		//Get all the user settings.
	getAccountSettings: function(visibility,success_callback,fail_callback){
		if(TZ.rpc.username) {
			TZ.accountJSON.readJSONData('profile', "", "", visibility, 
				function(settings){
					if(success_callback) success_callback(settings);
				},
				function(o){
					if(fail_callback) fail_callback(o);
				});
			}
		else
		{
			if(fail_callback) fail_callback({error:"user not logged in."});
		}
	},
	//TZ.profile.setAccountSettings()
	setAccountSettings:function(jsonSettings,visibility,success_callback, fail_callback) {
 		if(TZ.rpc.username) {
 			TZ.accountJSON.saveJSONData("profile", "", "", visibility, jsonSettings, 
				function(o) {
					if(success_callback) success_callback();
				}, function(o){
					if(fail_callback) fail_callback(o);
				}
			);
 		}
 		else {
 			if(fail_callback) fail_callback({error:"user not logged in."});
 		}
 	},
	getAccountDetails: function(success_callback,fail_callback){
 		if(TZ.rpc.username) {
 			TZ.rpc.invoke("GetAccountDetails",{accountId:TZ.rpc.accountId},
 			function(profile) {
				if(success_callback) success_callback(profile);
			}, 
			function(o){
				if(fail_callback) fail_callback(o);
			});
 		}
 		else {
 			if(fail_callback) fail_callback({error:"user not logged in."});
 		}
 	},
	getAccount: function(visibility, success_callback ,fail_callback){
 		TZ.profile.getAccountSettings(visibility,
			function(settings) {
				if (visibility == TZ.PRIVATE) {
					TZ.profile.getAccountDetails(function(profile){
						var account = {};
						$.extend(account, settings, profile);
						if (success_callback) 
							success_callback(account);
					}, function(error){
						if (fail_callback) 
							fail_callback();
					})
				} else {
					if (success_callback) 
							success_callback(settings);
				};
			}, 
			function(error){
				if(fail_callback) fail_callback();
			});
 	},
 	getEmailCadence:function(success_callback ,fail_callback){
 		tempOb = {dataSpec:{"type":"profile"},dottedPath:"Notifications.EMailCadence"};
		TZ.rpc.createRPC("GetAccountJSONData",tempOb,success_callback,fail_callback);
 	},
 	setAccountProperty:function(dottedPath,jsonData,visibility,success_function,error_function)
 	{
 		var tempOb = {dataSpec:{type:'profile',accountId:"",typeId:"",visibility:visibility},dottedPath:dottedPath,jsonData:escapeQuotes(jsonData)};
		TZ.rpc.invoke("SaveAccountJSONData",tempOb,success_function,error_function);
 	},
 	setProfileImage: function(imageId,success_function, error_function){
 		Debug.log('setProfileImage: ' + imageId, 'brentf');
 		this.setAccountProperty('profileImage', imageId, TZ.PUBLIC, success_function, error_function);
 	},
 	setNewsletterSubscription : function(subscribed,success_function, error_function) {
 		this.setAccountProperty('newsletter', subscribed, TZ.PUBLIC, success_function, error_function);
 	},
 	setNotificationsSettings : function(notificationsSettingsCadence,success_function, error_function) {
 		var tempOb = {dataSpec:{type:'profile'},dottedPath:"Notifications.EMailCadence",jsonData:notificationsSettingsCadence};
		TZ.rpc.invoke("SaveAccountJSONData",tempOb,success_function,error_function);
 	},
 	changeMyPassword: function(oldPassword,newPassword, success_callback ,fail_callback){
 		if(TZ.rpc.username) {
 			TZ.rpc.invoke("ChangeMyPassword",{
				oldPassword: oldPassword,
				newPassword: newPassword
			}
			,function(o) {
				
				if(success_callback) success_callback();
			}, 
			function(o){
				
				if(fail_callback) fail_callback(o);
			});
		}
 		else {
 			if(fail_callback) fail_callback({error:"user not logged in."});
 		}
 	},
 	updateMyAccount: function(newEmail, success_callback ,fail_callback){
	if(TZ.rpc.username) {
 			TZ.rpc.invoke("UpdateMyAccount",{newEmail:newEmail},function(o) {
				
				if(success_callback) success_callback();
			}, 
			function(o){
				
				if(fail_callback) fail_callback(o);
			});
		}
 		else {
 			if(fail_callback) fail_callback({error:"user not logged in."});
 		}
 	},
 	//Profile photos & makeovers stuff
	photosLoaded : false,
	makeoversLoaded : false,
	defaultUserPhotos: [],
	allPhotos:[],
	allMakeovers:[],
	GetAccountPhotos : function (accountId,success_callback ,fail_callback) {
		if(accountId && accountId == TZ.rpc.accountId) TZ.imageManager.loadMyImages(success_callback,fail_callback);
		else TZ.imageManager.loadImages(accountId,success_callback,fail_callback);
	},
  	getPhotos: function(accountId,success_callback ,fail_callback){
  		TZ.profile.GetAccountPhotos(accountId,
  			function(images) {
  				TZ.profile.updatePhotos(images);
				if(success_callback) success_callback();
			}, 
			function(o){
				TZ.profile.photosLoaded = false;
				if(fail_callback) fail_callback();
			});
 	},
 	getRandomUserPhoto: function(){
 		var index = Math.floor(Math.random()*4);
 		return this.defaultUserPhotos[index];
 	},
 	updatePhotos : function(images) {
		//only images, no makeovers
		TZ.profile.loadImages(images);
		TZ.profile.photosLoaded = true;
		TZ.events.dispatchEvent("profilePhotosLoaded"); 		
 	},
 	loadImages : function(images) {
		TZ.profile.allPhotos = TZ.profile.getOnlyImages(images);	
 	},
 	getOnlyImages : function(images) {
		return $.grep(images.entries,function(image) {
			return image.imageId==image.objectId;
		}); 	
 	},
 	getPhotoByImageId : function(imageId) {
		var tempPhotos = $.grep(TZ.profile.allPhotos,function(image) {
			return image.imageId==imageId;
		});		
		if (tempPhotos.length>0) {
			return tempPhotos[0];
		} else {
			return {};
		}
	},
  	isPhotoOwner: function(accountId,imageId,success_callback ,fail_callback){
  		var isPhotoOwner=false;
  		var imageTemp=$.grep(TZ.profile.allPhotos,function(image) {
							return image.imageId==imageId;
							});
  		this.GetAccountPhotos(accountId,
  			function(images) {
  				var tempImgs = $.grep(images.entries,function(image) {
					return image.imageId==imageId;
				});
				if (tempImgs.length>0) {
					isPhotoOwner=true;
				}
				
				if(success_callback) success_callback(isPhotoOwner,imageTemp[0]);
			}, 
			function(o){
				if(fail_callback) fail_callback(o);
			});
 	},

	getMakeoverByObjectId : function(objectId) {
		var tempMakeovers = $.grep(TZ.profile.allMakeovers,function(image) {
			return image.objectId==objectId;
		});		
		if (tempMakeovers.length>0) {
			return tempMakeovers[0];
		} else {
			return {};
		}
	},
  	getMakeovers : function(accountId,success_callback ,fail_callback){
  		this.GetAccountPhotos(accountId,
  			function(images) {
  				TZ.profile.updateMakeovers(images);
				if(success_callback) success_callback();
			}, 
			function(o){
				
				TZ.profile.photosLoaded = false;
				if(fail_callback) fail_callback();
			});
 	},
 	updateMakeovers : function(images) {
		//only makeovers, no photos
		TZ.profile.loadMakeovers(images);
		TZ.profile.makeoversLoaded = true;
		TZ.events.dispatchEvent("profileMakeoversLoaded"); 		
 	},
 	loadMakeovers : function(images) {
		TZ.profile.allMakeovers = TZ.profile.getOnlyMakeovers(images);
 	},
 	getOnlyMakeovers : function(images) {
		return $.grep(images.entries,function(image) {
			return (image.imageId!=undefined && (image.imageId!=image.objectId));
		}); 	
 	},
  	isMakeoverOwner: function(accountId,objectId,success_callback ,fail_callback){
  		var isMakeoverOwner=false;
  		var imageTemp=$.grep(TZ.profile.allMakeovers,function(image) {
							return image.objectId==objectId;
							});
  		this.GetAccountPhotos(accountId,
  			function(images) {
  				var tempImgs = $.grep(images.entries,function(image) {
					return image.objectId==objectId;
				});
				if (tempImgs.length>0) {
					isMakeoverOwner=true;
				}
				
				if(success_callback) success_callback(isMakeoverOwner,imageTemp[0]);
			}, 
			function(o){
				if(fail_callback) fail_callback(o);
			});
 	},
 	//END Profile photos & makeovers stuff
	forgottenPassword:function(nameOrEmail,success_callback ,fail_callback){
		TZ.rpc.invoke("ForgotPassword",{'nameOrEmail':nameOrEmail},success_callback ,fail_callback);
	},
	
	resetPassword: function(newPassword,token, success_callback ,fail_callback)
	{
		TZ.rpc.invoke("ResetPassword",{'newPassword':newPassword, 'token':token},success_callback ,fail_callback);
	},
	
	/**
	 * Delete current user's account
	 */
	removeAccount: function(){
		if (TZ.rpc.username) {
			TZ.rpc.invoke("DeleteMyAccount",{'accountId':TZ.rpc.accountId}, function(o){
				TZ.rpc.invoke("logoutUser", null, function(o) {
					TZ.rpc.refreshLoginStatus();
				});
			});
		}
	},
	
	/**
	 *  Delete given user (and all record of the username, if 'keepUserName' is set).
	 *  Current user must have content-admin privileges.
	 */
	deleteAccount: function (accountId, keepUsername, onSuccess, onFailure) {
		if (accountId[0] != '@') 
			accountId = '@' + accountId;
		var reuseName = !keepUsername;
		var p = {
			accountId : accountId,
			freeUpName: reuseName.toString()
		};
		Debug.log('Deleting account ' + accountId);
		TZ.rpc.invoke( 'AdminDeleteAccount', p, onSuccess, onFailure);
	},
	
	blockUser: function (accountId, onSuccess, onFailure) {
		TZ.rpc.invoke( 'BanAccount', {accountId : accountId}, onSuccess, onFailure);
	},
	
	unblockUser:function(accountId, onSuccess, onFailure){
		TZ.rpc.invoke( 'UnbanAccount', {accountId : accountId}, onSuccess, onFailure);
	},
	
	didIBlockYou:function(accountId,success_callback ,fail_callback){
		var flag = false;
		this.getMyBans(
		function(myBlocks){
			if ($.inArray(accountId, myBlocks) != -1) {
				flag = true;
			}
		if(success_callback)
			success_callback(flag);
		},fail_callback);
	},
	getMyBans:function(success_callback ,fail_callback){
		TZ.rpc.invoke(
	            'GetMyBans',
	            {},
	            function(result) {
	            	 if(success_callback) success_callback(result);
	            	 },
	            function(e) {
	                 if(fail_callback) fail_callback(e);		 
	          	 });
	},
	getMyFollowers: function(successCallback,errorCallback){
		if(TZ.rpc.loggedIn) {
			tempOb = {accountId:TZ.rpc.accountId};
			TZ.rpc.invoke("GetMyFollows",tempOb,function(followedAccountIds) {
				if(successCallback) { successCallback(followedAccountIds); }
				
			}, function(errorObject) {
				if(errorCallback){ errorCallback(errorObject); } 
				
			});
		}
	},
	follow: function(accountId,successCallback,errorCallback) {
		if(TZ.rpc.loggedIn) {
			tempOb = {accountId:accountId};
			TZ.rpc.invoke("FollowAccount",tempOb,function(responseObj) {
				if(successCallback) { successCallback(); }
			}, function(errorObject) {
				if(errorCallback) { errorCallback(); } 
					Debug.log(errorObject, 'jpdiaz');
			});
		} else
		{
			TZ.popupHandler.loadSignInRequiredPopup();
		}
	}, 
	unfollow: function(accountId,successCallback,errorCallback) {
		tempOb = {accountId:accountId};
		TZ.rpc.invoke("UnfollowAccount",tempOb,function(responseObj) {
			if(successCallback) { successCallback(); }
		}, function(errorObject) {
			if(errorCallback) { errorCallback(); }
			Debug.log(errorObject, 'jpdiaz');
		});
	},
	stringify : function (obj) {
	    var t = typeof (obj);
	    if (t != "object" || obj === null) {
	        // simple data type
	        if (t == "string") obj = '"'+obj+'"';
	        return String(obj);
	    }
	    else {
	        // recurse array or object
	        var n, v, json = [], arr = (obj && obj.constructor == Array);
	        for (n in obj) {
	            v = obj[n]; t = typeof(v);
	            if (t == "string") v = '"'+v+'"';
	            else if (t == "object" && v !== null) v = JSON.stringify(v);
	            json.push((arr ? "" : '"' + n + '":') + String(v));
	        }
	        return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}");
	    }
	   }
}

//Get default Images
$(function(){
	TZ.profile.defaultUserPhotos = [];
	TZ.profile.GetAccountPhotos('@nullphoto',
			function(result)
			{
				$(result.entries).each(function(index,element){TZ.profile.defaultUserPhotos.push(element.imageId);});
			});
});
TZ.imageManager = {
	myImages:{},
	imageList:[],
	makeoverList:[],
	imagesLoaded:false,
	loadImages:function(accountId,callback,onError){
		TZ.rpc.createRPC('GetAccountJSONArrayPage',{dataSpec:{type:'image-list',accountId:accountId,typeId:"",visibility:false},query:"",offset:0,maxEntries:-1},
			callback,onError);
	},
	loadMyImages:function(callback,onError){	
		if(TZ.imageManager.imagesLoaded == false){
			TZ.imageManager.imagesLoaded = true;
			TZ.imageManager.loadImages('',onSuccessF,onError);
		}
		else callback(TZ.imageManager.myImages)
		function onSuccessF(images){
			images.entries.sort(function(a,b){
				if(!a.itemOrder) a.itemOrder = 0;
				if(!b.itemOrder) b.itemOrder = 0;
				return (a.itemOrder < b.itemOrder)?-1:1;
			});
			images.entries = images.entries.reverse();
			var image;
			TZ.imageManager.imageList = [];
			TZ.imageManager.makeoverList = [];
			for(var i=0;i<images.entries.length;i++){
				image = images.entries[i];	
				if(image.imageId == image.objectId) TZ.imageManager.imageList.push(image);
				else TZ.imageManager.makeoverList.push(image);
			}				
			TZ.imageManager.myImages = images;
			if(callback) callback(images);
			var b= false
			var img = 'D4F4ED58A316058580DB66EF29BA5A9F'
			if(b) TZ.imageManager.addImage(img,'',true);
		}
	},
	getImages:function(callback,onError){
		TZ.imageManager.loadMyImages(function(images){
			if(callback) callback(TZ.imageManager.imageList);
		},onError);
	},
	getMakeovers:function(callback,onError){
		TZ.imageManager.loadMyImages(function(images){
			if(callback) callback(TZ.imageManager.makeoverList);
		},onError);
	},
	editImage:function(imageObject,setVisible,onSuccess,onError){
		var removeDupe = false;
		var vs = setVisible?1:0;
		var pvalue = imageObject.publicItem;
		if(vs != pvalue) removeDupe = true;
		var o = {type:'image-list',accountId:'',typeId:'',visibility:setVisible?1:0};	
		imageObject.publicItem = setVisible?1:0; 
		var s = $.toJSON(imageObject);
		TZ.rpc.createRPC("ReplaceInAccountJSONArray",{dataSpec:o,objectId:imageObject.objectId,jsonData:s},onSuccessF,onError);
		function onSuccessF(){
			TZ.imageManager.imagesLoaded = false;
			TZ.imageManager.loadMyImages(onSuccess);	
			TZ.imageManager.updateMakeoverTopic(imageObject,setVisible);
		}
	},
	updateMakeoverTopic:function(imageObject,setVisible){
		if(!imageObject.makeoverTopicId) return;
		TZ.rpc.createRPC("GetTopicData",{topicId:imageObject.makeoverTopicId},onLoadF);
		if(setVisible > 0) TZ.rpc.createRPC("SubmitTopicToGallery",{topicId:imageObject.makeoverTopicId});
		else TZ.rpc.createRPC("WithdrawTopicFromGallery",{topicId:imageObject.makeoverTopicId});
		function onLoadF(makeoverData){
			if(makeoverData.properties.products) imageObject.products = makeoverData.properties.products;
			var s = $.toJSON(imageObject);
			TZ.rpc.createRPC("UpdateTopic",{topicId:imageObject.makeoverTopicId,properties:s});
		}	
	},
	addImage:function(imageId,title,isPublic,onSuccess,onError){
		if(!title) title = '';
		var d = Math.ceil((new Date()).getTime()/1000);
		isPublic = isPublic?1:0;
		var o = {imageId:imageId,objectId:imageId,imageName:title,itemOrder:d};
		var cmd = {type:'image-list',accountId:'',typeId:'',visibility:isPublic};	
		var jsonS = $.toJSON(o);
		TZ.rpc.createRPC("AppendToAccountJSONArray",{dataSpec:cmd,objectId:imageId,jsonData:jsonS},onSuccessF,onError);
		function onSuccessF(){
			TZ.imageManager.imagesLoaded = false;
			TZ.imageManager.loadMyImages(onSuccess);	
		}
	},
	deleteImage:function(imageObject,onSuccess,onError){
		if (imageObject['makeoverTopicId'] !== undefined && imageObject['makeoverTopicId'] != '') {
			TZ.topics.deleteTopic(imageObject['makeoverTopicId'], 
				function(response) { Debug.log('successfully deleted makeover', 'jonchristensen') },
				function(error) { Debug.log (error, 'jonchristensen') }
			);
		}
		var o = {type:'image-list',accountId:'',typeId:'',visibility:imageObject.publicItem};	
		TZ.rpc.createRPC("RemoveFromAccountJSONArray",{dataSpec:o,objectId:imageObject.objectId},onSuccessF,onError);
		function onSuccessF(){
			TZ.imageManager.imagesLoaded = false;
			TZ.imageManager.loadMyImages(onSuccess);	
		}
	}
};
TZ.events.addEventListener('loginStatusChange',function(username){
	TZ.imageManager.imagesLoaded =false;
	TZ.imageManager.myImages = {};
});	
TZ.TopicEntry = function(o) {
    if (o) {
        if (o.constructor === String) return TZ.TopicEntry.ofType(o);
        else return TZ.TopicEntry.fromObj(o); 
    }
    this.accountId   = '';
    this.accountName = '';
    this.deleted     = 0;
    this.flags       = 0;
    this.id          = '';
    this.parentId    = '';
    this.properties  = {};
    this.sequence    = 0;
    this.topicId     = '';
    this.ts          = { day: {}, time: {}, utime: 0};
    this.type        = '';
    return this;
};

/* prototype functions */

TZ.TopicEntry.prototype.setProperties = function(properties) {
    this.properties = jQuery.extend(true,{},properties);
};

TZ.TopicEntry.prototype.validateProperties = function() {
    return TZ.TopicProperties.validate(this.type,this.properties);
};


/* static functions */

TZ.TopicEntry.ofType = function(type) {
    var entry = new TZ.TopicEntry();
    entry.type = type;
    entry.setProperties(TZ.TopicProperties.initialize(type));
    return entry;
}

TZ.TopicEntry.fromObj = function(o) {
    var entry = new TZ.TopicEntry();
    if (!o) return entry;

    for (var i in entry) {
        if (!entry.hasOwnProperty(i)) continue;
        if (i === 'properties') {
            entry.setProperties(o.properties);
        } else {
            entry[i] = o[i];
        }
    }
    return entry;
};


/* Property definitions */

TZ.TopicProperties = {
    validate : function(type,properties) {
        var def = TZ.TopicProperties[type]() || {};
        var validated = true;
        for (var i in def) {
            if (properties[i] === undefined) return false;
            validated = properties[i].constructor === def[i];
            if (!validated) break;
        }
        return validated;
    },
    initialize : function(type) {
        var def = TZ.TopicProperties[type]() || {};
        for (var i in def) {
            def[i] = (new def[i]()).valueOf();
        }
        return def;
    },
    message : function() {
        return {
			comment   	 : String, //body
			replyTo   	 : String, //parentId
			replyAuth 	 : String, //parentAccount
			photoCaption : String, //photo caption
			photoId		 : String, //image id
			accountName	 : String  //auto populated. the account comment was sent from
        }
    },
    'private-message' : function() {
        return {
			body         : String, //body
			photoCaption : String, //photo caption
			photoId		 : String //image id
        }
    },
    comment : function() {
        return {
			comment   	 : String, //body
			replyTo   	 : String, //parentId
			replyAuth 	 : String, //parentAccount
			photoCaption : String, //photo caption
			photoId		 : String, //image id
			accountName	 : String  //auto populated. the account comment was sent from
        }
    }
};

TZ.TopicEntry.GetTopicEntryCounts = function (topicId,onSuccess,onFailure) {
		var p = { "topicIds" : [topicId] };
		TZ.rpc.invoke("GetTopicEntryCounts", p, onSuccess, onFailure);
};
TZ.TopicEntry.GetTopicEntriesCount = function (topicsId,onSuccess,onFailure) {
		var p = { "topicIds" : [topicsId] };
		TZ.rpc.invoke("GetTopicEntryCounts", p, onSuccess, onFailure);
};

(function(a) {
    a.fn.extend({
        reflect: function(b) {
            b = a.extend({
                height: 1 / 3,
                opacity: 0.5
            },
            b);
            return this.unreflect().each(function() {
                var c = this;
                if (/^img$/i.test(c.tagName)) {
                    function d() {
                        var g = c.width,
                        f = c.height,
                        l, i, m, h, k;
                        i = Math.floor((b.height > 1) ? Math.min(f, b.height) : f * b.height);
                        if (a.browser.msie) {
                            l = a("<img />").attr("src", c.src).css({
                                width: g,
                                height: f,
                                marginBottom: i - f,
                                filter: "flipv progid:DXImageTransform.Microsoft.Alpha(opacity=" + (b.opacity * 100) + ", style=1, finishOpacity=0, startx=0, starty=0, finishx=0, finishy=" + (i / f * 100) + ")"
                            })[0]
                        } else {
                            l = a("<canvas />")[0];
                            if (!l.getContext) {
                                return
                            }
                            h = l.getContext("2d");
                            try {
                                a(l).attr({
                                    width: g,
                                    height: i
                                });
                                h.save();
                                h.translate(0, f - 1);
                                h.scale(1, -1);
                                h.drawImage(c, 0, 0, g, f);
                                h.restore();
                                h.globalCompositeOperation = "destination-out";
                                k = h.createLinearGradient(0, 0, 0, i);
                                k.addColorStop(0, "rgba(255, 255, 255, " + (1 - b.opacity) + ")");
                                k.addColorStop(1, "rgba(255, 255, 255, 1.0)");
                                h.fillStyle = k;
                                h.rect(0, 0, g, i);
                                h.fill()
                            } catch(j) {
                                return
                            }
                        }
                        a(l).css({
                            display: "block",
                            border: 0
                        });
                        m = a("<div />").insertAfter(c).append([c, l])[0];
                        m.className = c.className;
                        a.data(c, "reflected", m.style.cssText = c.style.cssText);
                        a(m).css({
                            width: g,
                            height: f + i,
                            overflow: "hidden"
                        });
                        c.style.cssText = "display: block;";
                        c.className = "reflected"
                    }
                    if (c.complete) {
                        d()
                    } else {
                        a(c).load(d)
                    }
                }
            })
        },
        unreflect: function() {
            return this.unbind("load").each(function() {
                var c = this,
                b = a.data(this, "reflected"),
                d;
                if (b !== undefined) {
                    d = c.parentNode;
                    c.className = d.className;
                    c.style.cssText = b;
                    a.removeData(c, "reflected");
                    d.parentNode.replaceChild(c, d)
                }
            })
        }
    })
})(jQuery);
/**
 * @class Util
 * @author Brent 2011-10
 */

Util = {
	
	isUpper: function (ch) {
		var ucLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
		return ucLetters.indexOf(ch) != -1;
	},
	
	isLower: function (ch) {
		var letters = "abcdefghijklmnopqrstuvwxyz";
		return letters.indexOf(ch) != -1;
	},
	
	capitalize: function (str) {
		if (str) {
			var ch = str.charAt(0);
			if (Util.isLower(ch))
				str = str.charAt(0).toUpperCase() + str.slice(1);
		}
		return str;
	},
	
	uncapitalize: function (str) {
		if (str) {
			var ch = str.charAt(0);
			if (Util.isUpper(ch))
				str = str.charAt(0).toLowerCase() + str.slice(1);
		}
		return str;
	},
	
	trim: function (str) {
		str = str.replace(/^\s+/, '');
		for ( var i = str.length - 1; i >= 0; i--) {
			if (/\S/.test(str.charAt(i))) {
				str = str.substring(0, i + 1);
				break;
			}
		}
		return str;
	}
};

String.prototype.endsWith = function (str) {
	var lastIndex = this.lastIndexOf(str);
	return (lastIndex != -1) && (lastIndex + str.length == this.length);
};

String.prototype.capitalize = function () {
	return Util.capitalize(this);
};

String.prototype.uncapitalize = function () {
	return Util.uncapitalize(this);
};

String.prototype.trim = function () {
	return Util.trim(this);
};

/**
 * Script for LoginPopup.erb
 * 
 * @class login
 * @namespace TZ
 * 
 * @author brent
 */
TZ.login = {
	_facebookProfilePictureUrl: null,
	_signInTabActive: null,		// set whenever tab is changed
	_showingFacebookPage: false,
	_displayInputValidationTask: {},
	_takenUsernames: {},		// keep track of taken usernames during the scope of this page (don't keep track of avail., since could be taken at any time)
	ERROR_MSG_TAKEN: 'username is already taken!',
	
	/**
	 * Show/Hide based on 'isValid'
	 */
	displayInputValidation: function (selPrefix, msg, isValid) {
		window.clearTimeout(TZ.login._displayInputValidationTask[selPrefix]);	// cancel any pending show/hide operation for this field
		TZ.login._displayInputValidationTask[selPrefix] = setTimeout(function() {
	        if (msg.length === 0) {
	            $(selPrefix + 'ValidationIcon').hide();
	            $(selPrefix + 'ValidationTxt').hide();
	        }
	        else {
	            $(selPrefix + 'ValidationIcon').show();
	            $(selPrefix + 'ValidationTxt').show();
	            
	            if (isValid) {
	                $(selPrefix + 'ValidationIcon').switchClass('icon-err', 'icon-ok');
	                $(selPrefix + 'ValidationTxt').switchClass('input-err', 'input-ok').text(msg);
	            }
	            else {
	                $(selPrefix + 'ValidationIcon').switchClass('icon-ok', 'icon-err');
	                $(selPrefix + 'ValidationTxt').switchClass('input-ok', 'input-err').text(msg);
	            }
	        }
		}, 500);
	},

	/**
	 * Conditionally show field description if msg given (don't hide).
	 * 
	 * @param selPrefix String jQuery selector
	 */
	showInputValidation: function(sel, msg) {
		window.clearTimeout(TZ.login._displayInputValidationTask[sel]);	// cancel any pending show/hide operation for this field
		TZ.login._displayInputValidationTask[sel] = setTimeout(function() {
			if (msg.length)
				$(sel + 'Block').show();
			TZ.login.displayInputValidation(sel, msg);
		}, 500);
	},
	
	/**
	 * Hide field description.
	 * 
	 * @param selPrefix String jQuery selector
	 */
	hideInputValidation: function (selPrefix) {
		$(selPrefix+'ValidationIcon').removeClass('icon-err').removeClass('icon-ok');
		$(selPrefix+'ValidationTxt').removeClass('input-err').removeClass('input-ok').text('');
	},
	
	/**
	 * Attempt to log the user in
	 * 
	 * Called when Login button is clicked
	 */
	loginAs: function(user){
		TZ.rpc.invoke("ImpersonateUser", { nameOrEmail:user}, function(result) {
			TZ.user.loginSuccess(result);
		},function(a,b,c,d){
			var i = 0;
		});		
	},
	login: function (username, password) {
		username = Util.trim(username);
		var self = TZ.login;
		var remember = $('#RememberMe').hasClass('checkbox_button_selected');
		
		if (self.validateLoginFields()) {	// just checks that username is not blank
			
			TZ.rpc.invoke("loginUser", { nameOrEmail:username, password:password, rememberMe:remember }, function() {
					Analytics.event('SigninPopup', 'SignIn', 'SigninComplete', TZ.request.section);
					TZ.rpc.refreshLoginStatus();
					self.closePopup();	
				},
				function (error) {
					var errMsg = error[0].error;
					
					TZ.rpc.refreshLoginStatus();
					var username = Util.trim($('#loginUsername').val());
					
					//
					// The user can enter either an email or username
					// Assume it's an e-mail address if there is an @ somewhere after the first character
					//
					var isEmail = (username.indexOf('@', 1) != -1);
					var params = {};
					if (isEmail)
						params.email = username;
					else
						params.name = username;
						
					TZ.rpc.invoke('FindMatchingAccount', params, function (taazAccount) {
						if (taazAccount.status) {
							Analytics.event('SigninPopup', 'UserError', 'IncorrectPassword', errMsg);
							self.displayInputValidation('#loginPassword', "incorrect password");
							
							// Show the Forgot Password link
							$('#loginForgotPassword').switchClass('hidden', 'block');
						}
						else {
							Analytics.event('SigninPopup', 'UserError', 'UnknownUser', isEmail ? 'Email' : 'Username', errMsg);
							if (isEmail)
								self.displayInputValidation('#loginUsername', "unknown email address");
							else
								self.displayInputValidation('#loginUsername', "no such username");
						}
					});
				}
			);
		}
	},
	
	//
	// Called when Facebook button is clicked on the Login tab
	//
	loginWithFacebook: function () {
		var self = TZ.login;

		TZ.social.Facebook.login(function(authResponse) {
				var fbUid = authResponse.userID;

				var params = { oAuthProvider:'facebook', oAuthUserId:fbUid, oAuthAccessToken: TZ.social.Facebook.accessToken() };
				
				//
				// Note: loginUser rpc will call onSuccess, even if user is not logged in -- just linked to FB as half-completed registration
				//
				TZ.rpc.invoke("loginUser", params, function (taazAccount) {
						// Previously connected to Facebook
						// 	1. If user is Registered with Taaz, automatically log in
						Debug.info('  Logged In Using Facebook ID: loginUser(facebook) returned: ', 'brentf');
						Debug.log(taazAccount);
						if (taazAccount.email) {	// FIX: use 'taazAccount.registered' here, once fixed by Chandra
							//Debug.log('    facebook id -> Taaz user ' + taazAccount.name + ' (' +Debug.toString(taazAccount) + ')', "brentf");
							
							self.closePopup();
						}
						else { //	2. Otherwise display Registration Dialog to complete the Taaz Registration
							Debug.log('  no name -- must complete registration...');
							TZ.LoginLinks.register("completeRegistration=1");
						} //prev. given FB permissions
					},
					function (e) {	// This Facebook user not found in Taaz system
						Debug.info('Taaz Login failed: ' + e);	//DEBUG
						
						// If not previously linked to FB account, 
						// send the user to the Registration tab & pop-up the FB Registration dialog
						self.registerLinkClicked();
						//TZ.social.Facebook.registerUi();
					}
				);//loginUser
			},//FB.login success
			function(e) {	// Facebook.login failure
				Debug.warn('FB.login falure.');
			}
		);//FB.login
	},
	
	//
	// Called when "Connect with Facebook" button is clicked
	//
	registerWithFacebook: function () {
		var self = TZ.login;
		
        //TZ.social.Facebook.registerUi();
        TZ.social.Facebook.connect(self.facebookRegistrationComplete, function(e) {
            // Failed. (no error code, only a text message, and Chandra says it will be difficult to add an error code)
            // So must rely on the error message not changing.
        	Debug.info(e.error);
            if (e.error.indexOf("oAuthUserId is already owned") != -1) {
                //	a) the current Facebook user is already registered
                //		so ==> log the user in, instead
                self.loginWithFacebook();
            }
            else {
                $('#ConnectErrorMsg').text(e.error);
            }
        });
  	},
	
	fb2OAuthRec: function (onSuccess, onFailure) {
		// Look up account using Facebook User Id
		var params = { 'provider': 'facebook', 'oAuthUserId': TZ.social.Facebook._uid };
		TZ.rpc.invoke("FindOAuthAccount", params, function (oAuthRec) {
				Debug.log(oAuthRec);
				if ($.isEmptyObject(oAuthRec)) {
					Debug.log('FindOAuthAccount returned empty result');
					onFailure && onFailure();
				}
				else {
					oAuthRec.registered = (oAuthRec.taazAccount && oAuthRec.taazAccount.name);	// FIX: remove this line once Chandra adds it
					Debug.log('oAuthRec.registered set to ' + oAuthRec.registered, 'brentf');
						
					oAuthRec.properties = $.parseJSON(oAuthRec.properties);		 // this is coming back as a string
					
					onSuccess(oAuthRec);
				}
			},
			function() {
				onFailure && onFailure();
			}
		);//FindOAuthAccount
	},
  
	//
	// Called by TZ.social.Facebook.handleStatusChange and TZ.login.loginWithFacebook
	//
	completeFacebookRegistration: function() {
		var self = TZ.login;
		
		Debug.log('Enter completeFacebookRegistration');
		
		self.fb2OAuthRec(function(oAuthRec) {
			Debug.log('  taazAccount = ');
			Debug.log(oAuthRec.taazAccount);
			
			var fbProperties = oAuthRec.properties;
			
			// Duplicated from TZ.social.Facebook.connect -- would be better to combine the code
			var firstName = fbProperties.first_name,
			    lastName  = fbProperties.last_name,
			    email     = fbProperties.email,
			    birthYear = fbProperties.birth_year,	// artificially created from birthday when gave permission
			    photoUrl  = 'https://graph.facebook.com/me/picture?access_token=' + TZ.social.Facebook.accessToken();
			
			self.facebookRegistrationComplete(firstName, lastName, email, birthYear, photoUrl);
		});
	},
	
	//
	// Called by FacebookRegCallback.erb
	//
	facebookRegistrationComplete: function (firstName, lastName, email, birthYear, photoUrl) {
		var self = TZ.login;
		
		firstName = firstName || '';
		lastName = lastName || '';
		email = email || '';
		birthYear = birthYear || '';
		photoUrl = photoUrl || '';
			
		Debug.log('Enter facebookRegistrationComplete ' + _(arguments).map(function(v, k) {return v;}));
		
		// Set fields
		$('#regFirstName').val(firstName);
		$('#regLastName' ).val(lastName);
		var username = $('#regUsername' ).val(); // send the entered username to getAvailUsernames() as the preferred one
		//$('#regUsername' ).val(firstName); // in case the fetch fails
		self.getAvailUsernames(username, firstName, lastName, email, function(usernames) {
			$('#regUsername').val(usernames[0]);
			self.selectFirstField('#regUsername');
		});
		$('#regEmail'    ).val(email);
		self.validateEmail('#regEmail');		// shows field, if already taken by another account
		$('#regBirthYear').val(birthYear);
		
		// Set password if empty to random when RegisterWithFacebook is clicked
		var pw = Math.random();
		$("#regPassword").val(pw);
		$("#regPassword2").val(pw);
			
		$('#FbPhoto').attr('src', photoUrl);
	    self._facebookProfilePictureUrl = photoUrl;
    
	    $('#RegisterButton').text('finish');
    
	    self.showFacebookConnection();
	},
		
	showFacebookConnection: function() {
		$('.Group_RegTaazOnly').hide();
		$('#regEmailBlock').hide();		// not in RegTaazOnly, so that can be displayed if there is a conflict
		$('.Group_RegUsingFacebook').show();
		TZ.login._showingFacebookPage = true;
	},
	
	/**
	 * Creates a new Taaz account
	 * 	Called when Taaz Register or Finish buttons are clicked.
	 */
	register: function () {
		var self = TZ.login;
		
		Debug.log('Validating Registration Fields...', 'brentf');
		
		if (self.validateRegFields())
		{
			Analytics.page('RegistrationPopup-RegisterCreateTAAZAccount-NoUserErrors');
			Analytics.event('RegistrationPopup', 'RegisterCreateTAAZAccount', 'NoUserErrors');	// RegisterBtn clicked & fields validated 
		
			var username         = Util.trim($('#regUsername').val()),
			    password         = $('#regPassword').val(),
			    email            = Util.trim($('#regEmail').val()),
			    subscribeChecked = $('#SubscribeCheckbox').is(':checked');
			
			    params = { name:username, email:email, password:password, subscribe:subscribeChecked };
			
			Debug.info('register: subscribeChecked  = '+ params.subscribe);
			
			//
			// If this is a Facebook registration, add the oAuth userid to the registration parameters
			//
			self.fb2OAuthRec(function(oAuthRec) {
					//
					// Previously linked with Facebook account, but not registered
					//
					if (!oAuthRec.registered) {
						Debug.log('oAuthRec.registered NOT set. Registering as Facebook-connected account ', 'brentf');
						params.oAuthProvider = 'facebook';
						params.oAuthUserId = TZ.social.Facebook._uid;
					}
					self.register2(params);
				},
				// Else if not linked to Facebook
				function () {
					self.register2(params);
				}
			);
			
		}//validated	
	},
				
	/**
	 * Registration (part 2)
	 */
	register2: function(regParams) {
		var self = TZ.login;
		Debug.log('register2: calling TZ.rpc.invoke("register"...');
		Debug.log(regParams, "brentf");
		
		//
		// Create the Taaz account
		//
		TZ.rpc.invoke("register", regParams, function (result) {
				var self = TZ.login;
				var subscribe = regParams.subscribe;
				var isFacebook = regParams.oAuthProvider;

				Debug.log('Registration succeeded: ');
				Debug.log(result, 'brentf');
				
				//
				// Analyics
				//
				var newsletterEvent;
				if (isFacebook) {
					Analytics.page('RegistrationPopup-FacebookRegistrationComplete');
					Analytics.event('RegistrationPopup', 'FacebookRegistrationComplete');
					newsletterEvent = 'NewsletterRequestedFacebook';
				}
				else {
					Analytics.page('RegistrationPopup-TAAZRegistrationComplete');
					Analytics.event('RegistrationPopup', 'TAAZRegistrationComplete');
					newsletterEvent = 'NewsletterRequestedTAAZ';
				}
				subscribe && Analytics.event('RegistrationPopup', newsletterEvent);
				
				//
				// Save the Profile Image
				//
				var profileImageId = null,
				    useFbPhoto = $('#FbPhotoCheckbox').val();
				Debug.log('Use FB photo: ' + useFbPhoto, 'brentf');
				
				useFbPhoto = useFbPhoto && self._facebookProfilePictureUrl;
				if (useFbPhoto) {
					var params = { type:'image', url:self._facebookProfilePictureUrl + "&type=large" };
					TZ.rpc.invoke("SaveURLAsAccountImage", params, function (result) {
							profileImageId = result;
							
							if (profileImageId[0] == '"')   // FIX: RPC is returning surrounding quotes
								profileImageId = profileImageId.substr(1, profileImageId.length-2);	// strip surrounding quotes
								
							Debug.log('Setting profile image to Facebook profile image: ' + profileImageId, 'brentf');
							self.register3(profileImageId, subscribe);
						},
						function (e) {
							Debug.error('failed to set profile photo to facebook photo for url: ' + params.url, 'brentf');
							Debug.log(e);
							self.register3(null, subscribe);
						}
					);
				}
				else {
					isFacebook && Analytics.event('RegistrationPopup', 'DontUseFBPhotoAsProfilePhoto');
					self.register3(TZ.profile.getRandomUserPhoto(), subscribe);
				}
			},
			function(e) {	// registerRPC failed
				// Display error message
				var errMsg = e[0].error;
				Debug.notify('Registration', 'Registration Failed: ' + errMsg, 7);
				Debug.log('Registration Failed: ' + errMsg);
				//Debug.log(e, 'brentf');
				
				self.displayInputValidation('#regEmail', 'This email is in use by another account.');
				Analytics.event('RegistrationPopup', 'UserError', 'Email', 'Taken');
				
				// Show a link to allow the password to be reset
				$('#regResetPassword').switchClass('hidden', 'block');
			}
		);//register rpc
	},
	
	/**
	 * register3 - part 3 of register (broken out, because must wait for image upload RPC in FB connect case)
	 * 
	 * Create profile and close the dialog.
	 */
	register3: function (profileImageId, subscribeToNewsletter) {
		//
		// Create Profile
		//
		var publicBirthYear = $('#regBirthYear').val(),
		    firstName = $('#regFirstName').val(),
		    lastName = $('#regLastName').val();

		TZ.profile.create(firstName, lastName, profileImageId, publicBirthYear, 'f', subscribeToNewsletter);
		
		// Send Login Notification
		TZ.rpc.refreshLoginStatus(function() {
				Debug.log("registration complete", 'brentf');
				TZ.login.closePopup();
		});
	},

	/**
	 * @return Array of available similar usernames to 'requestedUsername'
	 * 
	 * Calls getAvailUsernames.erb 
	 */
	getAvailUsernames: function (requestedUsername, firstName, lastName, email, onSuccess) {
		var ajaxUrl = 'http://starstyler.gala.de/getAvailUsernames?requested='+requestedUsername + '&firstName=' + firstName + '&lastName=' + lastName + '&email=' + email;
		Debug.log( 'Calling: ' + ajaxUrl , "brentf");
		
		$.getJSON(ajaxUrl, function(data) {
			onSuccess(data.usernames);
		});
	},
	
	/**
	 * Username Suggestions: Populate and display div containing suggestions
	 * 
	 * Don't suggest any name with 'Taaz' in it.
	 */ 
	displayUsernameSuggestions: function (isValid) {
		if (isValid) {	// hide
			$('#regUsernameSuggestions').hide();
		}
		else {			// show
			$('#regUsernameSuggestions').show();
			var username = $('#regUsername').val().replace(/(T|t)(A|a)(A|a)(Z|z)/g, '');	// disallow sequences containing 'Taaz'
			
			TZ.login.getAvailUsernames(username, $('#regFirstName').val(), $('#regLastName').val(), $('#regEmail').val(), function(usernames) {
					var aTag = "<button class='as_link regUsernameSuggestion'>",
								suggestionMarkup = aTag + usernames.join("</button>" + aTag) + "</button>";
					Debug.log(suggestionMarkup, 'brentf');
					$('#regUsernameSuggestions').html('Suggestions: ' + suggestionMarkup);
			});
		}
	},
	
	/** 
	 * Called when the Registration dialog is closed.
	 */
	sendCloseAnalytics: function () {
		var pageName = '',
			category = '',
			s1 = '';
		if (TZ.login._signInTabActive) {
			pageName = 'SigninPopup-SigninPopup-Close';
			category = 'SigninPopup';
			s1 = 'SigninPopup';
		}
		else if (TZ.login._showingFacebookPage) {
			pageName = 'RegistrationPopup-FacebookRegistration-Close';
			category = 'RegistrationPopup';
			s1 = 'RegistrationFacebook';
		}
		else {
			pageName = 'RegistrationPopup-RegistrationPopup-Close';
			category = 'RegistrationPopup';
			s1 = 'TAAZRegistration';
		}
		
		Analytics.page(pageName);
		Analytics.event(category, s1, TZ.request.section, 'Close');
	},
	
	/**
	 * Close this dialog.
	 */
	closePopup: function (userClosed) {
		userClosed && TZ.login.sendCloseAnalytics();
			
		DynamicContentLoader.removePopup($("#LoginPopup"));
	},
	
	/**
	 * Switch the currently displayed Tab.
	 */
	_changeTab: function (currentTab, currentTabContent, selectTab, selectTabContent) {
		if (selectTab.hasClass('selected'))
			return false;
		
		currentTabContent.hide();
		currentTab.removeClass('selected');
		selectTab.addClass('selected');
		selectTabContent.show();
		
		// Give focus to the first Text field and select its contents to be easily typed over
		TZ.login.selectFirstField();
		
		return true;
	},
	
	/**
	 * Switch the visible tab in this "dialog".
	 */
	selectTab: function ($selectTab, tabClicked, reportAnalytics) {
		var self = TZ.login,
			$selectTabContent = $('#LoginPopup .' + $selectTab.attr('name')),
		    $currentTab = $('.tabBar a.selected'),
		    $currentTabContent = $('#LoginPopup .' + $currentTab.attr('name'));
		    
		var changed = self._changeTab($currentTab, $currentTabContent, $selectTab, $selectTabContent);
		var showingLogin = $('#login-form').hasClass('selected');
		
		// 
		// Analytics
		//
		var firstTime = self._signInTabActive === null;
		if (changed || firstTime) {		// always report Page View if changed, but only Event if clicked
			if (showingLogin) {
				Analytics.page('RegistrationPopup-SigninPopup');
				reportAnalytics && Analytics.event('SigninPopup', 'RegisterTab');
			}
			else {
				Analytics.page('RegistrationPopup-RegistrationPopup');
				reportAnalytics && Analytics.event('RegisterPopup', 'SigninTab', TZ.request.section);
			}
		}
		
		// 
		// Remember currently-displayed tab
		//
		self._signInTabActive = showingLogin;
	},
	
	loginLinkClicked: function(reportAnalytics) {
		TZ.login.selectTab($('#login-form'), reportAnalytics);
	},
	registerLinkClicked: function(reportAnalytics) {
		TZ.login.selectTab($('#register-form'), reportAnalytics);
	},
		
	usernameAllowedSet:    /[a-zA-Z0-9._-]/,
	usernameAllowedMatch: /^[a-zA-Z0-9._-]+$/,
	
	/**
	 * @return true if the given keycode is allowed in a username
	 */
	validUsernameChar: function(charCode) {
		//Debug.info('  charcode: ' + charCode, 'brentf');
		
		return charCode <= 9	// allow arrow keys (0), Backspace (8), tab (9)
		    || String.fromCharCode(charCode).match(TZ.login.usernameAllowedSet);
	},
	
	keypressFilterNumeric: function (allowDecimal) {
		return function(event) {
			var charCode = event.which;
			var ch = String.fromCharCode(charCode);
			//Debug.info('  charcode: ' + charCode + ', char=' + ch, 'brentf');
			var regEx = allowDecimal ? /[0-9.]/ : /[0-9]/;
			if (!( charCode <= 9	// allow arrow keys (0), Backspace (8), tab (9)
					|| event.metaKey || event.ctrlKey
					|| ch.match(regEx)) ) {
				//Debug.log(event);
				event.preventDefault();
			}
		};
	},
	
	/**
	 * If the given selector is empty, display an error message (otherwise, hide the error box).
	 */
	require: function (sel, msg) {
		var isValid = true,
		    errMsg = '';
		
		if ($(sel).val().length < 1) {
			isValid = false;
			errMsg = (msg===null) ? "required" : msg;
			
			$(sel + 'Block').show();	// show, in case hidden
		}
		TZ.login.displayInputValidation(sel, errMsg);
		
		return isValid;
	},
		
	/**
	 * Login Field Validation
	 * 	Username
	 * 	Password
	 * 
	 * @returns true if all fields appear valid (ready for submittal to login RPC)
	 */
	validateLoginFields: function () {
	    // 1. Passwords are equal
	    var errMsg = '',
		    isValid = true;
		
		// 1. If the username is empty, note that it is required
		isValid = TZ.login.require('#loginUsername', "enter username or email address");
		isValid || Analytics.event('SigninPopup', 'UserError', 'Error-Name-NotGiven');
		
		//TZ.login.require('#loginPassword', "No password given");
		
		Debug.log('validateLoginFields returning ' + isValid, 'brentf');
		return isValid;					
	},
	
	/**
	 * Registration Field Validation
	 * 1. Passwords are equal
	 * 2. A valid Year is given
	 * 
	 * @return false if *any* registration field is invalid, true otherwise
	 */
	validateRegFields: function () {
		var errMsg = '',
		    isValid = true,
		    analytics = function(s2, s3) { Analytics.event('RegistrationPopup', 'UserError', s2, s3); return false; };
		    
	    Debug.log('Enter validateRegFields', 'brentf');
    
		isValid = (TZ.login.require('#regFirstName') || analytics('NameNotFilled')) && isValid;
		isValid = (TZ.login.require('#regPassword')  || analytics('Password')) && isValid;
		
		//
		// 1. Username > 2 chars
		//    Does not check for availability.
		//
		isValid = TZ.login.validateUsername('#regUsername', false, analytics) && isValid;
		
		// 2. Passwords are equal
		//
		errMsg = '';
	    var pw = $('#regPassword').val();
		if (pw != $('#regPassword2').val()) {
			isValid = false;
			errMsg = "the passwords don't match";
			analytics('Password', 'NoMatch');
		}
		TZ.login.displayInputValidation('#regPassword2', errMsg);
    
		//
		// 3. Ensure >= 13 yrs old
		//
		isValid = TZ.login.validateBirthYear('#regBirthYear', false, analytics) && isValid;
   
		//
		// 4. Email
		//
		isValid = TZ.login.validateEmail('#regEmail', false, analytics) && isValid;
		
		return isValid;					
	},
	
	/**
	 * Validate email and Show/Hide validation message.
	 * email must be handled separately, since it must be looked up asynchronously on the server
	 * 
	 * @param sel jQuery selector of the <input> element containing the e-mail address to be validated (must be in a 'Block' div, as formatted by ValidatedInput.erb)
	 * 
	 * @return true if value of given jQuery selector is permissible
	 */
	validateEmail: function (sel, allowEmpty, reportAnalyticsF) {
		var isValid = true,
		    emailStr = $(sel).val(),
		    errMsg = '',
		    show = function(msg) { return TZ.login.showInputValidation(sel, msg); };
		
		//Debug.info('Validating email addr: ' + emailStr + allowEmpty?', allowEmpty':'')
		
		isValid = allowEmpty || TZ.login.require('#regEmail');	// sets validation text
		if (isValid && (!allowEmpty || emailStr.length > 0)) {
			if (!TZ.formValidation.checkEmail(emailStr)) {
				isValid = false;
				show('Please enter a valid email address');
				reportAnalyticsF && reportAnalyticsF('Email', 'IncorrectFormat');
			}
			else {
				TZ.rpc.invoke('FindMatchingAccount', { 'email': emailStr }, function (taazAccount) {
					var isValid = taazAccount.status === 0;
					if (!isValid) {
						show('in use by another account');
						$('#regResetPassword').switchClass('hidden', 'block');
						reportAnalyticsF && reportAnalyticsF('Email', 'Taken');
					}
					else {
						show('');
						
						// Show the e-mail field to allow it to be changed
						$('#regEmailBlock').show();
				
						$('#regResetPassword').switchClass('block', 'hidden');
					}
				});//rpc.invoke
			}
		}
		else
			show('');
		
		return isValid;
	},
	
	/**
	 * Validate given username field and show/hide validation message.
	 * 
	 * @return true if value of given jQuery selector is permissible
	 */
	validateUsername: function (sel, allowEmpty, reportAnalyticsF) {
		var isValid = true,
		    val = $(sel).val(),
		    errMsg = '',
		    show = function(msg) { return TZ.login.showInputValidation(sel, msg); };
		    
		// Disallow names containing "TAAZ"
		// Check the Validated Username cache (if already looked up this session -- since last page refresh, that is)
		if ( val.match(/(T|t)(A|a)(A|a)(Z|z)/) || TZ.login._takenUsernames[val] ) {
			show(TZ.login.ERROR_MSG_TAKEN);
			return false;
		}
		
		//Debug.log('Validating username: ' + val, 'brentf')
		
		isValid = allowEmpty
				|| TZ.login.require('#regUsername')
				|| (reportAnalyticsF && reportAnalyticsF('NameNotFilled'));		// handles display of validation msg
		if (isValid && (!allowEmpty || val.length > 0)) {
			if (!val.match(/^[a-zA-Z]/)) {
				isValid = false;
				errMsg = "must begin with a letter";
				reportAnalyticsF && reportAnalyticsF('Username', 'IncorrectFormat');
			}
			else if (val.length < 3) {
				isValid = false;
				errMsg = "must be at least 3 characters";
				reportAnalyticsF && reportAnalyticsF('Username', 'IncorrectFormat');
			}
			else if (val.length > 20) {
				isValid = false;
				errMsg = "must be less than 20 characters";
				reportAnalyticsF && reportAnalyticsF('Username', 'IncorrectFormat');
			}
			else if (!val.match(TZ.login.usernameAllowedMatch)) {
				isValid = false;
				errMsg = "contains invalid characters";
				reportAnalyticsF && reportAnalyticsF('Username', 'IncorrectFormat');
			}
				
			show(errMsg);
		}
		
		return isValid;
	},
	
	/**
	 * Validate Birth Year and show/hide validation message.
	 * 
	 * @return true if value of given jQuery selector is permissible
	 */
	validateBirthYear: function (sel, allowEmpty, reportAnalyticsF) {
		var isValid = true,
		    birthYear = $(sel).val(),
		    thisYear  = new Date().getFullYear(),
		    age       = thisYear - birthYear - 1,   // -1: some 13-yr-olds will not qualify, but prevents false-positives for 12-yr-olds
		    over130   = age > 130 && !(allowEmpty && birthYear.length === 0),
		    under13   = age < 13,
		    notGiven  = (birthYear.length === 0),
		    errMsg = '';
        
        if (isNaN(age) || under13 || over130) {
            isValid = false;
            if (under13 && age >= -1) {
                errMsg = 'must be 13 years of age or older to use this site - ';
                reportAnalyticsF && reportAnalyticsF('IncorrectAge', (birthYear.length === 0) ? 'NotGiven' : 'Not13');
            }
            else {
                errMsg = 'invalid birth year - ';
                reportAnalyticsF && reportAnalyticsF('IncorrectAge', notGiven ? 'NotGiven' : (over130 ? 'Over130' : 'Invalid'));
            }
            errMsg += 'please enter the 4-digit year you were born';
            
            Debug.log(' age = ' + age + ', errMsg = ' + errMsg, 'brentf');
        }
		TZ.login.displayInputValidation(sel, errMsg);
		
		return isValid;
	},
	
	/**
	 * Select the text in the first visible input field, or the given one.
	 * 
	 * Note: this is a utility function that should exist in BasePopup.js (which doesn't currently exist).
	 */
	selectFirstField: function (sel) {
		var domElement = (sel) ? $(sel)[0] : $('.basePopUp').find('input:visible')[0];
		domElement.focus();
		domElement.select();
	},
		
    /**
     * Call on document ready to initialize this component.
     */
	init: function (showRegistration, completeFBRegistration) {
		var self = TZ.login;
		self._signInTabActive = null;
		Debug.log('showRegistration = ' + showRegistration + ', completeFBReg = ' + completeFBRegistration, 'brentf');
		
		//
		// Set connected flag in the background (cannot be done after click or browser will block the Facebook popup)
		//
		self._connectedWithFacebook = null;
		self.fb2OAuthRec(function(oAuthRec) {
				TZ.login._connectedWithFacebook = oAuthRec;
		});
		
		$('#LoginPopuppopup_close_button').click(function(){
			TZ.login.sendCloseAnalytics();
		});
		
		$('#LoginFacebookButton').click(function(event) {
			var self  = TZ.login;
			
			Analytics.event('SigninPopup', 'LoginFacebook', TZ.request.section);
			Debug.info ('login with facebook button clicked');
			
			if (self._connectedWithFacebook) {
				Debug.info('oAuthRec.registered = ' + self._connectedWithFacebook.registered);
				if (self._connectedWithFacebook.registered)
					self.loginWithFacebook();
				else {
					self.registerLinkClicked(false);
					self.registerWithFacebook();
				}
			} else {
				Debug.info('Click calling registerWithFacebook');
				// Dislay Registration tab
				self.registerLinkClicked(false);
				
				// Automatically click 'Connect using Facebook' button
				self.registerWithFacebook();
			}
			
			event.preventDefault();
			return false;
		});
	
		$('#RegisterFacebookButton').click(function(event) {
			Analytics.page('RegistrationPopup-RegisterFacebook');
			Analytics.event('RegistrationPopup', 'RegisterFacebook', TZ.request.section);
			TZ.login.registerWithFacebook();  // pops up FB window which calls callback on success, which calls TZ.login.facebookRegistrationComplete
			event.preventDefault();
			return false;
		});
	
		/*Tab Handler*/
		$('#LoginPopup .tabBar a').click(function() {
			TZ.login.selectTab($(this), true, true);
		});
		
		$('#LoginPopup').keydown(function(event){
			if (event.keyCode == '27')
				TZ.login.closePopup(true);	
		});
		$('.register-form').keydown(function(event){
			if (event.keyCode == '13') {
				event.preventDefault();
				$('#RegisterButton').click();
			}
		});
		$('.login-form').keydown(function(event){
			if (event.keyCode == '13') {
				event.preventDefault();
				$('#LoginButton').click();
			}
		});
		
		/**
		 * Link to switch to Login tab
		 */
		$('#GoToSignInLink').click(function() {
			Analytics.event('RegisterPopup', 'SigninLink', TZ.request.section);
			TZ.login.loginLinkClicked(true);
		});	
			
		/**
		 * Link to switch to Registration tab
		 */
		$('#GoToRegistrationLink').click(function() {
			Analytics.event('SigninPopup', 'NotAMember');
			TZ.login.registerLinkClicked(true);		
		});
		
		/**
		 * Look up username to see if available as the user types (wait for a pause between keystrokes before lookup)
		 */
		tzLoginUsernameLookup = 0;
		$('#regUsername').keyup(function(event) {
			window.clearTimeout(tzLoginUsernameLookup);		// cancel any pending lookup
			tzLoginUsernameLookup = setTimeout(function() {
			    var userName           = $('#regUsername').val(),
			        displaySuggestions = function (isValid) {
						var msg = (isValid) ? 'available.' : TZ.login.ERROR_MSG_TAKEN;
						TZ.login.displayInputValidation('#regUsername', msg, isValid);
						TZ.login.displayUsernameSuggestions(isValid);
				    };
			    
			    if (userName.length < 1)
				  TZ.login.hideInputValidation('#regUsername');
			    else if (TZ.login._takenUsernames[userName])	// check the avail username cache
			    	displaySuggestions(!TZ.login._takenUsernames[userName]);
			    else if (TZ.login.validateUsername('#regUsername')) {
			    	TZ.rpc.invoke('FindMatchingAccount', { 'name': userName }, function (taazAccount) {
			    		var isValid = (taazAccount.status === 0);
						TZ.login._takenUsernames[userName] = !isValid;
				    	displaySuggestions(isValid);
					});//FindMatchingAccount
			    }
			}, 1500);//setTimeout (look up username at most every sec or so)
		});
		
		/**
		 * Validate registration fields when loses focus (for fields that are not updated as the user types)
		 */
		$('#regEmail').blur(function(event) { TZ.login.validateEmail('#regEmail', 'allowEmpty'); });
		$('#regUsername').blur(function(event) { TZ.login.validateUsername('#regUsername', 'allowEmpty'); });
		$('#regBirthYear').blur(function(event) { TZ.login.validateBirthYear('#regBirthYear', 'allowEmpty'); });

		/**
		 * Use clicked Username Suggestion
		 */
		$('#regUsernameSuggestions').delegate('.regUsernameSuggestion', 'click', function(event) {
			var username=$(event.target).text();
			//Debug.log('  clicked to set username to ' + username, 'brentf');
			$("#regUsername").val(username);
			Debug.log("Set username to " + username, 'brentf');
		    TZ.login.hideInputValidation('#regUsername');
			
			Analytics.event('RegistrationPopup', 'UsernameSuggestion', 'Selected');
		});
		
		/**
		 * Birth Year Numeric keypress filter
		 */
		$('#regBirthYear').keypress(TZ.login.keypressFilterNumeric(false));
		
		/**
		 * Update Display "In her 20's" example
		 */
		var _regBirthYearKeyupTimeout = null;
		$('#regBirthYear').keyup(function(event) {
			var birthYearStr = $('#regBirthYear').val();
			if (birthYearStr.length > 4)
				$('#regBirthYear').val(birthYearStr.substr(0, 4));
			else {
		/* DISABLED - static example only
				var roundedAge = 20;
				if (birthYearStr.length == 4) {
					var age = new Date().getFullYear() - birthYearStr;
					if (age > 100)
						age = 100;
					if (age >= 30)
						roundedAge = age - age % 10;
					else if (age < 20 && age >= 13)
						roundedAge = "teen";
				}
				$('#regAgeDecade').text(roundedAge);
		--- end disabled birth year update */
			}
			
			window.clearTimeout(_regBirthYearKeyupTimeout);		// cancel any pending lookup
			_regBirthYearKeyupTimeout = setTimeout(function() {	// global
				TZ.login.validateBirthYear('#regBirthYear', 'allowEmpty'); 
			}, 2500);
		});
		
		$('#LoginButton').click(function() {
			var username = $("#loginUsername").val(),
			    password = $("#loginPassword").val();
			
			Analytics.event('SigninPopup', 'SigninLink', TZ.request.section);
			
			TZ.login.login(username, password);
		});
		
		/**
		 * Note: all this mess, but the last line, is to support the convoluted Analytics events.
		 */
		$('#RegisterButton').click(function() {
			var pageName = '',
			category = 'RegistrationPopup',
			s1 = '';
			if (TZ.login._showingFacebookPage) {
				pageName = 'RegistrationPopup-RegistrationFacebookFinish';
				s1 = 'RegistrationFacebookFinish';
			}
			else {
				pageName = 'RegistrationPopup-RegistrationCreateTaazAccount';
				s1 = 'RegisterCreateTaazAccount';
			}
		
			Analytics.page(pageName);
			Analytics.event(category, s1, TZ.request.section);
		
			TZ.login.register();
		});
		
		$('#loginForgotPassword').click(function(event) {
			var email = $('#loginUsername').val(),
			    url   = $(this).attr('href');
				
			Analytics.event('SigninPopup', 'ForgotUsernamePassword');
			
			$(this).attr('href', url + "?userOrEmail=" + email);
		});
      
		$('#regResetPassword').click(function(event) {
			var url = $(this).attr('href');
			
			$(this).attr('href', url + "?userOrEmail=" + $('#regEmail').val());
		});
      
		//
		// Keyboard input Filters
		$('#regUsername').keypress(function(event) { if (!TZ.login.validUsernameChar(event.which)) event.preventDefault(); });
		$('#loginUsername').keypress(function(event) { var ch=event.which; if ((ch > 9 && ch <= 32) || ch > 126) event.preventDefault(); });
		
		// 
		// Init UI:
		// 	Go to proper Tab
		//
		if (showRegistration)	// go to Registration tab if 'showRegistration' parameter was passed
			self.registerLinkClicked(false);
		if (completeFBRegistration)
			self.completeFacebookRegistration();
		else if (!showRegistration)
			self.loginLinkClicked(false);
	} //init()
	
}; // TZ.login
 
TZ.tooltip = {

		outTooltip: function (e) {
			TZ.events.dispatchEvent("tooltip_off");
			$('#tooltip').fadeOut("fast");
			$("#tooltip").remove();
		},
		removeToolTip : function (element) {
			$(element).removeData("tz.tooltip");
		}	
};
(function( $ ){
	var defaults = {
		yOffset: 15,
		tooltipOffset: 15,
		arrowWidth: 25,
		data: ""
	};
	
	var options = undefined;
		
	var methods = {
		init: function() {
			return this.each (function() {
				var $this = $(this);
				if ($this.data('tz.tooltip')){
					return;
				}
				
				var tooltip = undefined;
				if (options.data) {
					tooltip = getTooltip(options.data);
				} else if ($this.attr('tooltip') != undefined){
					tooltip = getTooltip($this.attr('tooltip'));
				}
		
				if (tooltip != undefined) {
					$this.data('tz.tooltip',tooltip);
					$this.bind('mouseenter.tooltip',show).bind('mouseleave.tooltip',hide);
				}
			});
		},
			
		destroy: function() {
			return this.each (function() {
				var $this = $(this);
				hide();
				$this.unbind('mouseenter.tooltip').unbind('mouseleave.tooltip').data('tz.tooltip','');
			});
		}
	};
		
	function show (e) {
		try {
			$(this).data("hover",true);
			var timeOut = parseInt($(this).data('tz.tooltip').find('[data-applytimeout]').data('applytimeout'));
			if(timeOut <= 0 || timeOut == undefined)
				timeOut = 0;
			var obj = this;
			setTimeout(function (){
					showTooltip(obj);
				},timeOut);
		} catch (ex) {
		}
	};
		
	function showTooltip(obj){
		try {
			if($(obj).data("hover"))
			{	
				$('#tooltip').remove();
				var tooltip = $(obj).data('tz.tooltip');
				$("body").append(tooltip);
				var pos = $.extend({},$(obj).offset(),{width:$(obj).width(), height:$(obj).height()});
				var position = calculatePosition(tooltip,pos);
				$('#tooltip .tip').css("left", position.tipLeft + "px");
				tooltip.css("top", position.top + "px").css("left", position.left + "px").fadeIn(100);
				$('#tooltip').bind('mouseleave',hide);
				var isImageTooltip = tooltip.find('[data-isimagetooltip]').data('isimagetooltip');
				if (isImageTooltip==undefined) {
					isImageTooltip=false;
				}
				if (isImageTooltip) {
					TZ.AdSlot.hideAds();
					$('.tip').hide();
				}
			}
		} catch (ex) {
		}
	}
	
	function hide (e) {
		$(this).data("hover",false);
		//var ttoffset = $('#tooltip').offset();
		//if (ttoffset) {
		//	var ttRight = $('#tooltip').width() + ttoffset.left;
		//	var ttBottom = $('#tooltip').height() + ttoffset.top;
		//	if (e && e.pageX>=ttoffset.left && e.pageX<=ttRight && e.pageY>=ttoffset.top && e.pageY<=ttBottom)
		//		return;
		//}
		TZ.AdSlot.showAds();
		$('#tooltip').stop(false,true).fadeOut(100);
		$("#tooltip").remove();
	};

    function calculateProperPosition(scrollPosish, windowMeasurement, tooltipMeasurement) {
    	var windowMidpoint = Math.floor(windowMeasurement/2);
    	var tooltipMidpoint = Math.floor(tooltipMeasurement/2);
    	if (windowMidpoint - tooltipMidpoint > 0) {
    		return windowMidpoint - tooltipMidpoint + scrollPosish;
    	} else {
    		return scrollPosish;
    	}
    };
    		
	function calculatePosition (tooltip,offset) {
		var toolTipHeight = tooltip.outerHeight();
		var toolTipWidth = tooltip.outerWidth();
		var top = 0;
		var left = 0;
		var tipLeft = 0;
		
		var isImageTooltip = tooltip.find('[data-isimagetooltip]').data('isimagetooltip');
		if (isImageTooltip==undefined) {
			isImageTooltip=false;
		}
		
		//if the tooltip is an image it should display it taking care of the current scrollbars position
		if (isImageTooltip) {
 			top = calculateProperPosition($(window).scrollTop(), $(window).height(), toolTipHeight);
        	left = offset.left + offset.width;
        	if (left>($(window).width()-toolTipWidth)) {
        		left = 10;
        	}
			tipLeft = 0;		
		} 
		//Else the tooltip is displayed on the top position
		else {
			top = offset.top - options.yOffset - toolTipHeight;
			var leftFix = 0;
			if(offset.left + toolTipWidth > $(window).width())
				leftFix  =  (offset.left + toolTipWidth) - $(window).width();
			left = offset.left + offset.width/4 - options.arrowWidth/2 - options.tooltipOffset - leftFix; 
			tipLeft = options.tooltipOffset  + leftFix;
		}
			
		return {top: top, left: left, tipLeft: tipLeft};
	};
		
	function getTooltip (data) {
		return $("<div id='tooltip'><div class='tooltip_content'>" + data + "</div><b class='tip'></b></div>").hide();
	};
		
	$.fn.tooltip = function(action,config) {
		if (typeof action === 'object' || !action) {
			config = action;
			action = 'init';
		}
		
		options = $.extend({},defaults,config);
		
		if ( methods[action] ) {
			return methods[action].apply( this, arguments);
		} else {
			$.error( 'Method ' +  action + ' does not exist on jQuery.tooltip' );
		}
	};
})( jQuery );

/*starting the script on page load*/
$(function(){
	$('[tooltip]').tooltip();
});







	
		/* Copyright 2011, Ben Lin (http://dreamerslab.com/)
* Licensed under the MIT License (LICENSE.txt).
*
* Version: 1.0.3
*
* Requires: jQuery 1.2.3+
*/
;(function(a){a.fn.extend({actual:function(b,k){var c,d,h,g,f,j,e,i;if(!this[b]){throw'$.actual => The jQuery method "'+b+'" you called does not exist';}h=a.extend({absolute:false,clone:false},k);d=this;if(h.clone===true){e=function(){d=d.filter(":first").clone().css({position:"absolute",top:-1000}).appendTo("body");};i=function(){d.remove();};}else{e=function(){c=d.parents().andSelf().filter(":hidden");g=h.absolute===true?{position:"absolute",visibility:"hidden",display:"block"}:{visibility:"hidden",display:"block"};f=[];c.each(function(){var m={},l;for(l in g){m[l]=this.style[l];this.style[l]=g[l];}f.push(m);});};i=function(){c.each(function(m){var n=f[m],l;for(l in g){this.style[l]=n[l];}});};}e();j=d[b]();i();return j;}});})(jQuery);
	
		/*! http://mths.be/placeholder v1.8.5 by @mathias */
(function(g,a,$){var f='placeholder' in a.createElement('input'),b='placeholder' in a.createElement('textarea');if(f&&b){$.fn.placeholder=function(){return this};$.fn.placeholder.input=$.fn.placeholder.textarea=true}else{$.fn.placeholder=function(){return this.filter((f?'textarea':':input')+'[placeholder]').bind('focus.placeholder',c).bind('blur.placeholder',e).trigger('blur.placeholder').end()};$.fn.placeholder.input=f;$.fn.placeholder.textarea=b;$(function(){$('form').bind('submit.placeholder',function(){var h=$('.placeholder',this).each(c);setTimeout(function(){h.each(e)},10)})});$(g).bind('unload.placeholder',function(){$('.placeholder').val('')})}function d(i){var h={},j=/^jQuery\d+$/;$.each(i.attributes,function(l,k){if(k.specified&&!j.test(k.name)){h[k.name]=k.value}});return h}function c(){var h=$(this);if(h.val()===h.attr('placeholder')&&h.hasClass('placeholder')){if(h.data('placeholder-password')){h.hide().next().show().focus().attr('id',h.removeAttr('id').data('placeholder-id'))}else{h.val('').removeClass('placeholder')}}}function e(){var l,k=$(this),h=k,j=this.id;if(k.val()===''){if(k.is(':password')){if(!k.data('placeholder-textinput')){try{l=k.clone().attr({type:'text'})}catch(i){l=$('<input>').attr($.extend(d(this),{type:'text'}))}l.removeAttr('name').data('placeholder-password',true).data('placeholder-id',j).bind('focus.placeholder',c);k.data('placeholder-textinput',l).data('placeholder-id',j).before(l)}k=k.removeAttr('id').hide().prev().attr('id',j).show()}k.addClass('placeholder').val(k.attr('placeholder'))}else{k.removeClass('placeholder')}}}(this,document,jQuery));
	

	
		// Do not use this, this is for backwards compatibility.
// You can use $(el).attr('disabled',true) which is more clear.

$.fn.buttonEnable = function(value){
	if (arguments.length === 0) return !$(this).attr('disabled')

	if (!value) $(this).attr('disabled','disabled')
	else       $(this).removeAttr('disabled')
};
	
	

	
		/**
 * @author Rakesh Patel
 */

(function($){
	$.fn.simpleMenu = function(menuHandler) {
		var menuHandler = menuHandler;
		
		return this.each(function() {
			$(this).find('li').click( function() {
				$('.simpleMenu .selected').removeClass('selected');
				$(this).addClass('selected');
				menuHandler(this);
			} );
		});
	};
})(jQuery);

	
		(function( $ ) {
'use strict';
$.widget( "taaz.simpleTabs", {
	options: {
	},
	_create: function() {
		var self = this;
		
		self._findPanes();
		
		var onhashchange = function(e) {
			if ($.bbq.getState('t')) self.select($.bbq.getState('t'))
		}
		self._onhashchange = onhashchange;
		$(window).bind('hashchange',onhashchange);

		
		var cancelClick = function(e) { e.preventDefault() };
		var tabClick    = function(e) { self.select(this)  };
		self._cancelClick  = cancelClick;
		self._tabClick     = tabClick;
		self.element.children().children('a').bind('click',cancelClick);
		self.element.children().bind('click',tabClick);

		self.defaultSelected = self.element.children('.selected');
		
		self.select($.bbq.getState('t') || self.defaultSelected);
	},
	select:function(tab) {
		if (typeof tab === 'number') {
			tab = this.element.children()[el];
		} else if (typeof tab === 'string') {
			var hash = tab.indexOf('#') < 0 ? '#' + tab : tab;
			var anchor = $('a[href*='+hash+']');
			tab= anchor.parent();
		} else if (!(tab instanceof jQuery)) { 
			tab = $(tab);
		} else if (!tab || !tab.length) {
			return;
		}
		
		tab.addClass('selected').siblings().removeClass('selected');
		var target = this._getTabTarget(tab);
		for (var i in this._panes) {
			this._panes[i].hide();
		}
		this._panes[target].show();
		$.bbq.pushState({'t' : target});
	},
	tab:function(index) {
		return $(this.element.children()[index]);
	},
	_findPanes:function() {
		var self = this;
		self._panes = {};
		this.element.children().each(function(i,tab){
			var name = self._getTabTarget(tab);
			var targetAnchor = $('a[name='+name+']'); 
			var pane = targetAnchor.parent();
			targetAnchor.data('name',targetAnchor.attr('name'));
			targetAnchor.attr('name','REMOVED');
			self._panes[name] = pane;
		});
	},
	_getTabTarget:function(tab) {
		var href = $(tab).children('a').attr('href') || '';
		return href.substr(href.indexOf('#') + 1) || 'UNDEFINED'
	},
	selected:function() {
		return this.element.children('.selected');
	},
	widget: function() {
		return this.element;
	},
	destroy: function() {
		$('a[name=REMOVED]').each(function(i,el){
			$(el).attr('name',$(el).data('name'));
		})
		for (var i in this._panes) {
			this._panes[i].show();
		}
		this.element.children().removeClass('selected');
		this.defaultSelected.addClass('selected');
		this.element.children().children('a').unbind('click',this._cancelClick);
		this.element.children().unbind('click',this._tabClick);
		$(window).unbind('hashchange',this._onhashchange);
		$.Widget.prototype.destroy.call( this );
	},

	_setOption: function( key, value ) {
		$.Widget.prototype._setOption.apply( this, [key,value] );
	}
});

}( jQuery ) );

	

