//	Javascript for Tinderbox Timeline
//	Copyright 2010 Eastgate Systems Inc All Rights Reserved
//	http://www.eastgate.com/
//
//	Mark Bernstein
//		
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//	want to use this?  contact info@eastgate.com 


// 1.0.1:  justify events

if (typeof Object.create != 'function') {
	Object.create=function(o) {
		var F=function(){};
		F.prototype=o;
		return new F();
		};
	}
	
var Timeline= function(){

	// local and private
	
	var versionString="1.0.1 © Copyright 2010 by Eastgate Systems, Inc. All Rights Reserved";
	
	function reset(timeline) {
		timeline.bands=1;
		timeline.bandLabels=[];
		timeline.events=[];
		timeline.labelSize=12;
		timeline.scaleHeight=36;
		timeline.backgroundColor="rgba(255,255,255,1)";
		timeline.bandColor="rgba(0,255,128,0.1)";
		timeline.bandLabelColor="rgba(0,0,0,0.1)";
		timeline.bandLabelFont="sans-serif";
		timeline.element='timeline';
		}
		
	function sort(timeline) {
		timeline.events.sort(function(a,b) {
			if (a.startDate<b.startDate) {return -1;}
			if (a.startDate>b.startDate) {return 1;}
			return 0;
			});
		}

	function createStamp(theMessage) {	
		// an object that displays an overlay, such as "draft", stamped over the timeline
		var messageText=theMessage||"";
		var initialized=false;
		var isInside=false;
		var width=0;
		var timeline=null;
		var left=0;
		var top=0;
		var tsize=96;
		var that;
		
		function initialize(inTimeline) {
			if (initialized) {return;}
			timeline=inTimeline;
			width=0;			
			left=24;
			top=tsize;	
			isInside=false;
			initialized=true;
			that.color=inTimeline.stampColor;
			}

		
		return {
		
		color: "rgba(255,192,192,0.2)",
		
		draw: function(inTimeline) {
			that=this;
			initialize(inTimeline);
			
			timeline.save();
		
			timeline.ctx.font=tsize+"pt sans-serif";
			timeline.ctx.fillStyle=this.color;
			
			if (width===0) {width=timeline.ctx.measureText(messageText).width-tsize;}
			if (width>0) {
				var ang=Math.asin((timeline.scale.getTop()-top-tsize/2)/width);
				if (ang>45*Math.PI/360) {ang=45*Math.PI/360;}
				timeline.ctx.rotate(ang);
					
				timeline.ctx.fillText(messageText,left,top);
				}
			timeline.restore();
			}
		};
		}
		
	function firstEvent(timeline) {
		if (timeline.events.size()===0) {return null;}
		sort(timeline);
		return timeline.events[0];
		}
		
	function lastEvent(timeline) {
		if (timeline.empty()) {return null;}
		var e=timeline.events;
		e.sort(function(a,b) {
			var da=a.latestDate();
			var db=b.latestDate();
			if (da<db) {return -1;}
			if (da>db) {return 1;}
			return 0;
			});
		return e[e.size()-1];
		}
		
	function drawBackground(timeline) {
		timeline.save();
		timeline.ctx.fillStyle=timeline.backgroundColor;
		timeline.ctx.fillRect(0,0,timeline.width(),timeline.height());
		timeline.restore();
		}
		
	function drawFrame(timeline) {
		timeline.save();
		timeline.ctx.strokeStyle = timeline.frameColor;
		timeline.ctx.strokeRect(0,0,timeline.width(),timeline.height());
		timeline.restore();
		}
		
	function drawBandLabel(timeline,i) {
		if (!timeline.bandLabels[i]) {return;}
		timeline.save();
		var top=timeline.bandTop(i);
		var height=timeline.bandHeight(i);
		var y=top+height-12;
		timeline.ctx.font="24px "+ timeline.bandLabelFont;
		timeline.ctx.fillStyle=timeline.bandLabelColor;
		
		timeline.ctx.fillText(timeline.bandLabels[i],4,y);
		timeline.restore();
		}
		
	function drawBands(timeline) {
		var i;			
		timeline.save();
		timeline.ctx.fillStyle=timeline.bandColor;

		for (i=0;i<timeline.bands;i++) {
			var top=timeline.bandTop(i);
			var height=timeline.bandHeight(i);
			if (i%2==1){
				timeline.ctx.fillRect(0,top,timeline.width(),height);
				}
				
			drawBandLabel(timeline,i);
			}
		timeline.restore();
		}
		

		
	function eventAt(mouseEvent,timeline) {
		// find the timeline item hit by a mouse event
		var hit=timeline.events.find(function(e) {
			var myPos=timeline.canvas.cumulativeOffset();
			var x=mouseEvent.pointerX()-myPos[0];
			var y=mouseEvent.pointerY()-myPos[1];
			

			if (x<e.xpos) { return false;}
			if (x>e.xpos+e.width) { return false;}
			if (y<e.ypos-12) { return false;}
			if (y>e.ypos+12) { return false;}
			
			return true;
			});
		return hit;
		}
		
			
	function addEvent(event) {
		if (event.band>=event.timeline.bands) {
			event.timeline.bands=event.band+1;
			}			
		event.timeline.events.push(event);
		event.timeline.needsLayout=true;
		}
	
	
	
	// ----------------------------------------------------------------------------

	return {

	version: versionString,

	TimelineEvent: function(){
		var timeline=null;
		var that=null;
				
		function drawTimelineMarker(x,y) {
			var timeline=that.timeline;
			timeline.save();
			timeline.ctx.strokeStyle= "rgba(0,0,0,0.2)"; 
			timeline.ctx.beginPath();
			timeline.ctx.moveTo(x,y+8);
			timeline.ctx.lineTo(x,timeline.scale.getTop());
			timeline.ctx.stroke();
			timeline.restore();
			}
			
		function drawMarker(start,end,y) {
			var timeline=that.timeline;
			timeline.save();
			if (end<=start) {
				timeline.ctx.fillStyle = that.markerColor;  
				timeline.ctx.fillRect(start-4,y-4,8,8);
				timeline.restore();
				return;
				}
			timeline.ctx.strokeStyle= that.markerColor; 
			timeline.ctx.beginPath();
			timeline.ctx.moveTo(start,y+2);
			timeline.ctx.lineTo(start,y+6);
			timeline.ctx.lineTo(end,y+6);
			timeline.ctx.lineTo(end,y+2);
			timeline.ctx.stroke();
			timeline.restore();
			}
		

	return {
		band: 0,
		startx:	0,
		endx: -1,
		y:	0,
		label: "",
		hasMarker: false,
		timeline: null,
		
		draw: function() {
			that=this;			
			var timeline=this.timeline;
			var ctx=timeline.ctx;
			var xpos=timeline.width()*this.startx;
			var xend=timeline.width()*this.endx;		
			var bandy=timeline.bandTop(this.band);
			var ypos=bandy+timeline.bandHeight(this.band)*this.y;
			
			this.xpos=xpos;
			this.ypos=ypos;
			this.width= ctx.measureText(this.label).width;
			
			ctx.save();
			
			drawMarker(xpos,xend,ypos);
			if (this.hasMarker) {
				drawTimelineMarker(xpos,ypos);
				}
			if (this.URL) {
				ctx.fillStyle = this.linkColor;  
				}
			else {
				ctx.fillStyle=this.labelColor;
				}
				
			var xx=xpos+8;
			
			if (this.justify=="right") {ctx.textAlign="right";xx-=16;}
			else {ctx.textAlign="left";}
				
			ctx.fillText(this.label,xx,ypos);
			ctx.restore();
			},
	
		make: function(inTimeline,param) {
			that=Object.create(this);
			timeline=inTimeline;
			that.timeline=inTimeline;
			
			// mandatory fields: start(date), label(string)
			// optional fields:  
			//	end(date), y(0-1), tip(string), marker(bool), URL(string)
			//	labelColor(color),linkColor(color), markerColor(color)
			
			var band=0;
			if (param.band) {band=param.band;}
			that.band=band;	
			
			var startDate=param.start; 
			if (!startDate) {startDate=(new Date()).toString();}
			var endDate=param.end; 
			if (!endDate) {endDate="";}
			
			that.startDate=new Date(startDate);
			if (endDate.length) {
				that.endDate=new Date(endDate);
				}
			else {
				that.endDate=null;
				}
				
			if (param.y) {that.y=param.y;}
			else {that.y=0.2;}
				
			that.label=param.label;
			that.labelColor=timeline.labelColor;
			if (param.labelColor) {that.labelColor=param.labelColor;}
			that.linkColor=timeline.linkColor;
			if (param.linkColor) {that.linkColor=param.linkColor;}
			that.markerColor=inTimeline.markerColor;
			if (param.markerColor) {that.markerColor=param.markerColor;}
			
			if (param.justify) {that.justify=param.justify;}
			
			that.tip=param.tip;	// set this to a string for a mouseover tooltip
			that.URL=param.URL;	// set this to a string for a link
			that.hasMarker=param.marker;
			
			addEvent(that);
			return that;
			},
			
		latestDate: function(){
			if (this.endDate!==null) {return this.endDate;}
			return this.startDate;
			}
		};
	}(),
	

	needsLayout: false,
	element: 'timeline',	// DOM id of the canvas that contains this timeline
	stampColor: "rgba(0,0,0,0.2)",
	scaleColor: "rgb(128,128,128)",
	labelFont:	"Museo,Helvetica Neue,Helvetica,Arial,sans-serif",
	frameColor:	"rgba(0,0,0,0.2)",
	markerColor: "rgb(192,0,0)",
	labelColor: "rgb(0,0,0)",
	linkColor: "rgb(0,0,192)",
	
	create: function(options) {
// mandatory options: 
//	element(id of canvas)
// other options:
// bands (array of strings)
// bandColor(color), backgroundColor(color)
// labelSize (points)
// labelFont (string)
// labelColor (color)
// bandLabelColor(color)
// bandLabelFont(string)
// scaleHeight (px)
// stampColor (color)
// scaleColor (color)
// markerColor (color)

		var that=Object.create(Timeline);
		
		reset(that);
		
		if (options && options.element) {
			that.element=options.element;
			}
			
		if (options && options.bandHeights) {
			that.bandHeights=options.bandHeights;
			}
		
		if (options && options.bands) {
			that.bandLabels=options.bands;
			}
		if (options&&options.backgroundColor) {
			that.backgroundColor=options.backgroundColor;
			}
		if (options&&options.bandColor) {
			that.bandColor=options.bandColor;
			}
		if (options&&options.stampColor) {
			that.stampColor=options.stampColor;
			}
		if (options&&options.bandLabelColor) {
			that.bandLabelColor=options.bandLabelColor;
			}
		if (options&&options.bandLabelFont) {
			that.bandLabelFont=options.bandLabelFont;
			}
		if (options&&options.labelSize) {
			that.labelSize=options.labelSize;
			}
		if (options&&options.labelFont) {
			that.labelFont=options.labelFont;
			}
		if (options&&options.scaleHeight) {
			that.scaleHeight=options.scaleHeight;
			}
		if (options&&options.scaleColor) {
			that.scaleColor=options.scaleColor;
			}
		if (options&&options.stamp) {
			that.stamp=createStamp(options.stamp);
			}
		if (options&&options.frameColor) {
			that.frameColor=options.frameColor;
			}
		if (options&&options.markerColor) {
			that.markerColor=options.markerColor;
			}
		if (options&&options.labelColor) {
			that.labelColor=options.labelColor;
			}
		if (options&&options.linkColor) {
			that.linkColor=options.linkColor;
			}
			
		// privileged function
		that.scale=that.TimelineScale.create(that);
		that.watermark=that.createWatermark();
		
		return that;
		},
	
	withEvents: function(events) {
		this.data=events;
		Timeline.register(this);
		},
		
	toString: function() {
		return "Timeline with "+this.events.size()+" events";
		},

		
	addEvents: function(events) {
		var that=this;
		events.each(function(e) {that.TimelineEvent.make(that,e);});
		},
		
	someTestEvents: [
		{band: 0, start: "Jan 1, 1874", end: "Dec 15, 1876",label: "Magdalen, Oxford"} ,
		{band: 0, start: "Jan 1, 1875",y:0.4,label:"Travels in Italy"},
		{band: 1, start: "Jan 1, 1876",label: "First Poems Published",
			URL:"http://www.eastgate.com", tip: "early work", marker: true },
		{band: 0, start: "Jan 1,1877",label: "Wins Newdigate Prize; takes double first"}
		],
		
		
	someMayEvents:	[
		{band: 0, start: "May 10, 1940 9:00 AM",label: "May 10"} ,
		{band: 0, start: "May 10, 1940 5:00 PM",label: "May 10 late", y:0.6} ,
		{band: 0, start: "May 12, 1940",label: "May 12", y:0.4},
		{band: 0, start: "May 15, 1940",label: "May 15", y:0.6} 
		],
		

		
	layout: function() {
		if (!this.needsLayout) {return;}
			
		var starting=firstEvent(this).startDate;
		var ending=lastEvent(this).latestDate();	// might be startDate or endDate
		var padding=(ending-starting)*0.2;
		if (padding<1000) {padding=1000;}
		
		this.startDate=new Date();
		this.startDate.setTime(starting.getTime()-padding);
		this.endDate=new Date();
		this.endDate.setTime(ending.getTime()+padding);
		
		var span=this.endDate-this.startDate;
		
		var i;
		for (i=0;i<this.events.size();i++) {
			var e=this.events[i];
			e.startx=(e.startDate-this.startDate)/span;
			if (e.endDate) {
				e.endx=(e.endDate-this.startDate)/span;
				}
			else {
				e.endx=-1;
				}
			}	
		this.needsLayout=false;
		},
	
		
	click: function(event,timeline) {
		if (this.watermark.hit(event)) {
			window.open(this.watermark.URL());
			return;
			}
		var hit=eventAt(event,timeline);
		
		if (hit && hit.URL) {		// follow a links
			window.open(hit.URL);
			}
		},
		
	hover: function(event,timeline) {
		if (this.watermark.track(event)) {
			return;
			}
		var hit=eventAt(event,timeline);
		if (hit===timeline.hoverEvent) {
			return;
			}
		timeline.hoverEvent=hit;	
		this.tip.hide();
		if (hit  && hit.URL) {
			timeline.canvas.style.cursor="pointer";
			}
		else {
			timeline.canvas.style.cursor="auto";
			}
		if (hit && hit.tip ) {
			timeline.tip.show(timeline,hit);
			}
		},
		
	// the tip appears in an absolutely-position div with class "tip"
	// the div appears in the DOM immediately after the canvas; if there isn't 
	// already a suitable div, we make one ourselves.  
	//
	//	tip.show(timeline,event) reveals the hint
	//	tip.hide() hides it
	//	tip.current() returns the element displaying the tip's div, if it exists
	
	tip: function() {
		
		var	currentTip=null;
		
		function get(canvas){
			if (!currentTip) { currentTip=canvas.next('tip');}
			if (!currentTip) { 
				currentTip=new Element('div').addClassName("tip").addClassName("hiddentip");
				currentTip.setStyle( {color:"#FFFFFF", backgroundColor:"rgba(0,0,0,0.5)",fontSize:"10px",padding:"6px"});
				canvas.insert({after: currentTip});
				}
			return currentTip;
			}
		
		return {
			current: function(timeline) { 
				return get(timeline.canvas);
				},
			
			hide: function() {
				if (!currentTip) { return;}
				if (currentTip.hasClassName('hiddentip')) { 
					return;
					}
				currentTip.fade({duration:0.5});
				currentTip.addClassName('hiddentip');
				},
				
			show: function(timeline,event) {
				var what=get(timeline.canvas);	
				var myPos=timeline.canvas.cumulativeOffset();
				var x=myPos[0];
				var y=myPos[1];
				
				what.removeClassName("hiddentip");
				what.setOpacity(0);
				what.setStyle({position: "absolute",top: y+event.ypos+"px",left:x+event.xpos+event.width+12+"px",'z-index':"1"});		
				what.innerHTML="<p>"+event.tip+"</p>";
				what.show();
				what.appear({duration:0.5});
				}
			};
		}(),
		
		
		
	showTip: function(event) {
		this.tip.show(this,event);
		},

	draw: function(theCanvas) {  
		var canvas = $(theCanvas); 
		// the user must set the element of the timeline to the id of the canvas in which it's drawn
		if (!canvas){return;}	
		var ctx = canvas.getContext("2d"); 
		
		this.ctx=ctx;
		this.canvas=canvas;
		
		var theTimeline=this;
		canvas.observe('click',function(e){ theTimeline.click(e,theTimeline);}  );
		canvas.observe('mousemove',function(e){ theTimeline.hover(e,theTimeline);}  );
	
		this.layout();
		
		ctx.font=this.labelSize+"px "+this.labelFont;
		
		drawBackground(this);
		drawBands(this);
		this.scale.draw();
		this.events.each(function(e) {e.draw();});
		if (this.watermark) {this.watermark.draw(this);}
		if (this.stamp) {
			this.stamp.draw(this);
			}		
		drawFrame(this);
		
		}  ,
		
	bandHeights: [],
	
	bandTop: function(band) {
		if (this.bandHeights.size()>=band) {
			if (band<=0) {return 0;}
			return (this.bandHeights[band-1]*(this.scale.getTop()/this.bandHeights[this.bands-1]));
			}
		return (band*(this.scale.getTop())/this.bands);
		},
	bandHeight: function(band) {
		if (this.bandHeights.size()>=band) {
			return this.bandTop(band+1)-this.bandTop(band);
			}
		return (this.scale.getTop())/this.bands;
		},

	watermark : null,
		
	
	createWatermark: function()	{ 
		var watermarkText="made with Tinderbox";
		var URL= "http://www.eastgate.com/Tinderbox/";
		var initialized=false;
		var isInside=false;
		var width=0;
		var height=18;
		var timeline=null;
		var left=0;
		var top=0;
		
		function initialize(inTimeline) {
			if (initialized) {return;}
			timeline=inTimeline;
			if (timeline.ctx) {
				width=timeline.ctx.measureText(watermarkText).width;
				}
			height=18;
			left=timeline.width()-4-width;
			top=timeline.scale.getTop()-height;	
			isInside=false;
			initialized=true;
			}
			
		function fill(style){
			timeline.ctx.fillStyle=style;
			timeline.ctx.fillRect(left,top,width,height);
			}
			
		
		return {
		
		URL: function() {return URL;},		//read-only
	
		draw: function(inTimeline) {
			initialize(inTimeline);

			timeline.save();
		
			fill(timeline.backgroundColor);
			if (timeline.bands%2===0) {
				fill(timeline.bandColor);
				}
			if (isInside) {timeline.ctx.fillStyle="rgb(64,64,64)";}
			else {timeline.ctx.fillStyle="rgb(192,192,192)";}
			timeline.ctx.fillText(watermarkText,left,top+14);
			
			timeline.restore();
			},
			
		hit: function(mouseEvent) {
			var myPos=timeline.canvas.cumulativeOffset();
			var x=mouseEvent.pointerX()-myPos[0];
			var y=mouseEvent.pointerY()-myPos[1];
			if (y<top) { return false;}
			if (y>top+height) { return false;}
			if (x<left) { return false;}
			if (x>left+width) { return false;}
			return true;
			},
			
		track: function(mouseEvent) {
			var inside=this.hit(mouseEvent);
			if (inside==isInside) { return;}
			isInside=inside;
			this.draw(inside);
			return inside;
			}
			};
		},
		

	stamp : null,		// disclaimer object for this timeline, e.g. "Sample Data" or "DEMO"
	
	TimelineScale:function() {
		// private
		var months=	["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
		var days=	["Sun","Mon","Tue","Wed","Thu","Fri","Sat","Sun"];
		var	day=1000*60*60*24;
		
		var ctx=null;	
		var start=null;
		var end=null;
		var width=0;
		var left=0;
		var top=0;
		var that=null;
		
		// private functions	
		
		function drawTic(x,label) {
			ctx.beginPath();
			ctx.moveTo(x,top);
			ctx.lineTo(x,top+that.timelineHeight);
			ctx.stroke();
			if (label) {
				ctx.fillText(label,x+4,top+that.timelineHeight-4);
				}
			}
			
		function drawMinorTic(x,label) {
			ctx.beginPath();
			ctx.moveTo(x,top);
			ctx.lineTo(x,top+that.timelineHeight/2);
			ctx.stroke();
			if (label) {
				ctx.fillText(label,x+4,top+that.timelineHeight/2+4);
				}
			}
			
		function drawTinyTic(x) {
			ctx.beginPath();
			ctx.moveTo(x,top);
			ctx.lineTo(x,top+that.timelineHeight/4);
			ctx.stroke();
			}
			
		function positionFor(theDate){
			var span=end-start;
			if (span===0) {return -1;}
			if (theDate<start) {return -1;}
			return width*(theDate-start)/span;
			}
			
		function drawCenturies() {
			var decadeTics=false;
			var interval=new Date(start.getTime()+3650*day);
			var x=positionFor(interval);
			if (x>5) {decadeTics=true;}
			
			var lastCentury=100*Math.floor(start.getFullYear()/100);
			var date=new Date(lastCentury,0,1);	// start of previous century
			for (;date<end;date=new Date(date.getFullYear()+10,1,1) ) {
				var xx=positionFor(date);
				if (xx<0) {continue;}
				if (date.getFullYear()%100===0){
					drawTic(xx,date.getFullYear());
					}
				else if(decadeTics&&date.getFullYear()%5===0) {drawMinorTic(xx);}
				else
					if(decadeTics) {drawTinyTic(xx);}
				}
			}
			
		function drawDecades() {
			var yearTics=false;
			var interval=new Date(start.getTime()+365*day);
			var xx=positionFor(interval);
			if (xx>5) {yearTics=true;}
			
			var lastDecade=10*Math.floor(start.getFullYear()/10);
			var date=new Date(lastDecade,0,1);	// start of previous decade
			for (;date<end;date=new Date(date.getFullYear()+1,1,1) ) {
				var x=positionFor(date);
				if (x<0) {continue;}
				if (date.getFullYear()%10===0) {
					drawTic(x,date.getFullYear());
					}
				else if(yearTics&&date.getFullYear()%5===0) {
					drawMinorTic(x);
					}
				else{
					if(yearTics) {drawTinyTic(x);}
					}
				}
			}
			
			
		function drawYears() {
			var date=new Date(start.getFullYear(),0,1);	// start of previous year
			for (;date<end;date=new Date(date.getFullYear(),date.getMonth()+3,1) ) {
				var x=positionFor(date);
				if (x<0) {continue;}
				if (date.getMonth()) {
					drawMinorTic(x);
					}
				else{
					drawTic(x,date.getFullYear());
					}
				}
			}
			
		function drawMonths() {
			
			var interval=new Date(start.getTime()+day);
			var xx=positionFor(interval);
			var dayTics=false;
			if (xx>5) {dayTics=true;}
			
			
			var date=new Date(start.getFullYear(),start.getMonth()-1,1);	// start of previous month
			for (;date<end;date=new Date(date.getTime()+day) ) {
				var x=positionFor(date);
				if (x<0) {continue;}
				if (date.getDate()===1){
					drawTic(x,months[date.getMonth()]);
					}
				else if (date.getDay()===0)	{// Sundays
					drawMinorTic(x);
					}
				else{
					if (dayTics) {drawTinyTic(x);}
					}
				}
			}
			
		function drawDays() {
			var interval=new Date(start.getTime()+day);
			var xx=positionFor(interval);
			var dayLabel=false;
			if (xx>36) {dayLabel=true;}
			
			var date=new Date(start.getFullYear(),start.getMonth(),start.getDate()-1);	// start of previous day
			for (;date<end;date=new Date(date.getTime()+day) ) {
				var x=positionFor(date);
				if (x<0) {continue;}
				if (date.getDay()===0){
					this.drawTic(x,days[date.getDay()]+" "+date.getDate());
					}
				else {
					if (dayLabel) {drawMinorTic(x,days[date.getDay()]);}
					else {drawMinorTic(x);}
					}
				}
			}
			
		function  drawHours() {
			var interval=new Date(start.getTime()+day);
			var xx=positionFor(interval);
			var noonLabel=false;
			if (xx>200) {noonLabel=true;}
			var hourTics=false;
			if (xx/24>4) {hourTics=true;}
			var ticLabels=false;
			if (xx/24>20) {ticLabels=true;}
			var first=true;
			var label;
			
			var date=new Date(start.getFullYear(),start.getMonth(),start.getDate()-1);	// start of previous day
			for (;date<end;date=new Date(date.getTime()+day/24) ) {
				var x=positionFor(date);
				if (x<0) {continue;}
				if (ticLabels) {
					label=date.getHours()+":00";
					}
				else {
					label="";
					}
				if (first || date.getHours()===0){
					drawTic(x,days[date.getDay()]+" "+date.getDate());
					first=false;
					}
				else if (date.getHours()%6===0){
					if(noonLabel&& date.getHours()==12) {drawMinorTic(x,"noon");}
					else {drawMinorTic(x,label);}
					}
				else 
					if (hourTics) {
						drawMinorTic(x,label);
						}
				}
			}
			

			
		function prepare()   {
			that.timeline.save();
			ctx.fillStyle = that.color;
			ctx.fillRect(left,top,width,that.timelineHeight);  
			ctx.strokeStyle = that.timeline.backgroundColor;
			ctx.fillStyle=that.timeline.backgroundColor;
			ctx.lineWidth=1.0;
			}
		
		return {

		create: function(inTimeline) {
			var that=Object.create(Timeline.TimelineScale);
			
			that.ctx=null;
			that.timeline=inTimeline;
			that.color=inTimeline.scaleColor;
			that.timelineHeight=that.timeline.scaleHeight;
			return that;
			},
		
			
		getTop: function() {return this.timeline.height()-this.timelineHeight;},

		draw: function(){
			ctx=this.timeline.ctx;
			that=this;
			
			width=this.timeline.width();
			left=0;
			top=this.getTop();
			
			this.timeline.layout();	// be sure the timeline layout up to date
			start=this.timeline.startDate;
			end=this.timeline.endDate;
			
			var span=end-start;
			
			var year=365*day;
			var month=30*day;
			var hour=day/24;
			
			prepare();
			
			if (span/year>120) {
				drawCenturies();
				}
			
			else if (span/year>20) {
				drawDecades();
				}
			
			else if (span/year>2 ) {
				drawYears();
				}
				
			else if (span/month>1) {
				drawMonths();
				}
			
			else if (span/day>7) {
				drawDays();
				}
				
			else if (span/hour>1) {
				drawHours();
				}
			
			this.timeline.restore();
			}
		};
		}(),
		
	manager: function() {
		var timelines=[];		// private
		function drawOne(theTimeline) {
				theTimeline.addEvents(theTimeline.data);
				theTimeline.draw(theTimeline.element);
				}
		
		return {
			count: function(){return timelines.size();},
			register: function(theTimeline){
				timelines.push(theTimeline);
				},
			remove: function(theTimeline){
				// note: we can't do this in one step because we'd clobber the timelines array before 
				// we were done with its iterator.
				var result=timelines.reject(function(x){return x===theTimeline;});
				timelines=result;
				},	
			setup: function() {
				timelines.each(function(theTimeline) {drawOne(theTimeline);});
				}
			};
		}(),
			
	register: function(theTimeline) { this.manager.register(theTimeline);},
	save: function(){this.ctx.save();},
	restore: function(){this.ctx.restore();},
	width: function() {return this.ctx.canvas.width;},
	height: function() {return this.ctx.canvas.height;},
	empty:	function() {return this.events.size()===0;},
			
	initialize: function() {
		$$('.hidden').each(function(e) {e.style.display="hidden";});
		Timeline.manager.setup();
		}
	};
	}();

	

// end of embedded code destined for target page

Event.observe(window,'load',Timeline.initialize);

