/**
 *  The CustomEvent class provides a simple event framework.  Code can subscribe to
 *  an event instance, and when the event is fired, all subscriber functions are called
 */
var CustomEvent = function() {
	this._arrSubscribers = [];
}
CustomEvent.prototype = {

/**
 * Call all subscribers functions.
 * param obj:  an option argument to be passed to the subscriber functions
 */
	fire: function(obj) {
		for (var i = 0; i < this._arrSubscribers.length; i++) {
			var function_subscriber = this._arrSubscribers[i];

			function_subscriber(obj);
		}
	},

/**
 * Subscribe to this event.  eventHandler function will be called when the event is fired
 */
	subscribe: function(function_eventHandler) {
		this._arrSubscribers.push(function_eventHandler);
	}
}


/**
 * State is change when the user clicks the BACK or FORWARD browser button
 * subscribers to STATE_CHANGED_EVENT are responsible to inspecting the new state
 * and updating the UI, if necessary
 */
var STATE_CHANGED_EVENT = new CustomEvent("state changed event");
EXANIMO.managers.StateManager.stateChangeHandler = function(e)
{
	STATE_CHANGED_EVENT.fire(e);
}

/**
 * Disable the auto initialization of the state manager
 * we want to initialize ourselves after everything else has been initialized
 */
clearInterval(EXANIMO.managers.StateManager._autoInitInterval);

/**
 * The PAGE_INITIALIZED_EVENT is fired after everything on the page has been initialized.
 * subscribe to this event if you to initialize a service that requires other services to be initialized
 */
var PAGE_INITIALIZED_EVENT = new CustomEvent("page initialized event");

/**
 * initialize the state manager after everything else has been initialized.
 * we need to allow the pandora pagers to subscribe to the STATE_CHANGED_EVENT
 * prior to initializing the state manager
 */
PAGE_INITIALIZED_EVENT.subscribe(function(e)
{
	EXANIMO.managers.StateManager.initialize();
});


/**
 * The USER_INTERACTION_EVENT is fired whenever the user interacts with a backstage page:
 *	- when the page through a table
 *  - when they add a bookmark
 */
var USER_INTERACTION_EVENT = new CustomEvent("USER_INTERACTION_EVENT");

USER_INTERACTION_EVENT.subscribe(function(e)
{
	Patron.refreshAds();
});


function expandBio(expand) {
	if (expand) {
		$("#bio_end").show();
		$("#see_all_link").hide();
		$("#shorten_link").show();
	} else {
		$("#bio_end").hide();
		$("#see_all_link").show();
		$("#shorten_link").hide();
	}
}

function expandAlbumGrid(expand) {
	if (expand) {
		$("#album_grid_end").show();
		$("#album_grid_see_all_link").hide();
		$("#album_grid_shorten_link").show();
	} else {
		$("#album_grid_end").hide();
		$("#album_grid_see_all_link").show();
		$("#album_grid_shorten_link").hide();
	}
}

function showStationDescriptionForm(expand) {
	if (expand) {
		$("#station_description_form").show();
		$("#show_description_form").hide();
		$("#community_section_title_description").hide();
		$("#station_details").hide();
		$("#hide_description_form").show();
		$("#hide_station_name_form").show();
		$("#station_description_text").hide();
	} else {
		$("#station_description_form").hide();
		$("#show_description_form").show();
		$("#community_section_title_description").show();
		$("#station_details").show();
		$("#hide_description_form").hide();
		$("#hide_station_name_form").hide();
		$("#station_description_text").show();
	}
}

function showCheckboxesFriend() {
	$('.profile_friend_checkbox').show();
	$('#remove_checked_friends').show();
	return false;
}

function hideCheckboxesFriend() {
	$('.profile_friend_checkbox').hide();
	$('#remove_checked_friends').hide();
	return false;
}

function showCheckboxesTrack() {
	$('.profile_track_buy').hide();
	$('.profile_track_checkbox').show();
	$('#remove_checked_tracks').show();
	return false;
}

function hideCheckboxesTrack() {
	$('.profile_track_buy').show();
	$('.profile_track_checkbox').hide();
	$('#remove_checked_tracks').hide();
	return false;
}

function showCheckboxesArtist() {
	$('.profile_artist_checkbox').show();
	$('#remove_checked_artists').show();
	return false;
}

function hideCheckboxesArtist() {
	$('.profile_artist_checkbox').hide();
	$('#remove_checked_artists').hide();
	return false;
}

function showQuickAddForm(showing, hiding) {
	$(showing).show();
	$(hiding).hide();
}

function quickAdd_onclick(htmlAnchor, function_callback)
{
	var imgQuickAdd_array = $("img", htmlAnchor);
	if (imgQuickAdd_array && imgQuickAdd_array.length == 1)
	{
		imgQuickAdd_array[0].src = "/images/searching_anim_70.gif";
		imgQuickAdd_array[0].onmouseout = null;
		imgQuickAdd_array[0].onmouseover = null;


		if (window.setTimeout)
		{
			window.setTimeout(function() {
				function_callback();
			}, 100);
		}
		else
		{
			function_callback();
		}
	}
	else
	{
		function_callback();
	}

}

function expandHiddenRows(expand, visibleRows, tableSelector, seeAllSelector, shortenSelector) {
	var rows = $(tableSelector + " tr");
	for (var i = visibleRows + 1; i < rows.size(); i++) {
		if (expand) {
			$(rows.get(i)).show();
		} else {
			$(rows.get(i)).hide();
		}
	}
	if (expand) {
		$(seeAllSelector).hide();
		$(shortenSelector).show();
	} else {
		$(seeAllSelector).show();
		$(shortenSelector).hide();
	}
	return false;
}

function prefGlobalEmailChange(checkbox) {
	if (checkbox.checked) {
		getDiv("emailCheckBoxes").className = "";
		getDiv("emailOptIn").checked = true;
		getDiv("notifyOnNote").checked = true;
		getDiv("emailOptIn").disabled = false;
		getDiv("notifyOnNote").disabled = false;
	} else {
		getDiv("emailCheckBoxes").className = "emailDisabled";
		getDiv("emailOptIn").checked = false;
		getDiv("notifyOnNote").checked = false;
		getDiv("emailOptIn").disabled = true;
		getDiv("notifyOnNote").disabled = true;
	}
}

function noteTextChanged(textarea, max, noteCharCount, noteCharCountDiv, classTooLong) {
	var count = textarea.value.length;
	getDiv(noteCharCount).value = count;
	if (count > max) {
		getDiv(noteCharCountDiv).className = classTooLong;
	} else {
		getDiv(noteCharCountDiv).className = "";
	}
}


// code to watch notes for character count changes
var listOfNoteChangeCheckers = {}

var NoteTextChangeChecker = function(objToWatch, maxNoteSize) {
    this.objToWatch = objToWatch;
	this.maxNoteSize = maxNoteSize;
	listOfNoteChangeCheckers[objToWatch.id] = this;
}

NoteTextChangeChecker.prototype = {
	isReady : false,
	check : function() {
		if (this.isReady) {
		   noteTextChanged(this.objToWatch, this.maxNoteSize, 'noteCharCount', 'noteCharCountDiv', 'noteTooLong');
		   var thisObj = this;
		   setTimeout(function() {thisObj.check()},300);
		}
	}
}

function startCheckNoteTextChanged(obj){
	var thisChecker = listOfNoteChangeCheckers[obj.id];
    if (typeof (thisChecker) == "undefined") {
		thisChecker = new NoteTextChangeChecker(obj);
    }
	thisChecker.isReady = true;
	thisChecker.check();
}

function stopCheckNoteTextChanged(obj){
	var thisChecker = listOfNoteChangeCheckers[obj.id];
	thisChecker.isReady = false;
}
// -- END / code to watch notes for character count changes


// Render some HTML rows in a "fans of" table, taking data from a JS object.
// The object is created by the #fansOf macro and has the format:
// ( (isOnline, "webname", "fullname", ("artist1", "artist2", ...)), ... )
function renderFansOf(tableSelector, fans, numRows) {
	var count = 0;
	var picked = [];
	while (count < Math.min(fans.length, numRows)) {
		var r = Math.floor(Math.random() * fans.length);
		if (picked[r]) continue;
		count++;
		picked[r] = true;
		var fan = fans[r];
		var row = '<tr>';
		row += '<td align="left" valign="top">';
		if (fan[0]) {
			row += '<img src="/images/online_status_on.gif" width="24" height="18" alt="currently online" title="currently online">';
		} else {
			row += '<img src="/images/online_status_off.gif" width="24" height="18" alt="currently offline" title="currently offline">';
		}
		row += '</td>'
		row += '<td align="left" valign="top">';
		row += '<div style="width:144px; word-wrap:break-word">';
		// Without this DIV, word-wrapping does not work in Safari
		row += '<b><a href="/people/' + fan[1] + '">' + fan[2] + '</a></b><br />';
		if (fan[3].length > 0) {
			row += '(also listening to:	';
			for (var i = 0; i < fan[3].length; i++) {
				row += fan[3][i];
				if (i < fan[3].length - 1) {
					row += ", ";
				}
			}
			row += ')';
		}
		row += '</div>';
		row += '</td>';
		$(tableSelector).append(row);
	}
}

// Create an artist link using special Pandora tag syntax, using the currently
// selected text as an artist name (if any) and prompting for any other information.
function createArtistLink(textAreaSelector) {
	var element = $(textAreaSelector).get(0);
	var artist = getSelectionValue(element);
	var context = createModalContext(element);
	if (artist == "") {
		showModalDialog("Please enter the name of the artist:", createArtistLinkFromText, restoreSelection, context);
	} else {
		createArtistLinkFromText(artist, context);
	}
}

// Create an artist link in the text selection for the specified artist
function createArtistLinkFromText(artist, context) {
	if (artist && artist.length > 0) {
		replaceSelectionWith(context.element, "[artist]" + artist + "[/artist]", context.range);
	} else {
		restoreSelection();
	}
}

// Create a profile link using special Pandora tag syntax, using the currently
// selected text as a profile name (if any) and prompting for any other information.
function createProfileLink(textAreaSelector) {
	var element = $(textAreaSelector).get(0);
	var user = getSelectionValue(element);
	var context = createModalContext(element);
	if (user == "") {
		showModalDialog("Please enter the email address or profile URL of the Pandora user:", createProfileLinkFromText, restoreSelection, context);
		//showModalDialog("Please enter the email address or profile URL of the Pandora user.", createProfileLinkFromText, restoreSelection, context);
	} else {
		createProfileLinkFromText(user, context);
	}
}

// Create a profile link in the text selection for the specified user
function createProfileLinkFromText(user, context) {
	if (user && user.length > 0) {
		var re = new RegExp("/people/(.*)");
		var match = re.exec(user);
		if (match && match.length > 1) {
			user = match[1];
		}
		replaceSelectionWith(context.element, "[profile]" + user + "[/profile]", context.range);
	} else {
		restoreSelection();
	}
}

// Create an album link using special Pandora tag syntax, using the currently
// selected text as an album name (if any) and prompting for any other information.
function createAlbumLink(textAreaSelector) {
	var element = $(textAreaSelector).get(0);
	var album = getSelectionValue(element);
	var context = createModalContext(element);
	if (album == "") {
		showModalDialog("Please enter the name of the album:", createAlbumLinkFromText, restoreSelection, context);
	} else {
		context.album = album;
		showModalDialog("To make sure we have the correct '" + album + "', please tell us the artist:", createAlbumLinkFromText, restoreSelection, context);
	}
}

// Create an album link in the text selection with the specified text
function createAlbumLinkFromText(text, context) {
	if (text && text.length > 0) {
		if (context.album == null) {
			// We only have album, get artist with another dialog
			context.album = text;
			showModalDialog("To make sure we have the correct '" + text + "', please tell us the artist:", createAlbumLinkFromText, restoreSelection, context);
		} else {
			// We have album and artist
			replaceSelectionWith(context.element, '[album artist="' + text + '"]' + context.album + "[/album]", context.range);
		}

	} else {
		restoreSelection();
	}
}

// Create a song link using special Pandora tag syntax, using the currently
// selected text as a song name (if any) and prompting for any other information.
function createSongLink(textAreaSelector) {
	var element = $(textAreaSelector).get(0);
	var song = getSelectionValue(element);
	var context = createModalContext(element);
	if (song == "") {
		showModalDialog("Please enter the name of the song:", createSongLinkFromText, restoreSelection, context);
	} else {
		context.song = song;
		showModalDialog("To make sure we have the correct '" + song + "', please tell us the artist:", createSongLinkFromText, restoreSelection, context);
	}
}

// Create a song link in the text selection with the specified text
function createSongLinkFromText(text, context) {
	if (text && text.length > 0) {
		if (context.song == null) {
			// We only have song, get artist with another dialog
			context.song = text;
			showModalDialog("To make sure we have the correct '" + text + "', please tell us the artist:", createSongLinkFromText, restoreSelection, context);
		} else {
			// We have song and artist
			replaceSelectionWith(context.element, '[song artist="' + text + '"]' + context.song + "[/song]", context.range);
		}
	} else {
		restoreSelection();
	}
}

// Create a context object to use with the modal dialog callbacks
function createModalContext(element) {
	var context = { element : element };
	if (document.selection) {
		// Save range in context because modal dialog wipes it out somehow (IE only)
		element.focus();
		range = document.selection.createRange();
		context.range = range;
	}
	return context;
}

// Restore the text selection's cursor position for the given modal context
function restoreSelection(context) {
	if (context.range != null) {	// IE only
		var dummyRange = context.range.duplicate();
		dummyRange.moveToElementText(context.element);
		dummyRange.setEndPoint("EndToEnd", context.range);
		var start = dummyRange.text.length - context.range.text.length;
		if (context.range.parentElement() == context.element) {
			var finalRange = context.element.createTextRange();
			finalRange.move("character", start);
			finalRange.select();
		}
	}
}

// Get the value of the currently-selected text for the element (i.e. textarea)
// Returns the text as a string, or empty string if nothing is selected.
function getSelectionValue(element) {
	element.focus();
	if (element.selectionStart >= 0) {
		var start = element.selectionStart;
		var end = element.selectionEnd;
		return element.value.substring(start, end);
	} else if (document.selection) {
		element.focus();
		var range = document.selection.createRange();
		if (range.parentElement() == element) {
			return range.text;
		}
	}
	return "";
}

// Replace the current text selection with the supplied text.  If nothing is selected,
// the new text will be inserted at the current cursor position.
function replaceSelectionWith(element, text, range) {
	element.focus();
	if (element.selectionStart >= 0) {			// Mozilla, Firefox, Safari and Opera
		var start = element.selectionStart;
		var end = element.selectionEnd;
		var value = element.value;
		element.value = value.substr(0, start) + text + value.substr(end, value.length);
		element.focus();
		element.setSelectionRange(start + text.length, start + text.length);

	} else if (document.selection) {			// Internet Explorer
		if (range == null) {
			range = document.selection.createRange();
		}
		var dummyRange = range.duplicate();
		dummyRange.moveToElementText(element);
		dummyRange.setEndPoint("EndToEnd", range);
		var start = dummyRange.text.length - range.text.length;

		// If selection range is not in the expected element, abort
		if (range.parentElement() != element) {
			return false;
		}

		// Set the range with the new text
		range.text = text;

		// Set carat to end of selection
		var finalRange = element.createTextRange();
		finalRange.move("character", start + text.length);
		finalRange.select();

	} else {
		// Fallback - just append it to the end
		element.value += text;
	}

	// Restore focus to element (only works reliably using setTimeout)
	setTimeout(function() {
		element.focus();
	}, 1);

}

// Escape HTML entities in a string
function escapeHtml(html) {
	html = html.replace(/&/g, '&amp;');
	html = html.replace(/</g, '&lt;');
	html = html.replace(/>/g, '&gt;');
	html = html.replace(/"/g, '&quot;');
	return html;
}

// Decode a URL-encoded string
function urlDecode(str) {
	return unescape(str.replace(/\+/g, " "));
}

// Prevent dragging of links into textarea (see RADIO-4066)
function preventDragDrop(selector) {
	$(selector).each(function() {
		if (this.addEventListener) {
			this.addEventListener("dragdrop", function(e) {
				e.stopPropagation();
				e.preventDefault();
			}, true);
		}
	});
}

// Display an error message in a floating div, with a dismiss button
function showErrorMessage(msg) {
	if (document.createElement) {
		msg = msg.replace(/\n/g, "<br>");
		var div = document.createElement("div");
		$(div).addClass("error").html(msg + "<br><br>");
		var img = document.createElement("img");
		img.src = "/images/continue_button.gif";
		img.width = 100;
		img.height = 22;
		img.style.cursor = "pointer";
		$(div).append(img);
		$(img).mouseover(function() {
			this.src = "/images/continue_button_hover.gif";
		});
		$(img).mouseout(function() {
			this.src = "/images/continue_button.gif";
		});
		$(img).click(function() {
			$(div).hide();
		})
		$(document.body).append(div);
	} else {
		alert(msg);
	}
}

var gModalDialogSubmitCallback = null;
var gModalDialogCancelCallback = null;
var gModalDialogContext = null;

// Show a modal dialog that simulates a javascript prompt() call.  If the user hits
// ENTER or submits the dialog, the submitCallback is called with the user's text and
// context as arguments.  Otherwise the cancelCallback (if any) is called with the context.
function showModalDialog(text, submitCallback, cancelCallback, context) {
	gModalDialogSubmitCallback = submitCallback;
	gModalDialogCancelCallback = cancelCallback;
	gModalDialogContext = context;
	$('#modalDialog').modalContent({opacity: '.40', background: '#fff'}, "fadeIn", "slow", function() {
		$('#modalContent input').get(0).focus();
	});
	$('#modalContent span').html(text);
}

// Internal function used by the modal dialog box
function modalDialogSubmit() {
	var text = $('#modalContent input').val();
	$('#modalDialog').unmodalContent();
	if (gModalDialogSubmitCallback) {
		var tmpCallback = gModalDialogSubmitCallback;
		var tmpContext = gModalDialogContext;
		setTimeout(function() {
			tmpCallback(text, tmpContext);
		}, 1);
	}
	gModalDialogSubmitCallback = null;
	gModalDialogCancelCallback = null;
	gModalDialogContext = null;
}

// Internal function used by the modal dialog box
function modalDialogCancel() {
	$('#modalDialog').unmodalContent();
	if (gModalDialogCancelCallback) {
		var tmpCallback = gModalDialogCancelCallback;
		var tmpContext = gModalDialogContext;
		setTimeout(function() {
			tmpCallback(tmpContext);
		}, 1);
	}
	gModalDialogSubmitCallback = null;
	gModalDialogCancelCallback = null;
	gModalDialogContext = null;
}

function reloadWindow() {
	if ($.browser.safari
			&& window.location.hash != null
			&& window.location.hash.length > 0) {
		// Safari will not reload the window if there is a hash value in the url
		// if the url has a hash value and we are in safari, reconstruct the url
		// without the hash.  Also, safari requires that we tack a unique query string onto the url
		var url = window.location.protocol + "//" + window.location.host + window.location.pathname + "?deleteHash=" + new Date().getTime();
		window.location.href = url;
	} else {
		window.location.reload();
	}
}

function getKeyCode(event) {
	if (event.which) {
		return event.which;
	} else {
		return event.keyCode;
	}
}

function executeClickEvent(jqueryAnchor) {
	var onClick = jqueryAnchor.attr("onclick");
	var strHref = jqueryAnchor.attr("href");

	if ($.browser.msie) {
		//TODO:  get rid of these IE hacks when we upgrade jquery (RADIO-4172)
		if (typeof(onClick) == "function") {
			//the anchor click event fails on ie6, so call the function a bit more manually
			onClick.call();
		} else if (strHref != null && strHref.length > 0) {
			//Marc is really going to like this one...
			if (strHref.indexOf("javascript:") == 0) {
				eval(strHref.substring("javascript:".length));
			} else {
				window.location.href = strHref;
			}
		}
	} else {
		if (onClick != null && onClick.length > 0) {
			jqueryAnchor.click();
		} else if (strHref != null && strHref.length > 0) {
			window.location.href = strHref;
		}
	}
}

