function Banner(canvas_id, container_id) {
	// 20 fps is believed by the author to be about the minimum required to
	// fool the human eye into precieving movement.
	
	this._fps = 20; 
	this._background = 'rgb(46,46,121)';
	this._outer_colour = 'rgba(46,46,121,0)';
	this._inner_colour = 'rgb(7,7,52)';
	this._corner_radius = 13;
	this._max_rotation = 0.07; // in radians
	this._decoration_position = { x: 650, y: 80 };
	this._reset_position_milliseconds = 2000;
	this._power_down_milliseconds = 5000; // the number of milliseconds after mouse off that we should stop updating
		
	// here ends the configurable portion! ---
		
	this._canvas_id = canvas_id;
	this._container_id = container_id;
	this._canvas = null;
	this._container = null;
	this._mouse_x = 0;
	this._mouse_y = 0;
	this._width = 0;
	this._height = 0;
	this._rotate = 0;
	this._intervalID = null;
	this._last_update = new Date().getTime();
	this._mouse_is_in = false;
	this._target = { x: 0, y: 0 };
	this._current = { x: 0, y: 0 };
	this._canvas_pos = { x: 0, y: 0 };
	this._mouse_out_time;
	
	//* invoke this after the document has loaded
	this.load = function(self) {
		this._canvas = document.getElementById(this._canvas_id);
		this._container = document.getElementById(this._container_id);
		
		if (this._canvas == null || this._container == null)
			return; // broken, dont register any handlers and dont continue the script
		
		this._width = this._canvas.getAttribute('width');
		this._height = this._canvas.getAttribute('height'); 
		this._rotate =  0.075;
		
		this._target.x = this._current.x = this._decoration_position.x;
		this._target.y = this._current.y = this._decoration_position.y;
		
		// setup callbacks
		$(this._container).mouseenter(function(event){
			self.mouse_over_event(self, event);
   		});
   		$(this._container).mouseleave(function(event){
			self.mouse_out_event(self, event);
   		});
		
		this.update();
		
	}
	
	this.update = function() {
		var time = this.seconds_since_last_updated();
		var xdif = (this._target.x - this._current.x) / 10;
		var ydif = (this._target.y - this._current.y) / 10;
		
		//*
		this._current.x += xdif;
		this._current.y += ydif;
		//*/
		
		var width_o2 = this._width / 2;
		
		this._rotate = ((this._current.x - width_o2) / width_o2) * this._max_rotation;
		//*
		if (!this._mouse_is_in) {
			var now = new Date().getTime();
			
			if (now > this._mouse_out_time + this._reset_position_milliseconds) {
				// send the decoration  to the default position
	 			this._target.x = this._decoration_position.x;
	 			this._target.y = this._decoration_position.y;
			}
			if (now > this._mouse_out_time + this._power_down_milliseconds) {
				// dont update or draw anymore
				window.clearInterval(this._intervalID);
				this._intervalID = null;
			}
		}
		//*/
		if (this._canvas.getContext) {       
			this.draw(this._canvas.getContext('2d'));
		}
	}
	
	this.seconds_since_last_updated = function() {
		var now = new Date().getTime(); // - this._last_update;
		var time = (now - this._last_update) / 1000;
		this._last_update = now;
		return time;
	}
		
	this.draw = function(ctx) {
		var radgrad = ctx.createRadialGradient(0,10,2,0,0,70);
		radgrad.addColorStop(0, this._inner_colour);
		radgrad.addColorStop(1, this._outer_colour);


		// solid block of colour
		ctx.fillStyle = this._background;
		ctx.fillRect(0,0,this._width, this._height);
		
		ctx.save();
		// the interactive bit
		ctx.beginPath();
		ctx.translate(this._current.x, this._current.y);
		ctx.rotate(Math.PI*this._rotate);
		ctx.scale(10,3);
		ctx.arc(0, 0, 70, 0, 2 * Math.PI, false);
		ctx.closePath();
		ctx.fillStyle = radgrad;
	 	ctx.fill();
		ctx.restore();
		
		// make the canvas look less square
		this.trim_corners(10, ctx);
		
	}
	
	this.get_offset = function() {
		var el = this._canvas;
		this._canvas_pos.x = 0;
		this._canvas_pos.y = 0;
		while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
			this._canvas_pos.x += el.offsetLeft - el.scrollLeft;
			this._canvas_pos.y += el.offsetTop  - el.scrollTop;
			el = el.parentNode;
		}
	}
	
	this.mouse_over_event = function(self, event) {
		if (this._intervalID == null) {
			this._intervalID = window.setInterval(function() {self.update();}, 1000/this._fps);
		}
		this._mouse_is_in = true;
		// reset time
		this._last_update = new Date().getTime();
		// update the data on the canvas' position
		this.get_offset();
		// add events to listen for mouse movement
		$(this._container).mousemove(function(event){
			self.mouse_move_event(self, event);
   		});
	}
		
	this.mouse_out_event = function(self, event) {
		this._mouse_is_in = false;
		$(self._container).unbind('mousemove');
		this._mouse_out_time = new Date().getTime();
	}
	
	this.mouse_move_event = function(self, event) {
		
		this._target.x = event.pageX;
		this._target.y = event.pageY;
		
		this._target.x -= this._canvas_pos.x;
		this._target.y -= this._canvas_pos.y;
		
	}
	
	// sets the corners of the canvas to transparent
	this.trim_corners = function(size, context) {
		h_size = size / 2;

		var positions = new Array(4);
		positions[0] = [h_size,					h_size];
		positions[1] = [this._width - h_size,	h_size];
		positions[2] = [this._width - h_size,	this._height - h_size];
		positions[3] = [h_size,					this._height - h_size];
		
		for(i = 0 ; i < 4 ; ++i) {
			context.save();
			context.translate(positions[i][0], positions[i][1]); // translated to the precalculated coordinates
			context.rotate(Math.PI * (i / 2)); // rotates each itteration by 90 degrees
			corner_piece(size, context);
			context.clip();
			context.clearRect(-h_size, -h_size, size, size);
			context.restore();
		}
	}
	
}

function rounded_box(x, y, width, height, radius, context) {
	var diameter = radius * 2;
		
	// starting position
	context.moveTo(x + radius, y);
		
	// top line
	context.lineTo(x + width - radius, y);
		
	// top right arc
	context.arc(x + width - radius, y + radius, radius, Math.PI * 1.5, Math.PI * 2, false);
		
	// right line
	context.lineTo(x + width, y + height - radius);
		
	// bottom right arc
	context.arc(x + width - radius, y + height - radius, radius, 0, Math.PI * 0.5, false);
		
	// bottom line
	context.lineTo(x + radius, y + height);
		
	// bottom left arc
	context.arc(x + radius, y + height - radius, radius, Math.PI * 0.5, Math.PI, false);
		
	// left line
	context.lineTo(x, y + radius);
		
	// top left arc
	context.arc(x + radius, y + radius, radius, Math.PI, Math.PI * 1.5, false);
		
	context.closePath();
}

function corner_piece(size, context) {
	h_size = size / 2;
	
	context.beginPath();
	
	// starting position
	context.moveTo(-h_size, -h_size);
	
	// line down
	context.lineTo(-h_size, h_size);
	
	// the arc
	context.arc(h_size, h_size, size, Math.PI, Math.PI * 1.5, false);
	
	context.closePath();
	
}


