Timer = ( function ( ) {
	$( function ( ) {
		$("#timer").css ( {
			top : -$("#timer").outerHeight()
		} );
	} );
	var stack = []; // used backwards, for ease of peeking (ie. we're unshifting, not pushing)
	var ticker = null;
	var tick = function ( ) {
		if ( ticker || !stack.length ) return;
		var update = function ( ) {
			if ( !stack.length ) return;
			var current = stack[0].remaining;
			var minutes = parseInt ( current / 60000 );
			if ( minutes < 10 ) minutes = "0" + minutes;
			var seconds = parseInt ( current / 1000 ) - ( minutes * 60 );
			if ( seconds < 10 ) seconds = "0" + seconds;
			$("#timer span").text ( minutes + ":" + seconds );
			if ( current < 21000 && Math.floor( current / 1000 ) % 2 ) {
				$("#timer span").addClass("flash")
			} else {
				$("#timer span").removeClass("flash");
			}
		}
		update();
		ticker = true;
		var last_tick = new Date();
		var fn = function ( ) {
			if ( !stack.length ) {
				clearInterval ( ticker );
				ticker = null;
				Timer.hide();
			} else {
				var now = new Date();
				var diff = now - last_tick;
				last_tick = now;
				for ( var i = 0; i < stack.length; i ++ ) {
					stack[i].remaining -= diff;
					if ( stack[i].remaining <= 0 ) {
						stack[i].remaining = 0;
						Timer.end ( stack[i].id );
					}
					if ( i == 0 ) update();
				}
			}
		}
		Timer.show ( function ( ) { ticker = setInterval ( fn, 50 ); } );
	}
	return {
		// time - in seconds
		// cb - callback on end
		start : function ( time, cb ) {
			if ( isNaN ( time ) ) throw "Must be a valid time";
			var id = (""+Math.random()).substring(2);
			var timer = {
				id : id,
				time : time * 1000,
				remaining : ( time + 0.75 ) * 1000,
				cb : cb,
				stop : function ( ) {
					return Timer.end ( this.id );
				}
			};
			stack.unshift ( timer );
			tick();
			return timer;
		},
		end : function ( id ) {
			if ( !stack.length ) return null;
			if ( id && id.id ) id = id.id;
			if ( !id ) id = stack[0].id;
			for ( var i = stack.length; i --; ) {
				if ( !stack[i] || !stack[i].id )
					stack.splice(i,1);
				if ( stack[i].id == id ) {
					var timer = stack.splice(i,1)[0];
					if ( typeof timer.cb == "function" )
						timer.cb ( timer );
					return timer.remaining;
				}
			}
			return null;
		},
		pause : function ( ) {
			clearInterval ( ticker );
			ticker = null;
		},
		resume : function ( ) {
			tick();
		},
		hide : function ( cb ) {
			$("#timer").animate ( { top : -$("#timer").outerHeight() }, {
				duration : 500,
				complete : cb
			} );
		},
		show : function ( cb ) {
			$("#timer").animate ( { top : 0 }, {
				duration : 500,
				complete : cb
			} );
		}
	}
} )();

var Score = ( function ( ) {
	$( function ( ) {
		$("#score").css ( {
			top : -$("#score").outerHeight()
		} );
	} );
	return {
		set : function ( percent ) {
			$("#score span span span").animate ( { left : percent + '%' }, { duration : 500 } );
		},
		add : function ( change ) {
			percent = parseInt( $("#score span span span").css( 'left' ) ) + change;
			$("#score span span span").animate ( { left : percent + '%' }, {
				duration : 500,
				complete : function () {
					if ( percent < 0 ) {
						$("#score span span span").animate ( { left : '0%' }, { duration : 200 } );
					} else if ( percent > 100 ) {
						$("#score span span span").animate ( { left : '100%' }, { duration : 200 } );
					}
				}
			} );
		},
		hide : function () {
			$("#score").animate ( { top : -$("#score").outerHeight() }, {
				duration : 500,
				complete : function () {
					$("#score span span span").css( 'left', '50%' );
				}
			} );
		},
		show : function () {
			$("#score").animate ( { top : 0 }, { duration : 500 } );
		}
	}
} )();

var Portal = ( function ( $ ) {

	$("a[rel=internal]").live ( "click", function ( ) {
		if ( this.getAttribute("href").charAt(0) == "#" ) {
			var fn;
			if ( fn = (this.getAttribute("href").match ( /#fn:(\w+)/ )||[]).pop().split(".") ) {
				var context = window;
				while ( context && ( fn.length > 1 ) )
					context = context[fn.shift()];
				if ( context ) {
					fn = fn.pop();
					if ( typeof context[fn] == "function" )
						context[fn]();
				}
			}
		} else {
			var location = this.href;
			var load = this.getAttribute("onLoad");
			this.clicking = setTimeout ( function ( ) {
				$('body').addClass('loading');
				Portal.loadPage ( null, location, null, function ( ) {
					$('body').removeClass('loading');
					if ( load )
						setTimeout ( function ( ) {
							eval ( load );
						}, 2500 );
					//console.log ( load );
				} );
				//this.blur();
			}, 10 );
		}
		return false;
	} );

	$("a").live ( "click", function ( ) {
		if ( this.getAttribute("href") == "#" )
			return false;
	} );

	$( function ( ) {
		$(window).resize ( ( function ( ) {
			var reposition = function ( ) {
				var body = document.getElementById("portal_body");
				var gutter = Math.max ( 0, body.parentNode.offsetWidth - body.offsetWidth );
				$("#portal_overlay table.message td.messageContainer").css ( "paddingRight", gutter + "px" );
			}
			reposition();
			return reposition;
		} )() );
	} );

	var metadata = {};
	var hintsUsed = false;

	( function ( ) {
		var popupStack = [];
		var showing = false;
		var showPopup = function ( ) {
			if ( showing ) return;
			var next = popupStack.shift();
			if ( !next ) return;
			showing = true;
			var name = metadata.achievements[next].name.replace(/\s*\(.*\)/,'');
			var icon = metadata.achievements[next].icons[1];
			var popup = $('<div/>')
				.attr ( "class", "popup" )
				.appendTo ( "#tb_achievement_anchor" )
				.append ( $('<div/>').html ( '<span>' + name + '</span>' ) )
				.append ( $('<img/>').attr ( "src", icon ).css ( { width : 48, height : 48 } ) )
				.css ( "opacity", 0 )
				.click ( function ( ) { $("#tb_achievements").click() } )
				.animate ( { bottom : 10, opacity : 1 }, {
					duration : 1000,
					complete : function ( ) {
						setTimeout ( function ( ) {
							showing = false;
							showPopup();
							popup.animate ( { bottom : 30, opacity : 0 }, {
								duration : 500,
								complete : function ( ) {
									popup.remove();
								}
							} );
						}, 2500 );
					}
				} );
		}
		API.smokescreen.action['do'].listen ( function ( rsp, data ) {
			if ( !rsp.error && rsp.action.achievement && rsp.result == 'new' ) {
				metadata.achievements[data.action_hash].achieved = true;
				var finished = 0;
				for ( var hash in metadata.achievements )
					if ( metadata.achievements[hash].achieved )
						finished += 1;
				$("#tb_achievements").find("b").text(finished).end().effect('pulsate',{times:3},500);
				popupStack.push ( data.action_hash );
				showPopup();
			}
		} );
	} )();

	API.smokescreen.mission.start.listen ( function ( ) {
		$('body').addClass('taskbar');
		if ( !isNaN ( parseInt ( Mission.get('time') ) ) ) {
			// TO DO: take into consideration starting point, and any elapsed
			// time they've taken so far
			var time = parseInt ( Mission.get('time') );
			Timer.start ( time, function ( timer ) {
				if ( !timer.remaining ) {
					// things to do...
					// - pause remaining timers
					Timer.pause();
					Timer.hide();
					// - fail mission
					Mission.fail();
					// - kill audio (voices)
					// - hide subtitles
				}
			} );
		}
	} );

	API.smokescreen.mission.end.listen ( function ( rsp ) {
		$('body').removeClass('taskbar');
	} );

	API.smokescreen.task.start.listen ( function ( rsp, data ) {
		Timer.resume();
		//hintsUsed = false;

		var index = data.task_index;
		var task = Mission.tasks(index);
		var w = 0;
		for ( var i = 0; i < index; i ++ ) {
			w += Mission.tasks(i).get('weight');
		}

		$("#currentTaskDisplay").html ( '<span id="currentTaskNumber">' + (index+1) + '</span>/<span id="totalTaskCount">' + Mission.get('tasks') + '</span>');

		var progress = 100 * w;
		if ( progress != 100 )
			$("#tb_progress").removeClass ( "finished" );
		$("#tb_progress span span").animate ( { width : progress + "%" }, {
			duraction : 500,
			complete : function ( ) {
				if ( progress == 100 )
					$("#tb_progress").addClass ( "finished" )
			}
		});

		if ( !isNaN ( parseInt ( task.get('time') ) ) ) {
			Timer.start ( task.get('time'), function ( timer ) {
				if ( !timer.remaining ) {
					Timer.pause();
					Timer.hide();
					task.fail();
				}
			} );
		}

		Portal.setObjective ( rsp.task.description );
		Portal.setHint ( rsp.task.hint );
	} );

	API.smokescreen.task.end.listen ( function ( rsp, data ) {
		Timer.pause();
	} );

	var fixPage = function ( ) {
		$("#portal_body form[rel=internal]").submit ( function ( ) {
			var location = this.action;
			var data = $(this).seralize();
			var method = this.method;
			this.submitting = setTimeout ( function ( ) {
				goToPage ( location, data, method, true );
			}, 10 );
			return false;
		} );
	}

	return {
		init : function ( md ) {
			delete Portal.init;
			for ( var item in md ) {
				switch ( item ) {
					case "mission":
						Mission.init ( md[item] );
						break;
					case "tasks":
						Mission.setTasks ( md[item] );
						break;
					default:
						metadata[item] = md[item];
				}
			}
		},

		getUserName : function ( def ) {
			return metadata.user.name || def;
		},
		setUserName : function ( un ) {
			metadata.user.name = un;
		},

		getMetaData : function ( key ) {
			return metadata[key]
		},

		loadPage : ( function ( ) {
			var currentPage = null;
			var cleanStyles = function ( ) {
				$('link[temporary], style[temporary]').each ( function ( ) {
					if ( this.parentNode )
						this.parentNode.removeChild ( this );
				} )
			}
			var addStyle = function ( href, temporary ) {
				if ( $('link[href='+href+']').length ) return false;
				var link = document.createElement("link");
				link.setAttribute ( "rel", "stylesheet" );
				link.setAttribute ( "type", "text/css" );
				link.setAttribute ( "href", href );
				if ( temporary ) link.setAttribute ( "temporary", true );
				document.getElementsByTagName("head")[0].appendChild ( link );
				return true;
			}
			var scriptExists = function ( src ) {
				var old_src = src;
				src = resolveURI ( src );
				//if ( src.charAt(0) == "/" )
				//	src = window.location.protocol + "//" + window.location.host + src;
				//console.log ( old_src + " - " + src );
				var exists = !!$('script').filter( function ( ) {
					/*
					var s = this._src_ || this.src;
					if ( s.charAt(0) == "/" )
						s = window.location.protocol + "//" + window.location.host + s;
					this._src_ = s;
					//console.log ( src, ' => ', s, ' (', !!(s==src), ')' );
					*/
					return ( this.src ? resolveURI ( this.src ) : "" )  == src;
				} ).length;
				//console.log ( src, "exits?", exists );
				return exists;
			}
			var script_regex = /(<script[^>]*>[\s\S]*?<\/script>)/g;
			var stylesheet_regex = /(<link[^>]+?rel="stylesheet"[^>]*>)/g;
			var inline_styles_regex = /(<style[^>]*>[\s\S]*?<\/style>)/g;
			var last_context = null;
			return function ( context, src, data, callback, container, permanent, hard ) {
				if ( ( typeof data == "function" ) && !callback ) {
					callback = data;
					data = null;
				}

				// guessing that if there's no leading slash (or a protocol),
				// you want it relative to the current url
				if ( !src.match ( /^(\w+\:\/\/|\/)/ ) )
					src = "./" + src;
				src = resolveURI ( src );
				/*
				// normalize src
				var link = document.createElement("a");
				link.setAttribute ( "href", src );
				src = link.href;
				*/

				if ( context )
					last_context = context;
				else
					context = last_context;

				if ( typeof container == "function" )
					container = container();
				if ( typeof container == "string" )
					container = document.getElementById(container);
				if ( !container || !container.nodeType ) {
					container = document.getElementById('portal_body');
				}

				if ( container.id == "portal_body" ) {
					if ( ( src == currentPage ) && !hard ) {
						if ( typeof callback == "function" ) {
							setTimeout ( function ( ) { callback ( src, data ); }, 100 );
						}
						return;
					}
					currentPage = src;
				}

				var connection = new Request(src);
				connection.send ( data, function ( response, success, headers ) {
					if ( success ) {
						// dealing with conditional comments
						response = response.replace(/<!--\[([^\]]*?)\]>([\s\S]*?)<!\[endif\]-->/, function ( comment, query, content ) {
							var query = query.toLowerCase().match ( /if(?: +(gt|gte|lte|lt))? +(ie|moz|ff|wk|sf|gc|op)(?: (\d+(?:\.\d+)?))?/i );

							var browser = $.browser[{ie:"msie",moz:"mozilla",ff:"mozilla",wk:"safari",sf:"safari",op:"opera",gc:"chrome"}[query[2]]];
							if ( !browser ) return "";

							var version = parseFloat ( query[3] );
							if ( !version ) return content;

							var currentVersion = parseFloat ( $.browser.version.substr(0.3) );
							switch ( query[1] ) {
								case "gt": // greater than
									if ( currentVersion > version ) return content;
									break;
								case "gte": // greater than or equal to
									if ( currentVersion >= version ) return content;
									break;
								case "lte": // less than or equal to
									if ( currentVersion <= version ) return content;
									break;
								case "lt": // less than
									if ( currentVersion < version ) return content;
									break;
								default: // equal to
									if ( currentVersion == version ) return content;
									break;
							}

							return '';
						} );
						response = response.replace(/<!--[\s\S]*?-->/,'');
						var scripts = response.match(script_regex) || [];
						var stylesheets = response.match(stylesheet_regex) || [];
						var inline_styles = response.match(inline_styles_regex) || [];
						response = response.replace(script_regex,'').replace(stylesheet_regex,'').replace(inline_styles_regex,'');
						$(container).hide();
						if ( container.id == "portal_body" ) {
							cleanStyles();
						}
						//container.style.display = "none";

						$.each ( stylesheets, function ( i, stylesheet ) {
							addStyle ( (stylesheet.match(/href="([^"]+)"/)||[]).pop(), !permanent );
						} );
						$.each ( inline_styles, function ( i, style ) {
							var node = $(style);
							if ( !permanent )
								node.attr("temporary",true);
							document.getElementsByTagName("head")[0].appendChild ( node[0] );
						} );

						setStoppableTimeout ( context, function ( ) {
							var temp_container = document.createElement("div");
							temp_container.style.display = "none";
							var content = response.match(/<body([^>]*)>([\s\S]*?)<\/body>/) || [];
							temp_container.innerHTML = content.pop();
							var body_meta = content.pop().match(/(\w+="[^"]*")/g) || [];
							var meta = {};
							for ( var i = body_meta.length; i --; ) {
								var m = body_meta.pop().match(/^(\w+)="\s*([^"]*)\s*"/);
								meta[m[1]] = m[2];
							}
							meta.title = (response.match(/<title>(.*)<\/title>/)||[]).pop();
							document.getElementById("portal_body").appendChild ( temp_container );
							
							var finishPage = function ( ) {
								if ( temp_container.parentNode )
									temp_container.parentNode.removeChild ( temp_container );
								container.innerHTML = "";
								for ( var i = temp_container.childNodes.length; i > 0; i -- ) { 
									container.appendChild ( temp_container.childNodes[0] );
								}
								if ( container.id == "portal_body" ) {
									if ( meta.title ) document.title = $('<div/>').html(meta.title).html();
									$(document.body).addClass ( (meta['class']||"").replace(/\s*_\w+/,'') ).removeClass ( (meta['noclass']||"").replace(/\s_\w+/,'') );
								}
								$(container).show();

								/*
								if ( window.G_vmlCanvasManager && G_vmlCanvasManager.initElement ) {
									$("canvas",container).each ( function ( ) {
										if ( !this.getContext )
											G_vmlCanvasManager.initElement ( this );
									} );
								}
								*/
								var head = document.getElementsByTagName('head')[0];
								$.each ( scripts, function ( i, script ) {
									var src = (script.match(/src="([^"]+)"/)||[]).pop();
									var rel = (script.match(/rel="([^"]+)"/)||[]).pop();
									if ( rel == "static" ) return;
									var code = (script.match(/<script[^>]*>([\s\S]*?)<\/script>/)||[]).pop();
									if ( !src || ( rel == "required" ) || !scriptExists(src)  ) {
										var s = document.createElement("script");
										if ( src ) s.src = src;
										if ( rel ) s.rel = rel;
										if ( code ) {
											//code = "alert('Hello');";
											try {
												s.appendChild ( document.createTextNode ( code ) );
											} catch ( e ) {
												s.text = code;
												//s.innerText = code;
												//s.textContent = code;
											}
											setTimeout ( function ( ) { head.appendChild ( s ); }, 50 );
										} else {
											head.appendChild ( s );
										}
										//if ( !src ) s.parentNode.removeChild ( s );
									}
								} );
								$(window).resize();
								fixPage();
								document.getElementById("portal_middle").scrollTop = 0;
								if ( typeof callback == "function" ) {
									callback ( src, data, meta );
								}
							}
							setTimeout ( finishPage, 500 );
						}, 100 );
					} else {
						// TO DO - work out what to do with HTTP failures
					}
				} );
			}
		} )(),
		showMessage : ( function ( ) {
			var messages = {};
			/*
			return function ( msg, style ) {
				var container = $('<table class="message"><tr><td></td></tr></table>').appendTo("#portal_overlay");
				var message = $('<table class="dialog">'
									+ '<tr><td class="tl"></td><td class="t"></td><td class="tr"></td></tr>'
									+ '<tr><td class="l"></td><td class="c"></td><td class="r"></td></tr>'
									+ '<tr><td class="bl"></td><td class="b"></td><td class="br"></td></tr>'
								+ '</table>').appendTo($('td',container));
				$('td.c',message).css(style||{}).html(msg);
				$('#portal_overlay').css("display","block");
				var id = "msg-" + (""+Math.random()).substr(2);
				messages[id] = true;
				return {
					contents : msg,
					container : $('td.c',message),
					close : function ( ) {
						if ( container[0] && container[0].parentNode )
							container[0].parentNode.removeChild ( container[0] );
						delete messages[id];
						for ( m in messages )
							return;
						$('#portal_overlay').css("display","none");
					}
				}
			}
			*/
			return function ( msg, style, oldschool ) {
				if ( oldschool ) {
					var container = $('<table class="message"><tr><td class="messageContainer"></td></tr></table>').appendTo("#portal_overlay");
					var message = $('<table class="dialog">'
										+ '<tr><td class="tl"></td><td class="t"></td><td class="tr"></td></tr>'
										+ '<tr><td class="l"></td><td class="c"></td><td class="r"></td></tr>'
										+ '<tr><td class="bl"></td><td class="b"></td><td class="br"></td></tr>'
									+ '</table>').appendTo($('td',container));
					$('td.c',message).css(style||{}).html(msg);
					var rsp_box = $('td.c',message);
				} else {
					var box = document.createElement('div');
					box.className = "hud-box";
					var wrapper = document.createElement('div');
					wrapper.className = "hud-box-wrapper";
					box.appendChild ( wrapper );
					var inner = document.createElement('div');
					inner.className = "hud-box-inner";
					wrapper.appendChild ( inner );
					var content = document.createElement('div');
					content.className = "hud-box-content";
					inner.appendChild ( content );
					var message = document.createElement('div');
					message.className = "hud-message";
					content.appendChild ( message );
					if ( typeof msg == "string" ) {
						message.innerHTML = '<p style="margin:0;">' + msg + '</p>';
					} else if ( msg.nodeType ) {
						message.appendChild ( msg );
					}
					var container = $('<table class="message"><tr><td></td></tr></table>')
						.appendTo ( "#portal_overlay" )
						.find('td')
							.append ( box )
						.end();
					var rsp_box = $(message);
				}
				$("#portal_overlay").css ( "display", "block" );
				var id = "msg-" + (""+Math.random()).substr(2);
				messages[id] = true;
				$(window).resize();
				return {
					container : rsp_box,
					close : function ( ) {
						if ( container[0] && container[0].parentNode )
							container[0].parentNode.removeChild ( container[0] );
						delete messages[id];
						for ( m in messages ) return;
						$('#portal_overlay').css("display","none");
					}
				}
			}
		} )(),
		confirm : ( function ( ) {
			return function ( msg, cb, raw ) {
				var content = document.createElement('div');
				content.className = "confirm";
				content.innerHTML = '<div>' + ( raw ? msg : msg.split(/\n/).join('<br>') ) + '</div>';
				var buttons = document.createElement('div');
				buttons.className = "hud-buttons";
				content.appendChild ( buttons );
				var yes = document.createElement('button');
				yes.className = "hud-button";
				yes.innerHTML = "<span><span>Yes</span></span>";
				buttons.appendChild ( yes );
				var no = document.createElement('button');
				no.className = "hud-button";
				no.innerHTML = "<span><span>No</span></span>";
				buttons.appendChild ( no );
				var msg = Portal.showMessage ( content );
				var click = function ( rsp ) {
					return function ( ) {
						msg.close();
						if ( typeof cb == "function" ) cb ( rsp );
					}
				}
				$(yes).click ( click ( true ) ).focus();
				$(no).click ( click ( false ) );
				return msg;
			}
		} )(),
		alert : ( function ( ) {
			return function ( msg, cb, raw, buttonLabel ) {
				var content = document.createElement('div');
				content.className = "alert";
				content.innerHTML = '<div>' + ( raw ? msg : msg.split(/\n/).join('<br>') ) + '</div>';
				var buttons = document.createElement('div');
				buttons.className = "hud-buttons";
				content.appendChild ( buttons );
				var ok = document.createElement('button');
				ok.className = "hud-button";
				ok.innerHTML = "<span><span>" + ( buttonLabel||"OK" ) + "</span></span>";
				buttons.appendChild ( ok );
				var msg = Portal.showMessage ( content );
				$(ok).click ( function ( ) {
					msg.close();
					if ( typeof cb == "function" ) cb ( true );
				} ).focus();
				return msg;
			}
		} )(),
		showNotification : ( function ( ) {
			var timeout;
			return function ( elem, msg, delay ) {
				var anchor = $('<span class="notification"><span class="notificationAnchor"></span></span>').prependTo ( elem );
				if ( !anchor.length ) return false;
				clearTimeout ( timeout );
				if ( isNaN ( parseInt ( delay ) ) ) delay = 2;
				$(".notification .notificationAnchor .notificationMessage").stop(true).animate (
					{ opacity : 0, bottom : "+=10" }, {
						duration: 100,
						complete : function ( ) {
							$(this.parentNode.parentNode).remove();
						}
					}
				);
				var bubble = $('<div class="notificationMessage"><div class="top"></div><div class="middle"></div><div class="bottom"></div></div>');
				$(".middle",bubble).html ( msg );
				$(".notificationAnchor", anchor )
					.css ( { width : $(elem).innerWidth() } )
					.append ( bubble );
				bubble
					.css ( { opacity : 0, bottom : -10 } )
					.animate ( { opacity : 1, bottom : 0 }, { complete : function ( ) {
						timeout = setTimeout ( function ( ) {
							bubble.animate ( { opacity : 0, bottom: 10 }, { duration : 1000, complete : function ( ) {
								anchor.remove();
							} } );
						}, parseInt ( delay ) * 1000 );
					} } );
			}
		} )(),
		incrementAchievements : function ( ) {
			var achievements = $("#achievementCount").text().split("/");
			var current = parseInt ( achievements[0] );
			var total = parseInt ( achievements[1] );
			if ( current == total ) return false;
			$("#achievementCount").text((current+1)+'/'+total);
			return true;
		},
		setObjective : ( function ( objective ) {
			if ( objective && objective.length ) {
				metadata['objective'] = objective;
				$("#tb_objective")
					.removeClass("disabled")
					.attr("disabled",false)
					.effect("pulsate",{times:3},500);
			} else {
				metadata['objective'] = null;
				$("#tb_objective").addClass("disabled").attr("disabled",true);
			}
		} ),
		setHint : function ( hint, cb, confirm ) {
			if ( hint && hint.length ) {
				metadata['hint'] = hint;
				$("#tb_hint")
					.data("action",cb||"")
					.data("confirm",confirm||"")
					.removeClass("disabled")
					.attr("disabled",false)
					.effect("pulsate",{times:3},500);
			} else {
				metadata['hint'] = null;
				$("#tb_hint")
					.data("action","")
					.addClass("disabled")
					.attr("disabled",true);
			}
		},
		hintsUsed : function ( used ) {
			var rsp = hintsUsed;
			if ( used !== null ) hintsUsed = !!used;
			return rsp;
		},

		findEntryPoint : function ( cb ) {
			var list = [];
			var currentHash = window.location.hash.substring(1);
			if ( currentHash == "start" ) {
				cb ( null );
			} else if ( currentHash != "" ) {
				var ep = Mission.tasks(currentHash);
				if ( ep ) {
					var previous = Mission.tasks(ep.get('index')-1);
					if ( !previous || previous.completed() ) {
						// see if they have a cookie to skip confirmation
						if ( $.cookie( 'skip_confirm_task' ) == '1' ) {
							// delete the cookie and get started
							$.cookie( 'skip_confirm_task', null );
							cb ( parseInt ( ep.get('index') ) );
						} else {
							// confirm they want to start at this point
							Portal.confirm ( "Do you want to start " + Mission.get('name') + " at <em>" + ep.get('name') + "</em>?", function ( rsp ) {
								if ( rsp ) {
									cb ( parseInt ( ep.get('index') ) );
								} else {
									// reload page - ignore the entry point
									window.location = window.location.protocol + "//" + window.location.host + window.location.pathname + window.location.search;
								}
							} );
						}
					} else {
						// reload page - this entry point hasn't been reached yet
						window.location = window.location.protocol + "//" + window.location.host + window.location.pathname + window.location.search;
					}
				} else {
					// reload page - this entry point doesn't exist
					window.location = window.location.protocol + "//" + window.location.host + window.location.pathname + window.location.search;
				}
			} else {
				for ( var i = 0; i < Mission.length(); i ++ ) {
					var task = Mission.tasks(i);
					list.push ( task );
					if ( !task.completed() ) break;
				}
				if ( list.length <= 1 ) {
					cb ( null );
				} else {
					var content = document.createElement('div');
					content.innerHTML = '<div><strong>It looks like you\'ve played ' + Mission.get('name') + ' before</strong><br>&mdash; do you want to pick a task to start from?</div><hr>';
					var items = document.createElement('ul');
					items.className = "hud-options";
					content.appendChild ( items );
					
					for ( var i = 0; i < list.length; i ++ ) {
						var item = document.createElement('li');
						$(item).html("&bull; ").css({paddingLeft:'1em',textIndent:'-1em',lineHeight:'1.5em'});
						/*
						var button = document.createElement('button');
						button.className = 'hud-button';
						button.innerHTML = '<span><span>' + list[i].get('name') + '</span></span>';
						item.appendChild ( button );
						var info = document.createElement('span');
						info.innerHTML = list[i].get('description');
						//item.appendChild ( info );
						*/
						var link = document.createElement('a');
						link.href = "#" + list[i].get('label');
						link.className = "item";
						link.innerHTML = list[i].get('name');
						item.appendChild ( link );
						item.innerHTML += " - " + list[i].get('description');
						items.appendChild ( item );
					}
					
					content.innerHTML += "<hr>";
					var buttons = document.createElement('div');
					buttons.className = "hud-buttons";
					content.appendChild ( buttons );
					var btn = document.createElement('button');
					btn.className = "hud-button";
					$(btn).css({display:'block',width:'100%',position:'relative',left:'-10px'});
					btn.innerHTML = "<span><span>No, I'll start from the beginning</span></span>";
					buttons.appendChild ( btn );
					var msg = Portal.showMessage ( content );
					$('a.item',msg.container)
						.css ( 'color', "#fff" )
						.click ( function ( e ) {
							var ep = this.getAttribute('href').substring( this.getAttribute('href').indexOf( '#' ) + 1 );
							msg.close();
							cb ( ep );
							return false;
						} );
					$(btn).click ( function ( ) {
						msg.close();
						cb ( null );
					} );
				}
			}
		}
	}

} ) ( jQuery );

( function ( $ ) {
	var hide = function ( ) {
		$("#subtitles").animate ( { opacity : 0 }, {
			duration: 1000,
			complete: function ( ) {
				$("p",this).remove();
			}
		} );
	}
	var currentSubtitleId = null
	window.showSubtitle = function ( subtitle, ttl ) {
		clearTimeout ( currentSubtitleId );
		var subtitleId = (""+Math.random()).substr(2);
		$("#subtitles").stop(true).css({"opacity":1,"display":"block"}).html ( subtitle ? '<p>' + subtitle + '</p>' : '' );
		if ( !isNaN ( parseFloat ( ttl ) ) )
		currentSubtitleId = subtitleId = setTimeout ( function ( ) {
			window.hideSubtitle ( subtitleId );
		}, parseFloat ( ttl ) * 1000 );
		return subtitleId;
	}
	window.hideSubtitle = function ( subtitleId ) {
		clearTimeout ( subtitleId || currentSubtitleId );
		hide();
	}
} )( jQuery );

( function ( $ ) {
	$( function ( ) {
		$('#textmessage-dismiss').click(hideTextMessage);
	} );
	var textMessageAlert = { play : function ( ) { } };
	SoundStage.whenReady ( function ( ) {
		textMessageAlert = new SoundStage.Sound( '/audio/text-message-alert.mp3', {
			noSubtitles : true,
			id : "textmessage-" + (""+Math.random()).substr(2)
		} );
	} );
	var hide = function ( ) {
		$("#textmessage").animate ( { opacity : 0 }, {
			duration: 1000,
			complete: function ( ) {
				$("#textmessage-content p",this).remove();
				$( this ).hide();
			}
		} );
	}
	window.showTextMessage = function ( message, sender, btnText, cb ) {
		textMessageAlert.play();
		$("#textmessage")
			.stop(true)
			.css({"opacity":1,"display":"block"});
		$("#textmessage-content")
			.html( '<p class="from">From ' + sender + '</p><p>' + message + '</p>' );
		$("#textmessage-dismiss")
			.text ( btnText || "Close" )
			.one ( "click", function ( ) {
				if ( typeof cb == "function" ) cb();
			} );
	}
	window.hideTextMessage = function ( ) {
		hide();
	}
} )( jQuery );
