// ticker.js

function Panel( divName )
{
	this._div = document.getElementById( divName )
	this._clipTop = 0
	this._clipHeight = 0
	
	this._DivLeft = function()
	{
		return parseInt( this._div.style.left )
	}
	this._DivTop = function()
	{
		return parseInt( this._div.style.top )
	}
	this._DivBottom = function()
	{
		return this._DivTop() + this._div.offsetHeight
	}
	this._ClipBottom = function()
	{
		return this._clipTop + this._clipHeight
	}
	this._SetClip = function( left, top, width, height )
	{
		var right = left + width
		var bottom = top + height
		// clip rect: <top> <right> <bottom> <left>
		clip = "rect("
		clip += ( top +","+ right +","+ bottom +","+ left +")" )
		this._div.style.clip = clip
	}
	this._IsVisible = function()
	{
		return 0 < this._clipHeight
	}
	this._Describe = function( suppressAlert )
	{
		var s = this._div.id + "\n"
		s += ( "("+ this._div.style.left +", "+ this._div.style.top +"), " )
		s += ( "("+ this._clipTop +", "+ this._clipHeight +")\n" )
		var clipBottom = parseInt( this._div.style.top ) + this._clipTop
		s += ( " "+ this._div.style.top +" "+ clipBottom +"\n" )
		s += ( this._div.style.clip + "\n" )
		if ( !suppressAlert )
		{
			alert( s )
		}
		return s
	}
}

function Ticker( name, left, top, width, height,
					vertSpaceBetween, tickInterval, pauseTime, nudge )
{
	this._name = name
	this._left = left
	this._top = top
	this._width = width
	this._height = height
	this._verticalSpaceBetween = vertSpaceBetween // must be non-zero to prevent overlap
	this._tickInterval = tickInterval
	this._pauseTime = pauseTime
	this._nudge = nudge

	this._topPanel = 0
	this._panels = new Array()

	this._timerId = 0
	this._pausedAt = 0

	this._Start = StartTicker
	this._Append = AppendPanel
	this._Intersects = Intersects
	this._Tick = Tick
	this._PanelsHeight = PanelsHeight
	this._SetClip = SetClip
	this._MoveTo = MoveTo
	this._Bottom = function() { return this._top + this._height }
	this._Describe = function()
	{
		var s = "Ticker: " + this._name +"\n"
		s += "("+ this._left +", "+ this._top +"), ("+ this._width +", "+ this._height +")\n"
		s += ( "between: "+ this._verticalSpaceBetween +", tick: "+ this._tickInterval )
		s += ( ", pause: "+ this._pauseTime + "\n" )
		for ( var i = this._topPanel; i < this._panels.length; ++i )
		{
			s += ( i +": "+ this._panels[ i ]._Describe( true ))
		}
		for ( var i = 0; i < this._topPanel; ++i )
		{
			s += ( i +": "+ this._panels[ i ]._Describe( true ))
		}
		alert( s )
	}
}

function AppendPanel( divName )
{
	var panel = new Panel( divName )
	if ( !panel ) { return }
	var top
	if ( this._panels.length == 0 )
	{
		top = this._Bottom()
	}
	else
	{
		var bottom = this._topPanel - 1
		if ( bottom < 0 )
		{
			bottom = this._panels.length - 1
		}
		top = this._panels[ bottom ]._DivBottom() + this._verticalSpaceBetween
	}

	panel._div.style.left = this._left
	panel._div.style.top = top
	this._SetClip( panel, 0, 0, panel._div.offsetWidth, 0 )
	panel._div.style.visibility = "visible"
	this._panels[ this._panels.length ] = panel
}

function StartTicker()
{
	if ( this._PanelsHeight() < this._height )
	{
		var top = this._top
		for ( var i = 0; i < this._panels.length; ++i )
		{
			var panel = this._panels[i]
			this._SetClip( panel, 0, 0, panel._div.offsetWidth, panel._div.offsetHeight )
			panel._div.style.top = top + this._verticalSpaceBetween
			top += panel._div.offsetHeight
		}
	}
	else
	{
		var command = this._name + "._Tick()"
		this._timerId = setInterval( command, this._tickInterval )
	}
	// else
	// * Handle the case where the sum of the heights of the panels minus the height
	//   of the tallest panel is less than the height of the Ticker.  In other words,
	//   handle the case where the tallest panel is above the Ticker top and must be
	//   moved to the bottom.  Do you move it to the bottom of the ticker or the
	//   bottom of the lowest panel?
}

function Intersects( panel )
{
	if ( panel._DivTop() <= this._top && this._Bottom() <= panel._DivBottom())
	{
		return true
	}
	if ( this._top <= panel._DivTop() && panel._DivTop() <= this._Bottom())
	{
		return true
	}
	if ( this._top <= panel._DivBottom() && panel._DivBottom() <= this._Bottom())
	{
		return true
	}
	return false
}

function Tick()
{
	if ( !this._panels || !this._panels.length ) { return }
	
	var paused = 0 < this._pausedAt ? true : false
	if ( paused )
	{
		// Date().getTime() only works if the new operator is used.
		// Will all these frequent little allocations hurt performance?
		if ( new Date().getTime() < this._pausedAt.getTime() + this._pauseTime )
		{
			return
		}
		else
		{
			this._pausedAt = 0
		}
	}
	
	// move all panels up
	for ( var i = 0; i < this._panels.length; ++i )
	{
		this._panels[i]._div.style.top = this._panels[i]._DivTop() - 1
	}
		
	var topDiv = this._panels[ this._topPanel ]
	
	// pause when top panel meets top (nudge is for aesthetics)
	if ( topDiv._DivTop() == this._top + this._nudge )
	{
		this._pausedAt = new Date()
	}
	
	// when top panel is above the top, then move it to the bottom
	if ( topDiv._DivBottom() == this._top + this._nudge )
	{
		topDiv._clipHeight = 0
		topDiv._clipTop = 0
		this._SetClip( topDiv, 0, 0, topDiv._div.offsetWidth, 0 )
		
		var lowDivIndex = this._topPanel - 1
		if ( lowDivIndex < 0 ) { lowDivIndex = this._panels.length - 1 }	
		var lowDiv = this._panels[ lowDivIndex ]
		topDiv._div.style.top = lowDiv._DivBottom() + this._verticalSpaceBetween
		
		++this._topPanel
		if ( this._panels.length <= this._topPanel )
		{
			this._topPanel = 0
		}
	}	

	// adjust clips of panels
	for ( i = 0; i < this._panels.length; ++i )
	{
		var panel = this._panels[i]
		if ( this._Intersects( panel ))
		{
			if ( this._top <= panel._DivTop())
			{
				if ( panel._clipHeight < this._height )
				{
					++panel._clipHeight
				}
			}
			else
			{
				++panel._clipTop
			}
	
			this._SetClip( panel, 0, panel._clipTop, panel._div.offsetWidth, panel._clipHeight )
		}
	}
}

function PanelsHeight()
{
	if ( !this._panels ) { return 0 }
	var sum = 0
	for ( var i = 0; i < this._panels.length; ++i )
	{
		sum += ( this._panels[i]._div.offsetHeight + this._verticalSpaceBetween )
	}
	// subtract _verticalSpaceBetween from last panel
	if ( 0 < this._panels.length )
	{
		sum -= this._verticalSpaceBetween
	}
	return sum
}

function SetClip( panel, left, top, width, height )
{
	if ( !panel ) { return }
	if ( this._width < width ) { width = this._width }
	panel._SetClip( left, top, width, height )
}

function MoveTo( left, top )
{
	var diffLeft = this._left - left
	var diffTop = this._top - top
	this._left = left
	this._top = top
	for ( var i = 0; i < this._panels.length; ++i )
	{
		var panel = this._panels[i]
		panel._div.style.left = panel._DivLeft() - diffLeft
		panel._div.style.top = panel._DivTop() - diffTop
	}
}

