function Presentation(id, map, steps)
 {
	// Presentation fields
	var presentationId = id;
	var displayMap = map;
	var steps = steps ? steps : new Array();
	
	// State Variables
	// Whether or not this Presentation has been disposed - disposed presentations cannot be played.
	var disposed = false;
	
	// Whether or not this Presentatino is currently playing
	var playing = false;
	
	// Whether or not this Presentation should play to the end.  If this is false, this Presentation will stop at the next step.
	var playToEnd = false;
	
	// The index of the current step that is being played
	var currentStepIndex = -1;
	
	// The index of the current sub step that is being played.  This is only matters with CombinedPresentationStep
	var currentSubStepIndex = 0;
		
	// Properties
	this.GetId = function()
	{
		return presentationId;
	}

	// Add Self to PresentationManger collection
	PresentationManager.Instance.Add(this);
	
	// Map Event Handlers
	function OnMapChangeView(e) 
	{
		// Get the actual current step - it may actually be one of the sub steps of a CombinedPresentationStep
		var currentStep = CombinedPresentationStep.prototype.isPrototypeOf(steps[currentStepIndex]) ?
			steps[currentStepIndex].Steps[currentSubStepIndex] :
			steps[currentStepIndex];
			
		// TODO: Figure out what to do if the viewport does not move
		if (PresentationTravelStep.prototype.isPrototypeOf(currentStep) || PresentationSetMapZoomStep.prototype.isPrototypeOf(currentStep))
		{
			PresentationManager.Instance.OnStepComplete(presentationId);
		}
	}

	// Attach Map Event Handlers
	displayMap.AttachEvent("onchangeview", OnMapChangeView);	
	
	// PresentationStep Event Handlers
	this.OnStepComplete = function(stepId, subStepId)
	{
		if (disposed)
		{
			return;
		}
					
		if (CombinedPresentationStep.prototype.isPrototypeOf(steps[currentStepIndex]) && currentSubStepIndex < steps[currentStepIndex].Steps.length - 1)
		{
			// Move to the next sub-step
			currentSubStepIndex++;
			steps[currentStepIndex].PerformStep(presentationId, currentSubStepIndex);
		}
		else 
		{
			if (this.PresentationStepCompleteCallback)
			{
				this.PresentationStepCompleteCallback(presentationId, currentStepIndex, steps[currentStepIndex].Pin);
			}
			
			if (playing && playToEnd)
			{
				this.Next();
			}
			else
			{
				playing = false;
			}
		}
	}
		
	// Playback Methods
	
	this.JumpToStart = function(playStep)
	{
		if (disposed)
		{
			return;
		}
		
		if (this.PresentationStartCallback)
		{
			this.PresentationStartCallback(presentationId);
		}
		
		currentStepIndex = 0;
		currentSubStepIndex  = 0;
		
		if (playStep)
		{
			if (this.PresentationStepBeginCallback)
			{
				this.PresentationStepBeginCallback(presentationId, currentStepIndex, steps[currentStepIndex].Pin);
			}
			
			steps[currentStepIndex].PerformStep(presentationId, currentSubStepIndex);
		}
	}
	
	this.JumpToEnd = function(playStep)
	{
		if (disposed)
		{
			return;
		}
		currentStepIndex = steps.length - 1;
		currentSubStepIndex  = 0;
		
		if (playStep)
		{
			if (this.PresentationStepBeginCallback)
			{
				this.PresentationStepBeginCallback(presentationId, currentStepIndex, steps[currentStepIndex].Pin);
			}
			
			steps[currentStepIndex].PerformStep(presentationId, currentSubStepIndex);
		}
		else if (this.PresentationEndCallback)
		{
			this.PresentationEndCallback(presentationId);
		}
	}
	
	this.Next = function()
	{
		if (disposed)
		{
			return false;
		}

		// Exit early if we've reached the end.
		if (currentStepIndex == steps.length - 1)
		{
			if (playing && this.PresentationEndCallback)
			{
				this.PresentationEndCallback(presentationId);
			}
			playing = false;
			return false;
		}

		// Update the Position
		currentStepIndex++;
		currentSubStepIndex = 0;
		
		// Play the new current step
		if (this.PresentationStepBeginCallback)
		{
			this.PresentationStepBeginCallback(presentationId, currentStepIndex, steps[currentStepIndex].Pin);
		}
		
		steps[currentStepIndex].PerformStep(presentationId, currentSubStepIndex);
		
		return true;
	}
	
	this.Previous = function()
	{
		if (disposed)
		{
			return false;
		}
		
		// Back this up to the previous step.
		if (currentStepIndex != 0)
		{
			currentStepIndex--;
		}
		
		// And start from the very beginning of that step.
		currentSubStepIndex = 0;
		
		// Play the new current step
		if (this.PresentationStepBeginCallback)
		{
			this.PresentationStepBeginCallback(presentationId, currentStepIndex, steps[currentStepIndex].Pin);
		}
		
		steps[currentStepIndex].PerformStep(presentationId, currentSubStepIndex);
		
		return true;		
	}
	
	this.Play = function(playFromStart)
	{
		if (disposed || playing)
		{
			return;
		}
		
		playing = true;
		playToEnd = true;

		if (playFromStart)
		{
			this.JumpToStart(true);
		}
		else
		{
			this.Next();
		}
	}
	
	this.Stop = function()
	{
		if (disposed)
		{
			return;
		}
		
		playing = false;
		playToEnd = false;
	}	
	
	// Clean-up
	this.Dispose = function()
	{
		if (disposed)
		{
			return;
		}
		
		for (var i = 0; i < steps.length; i++)
		{
			steps[i].Dispose();
			steps[i] = null;
		}

		// Detach Map Events
		displayMap.DetachEvent("onchangeview", OnMapChangeView);
		
		steps = null;		
		displayMap = null;
		
		// Don't forget to remove oneself from the Presentation Manager
		PresentationManager.Instance.Remove(this);
	}	
 }