function player(id, playlist){
	
	// setup player
	this.id = id;
	this.videoId = null;
	this.playerState = null;
	this.playerVolume = null;
	this.playerPercentPlayed = null;
	this.playlist = playlist;
	this.ready = false;
	this.obj = document.getElementById(id);
	this.ytplayer = null;
	$(this.obj).empty();
	var placeholder = document.createElement('div');
	placeholder.id = id+'_player';
	this.obj.appendChild(placeholder);

	// embed the player
	swfobject.embedSWF("http://gdata.youtube.com/apiplayer?key=AI39si7_iVgupC7CsrMKwkOAKqm2TCPA_uFKGzhjGyCZgDXJepnB7Q-oWRCeqC9xcE6PcXHwcQR-46HXr-fQYgZ2izpnAzMBEw&enablejsapi=1&playerapiid="+id+"_player", 
		                       id+"_player", "238", "200", "8", null, null, { allowScriptAccess: "always" }, {"id":id+"_player"});		
};


player.prototype.onReady = function(){
	this.ytplayer = document.getElementById(this.id+"_player");		

	// setup control buttons and their click events
	$(this.obj).append('<div class="controls"><input type="button" value="Eject" class="player_eject" disabled="true"><input type="button" value="Recue" class="player_recue" disabled="true"><input type="button" value="Play"  class="player_play" disabled="true"><div><input type="button" value="««" class="player_skipbackwards halfwidth" disabled="true"><input type="button" value="»»"  class="player_skipforwards halfwidth" disabled="true"></div></div');
	$(".player_eject", this.obj).bind('click', this, function(e){
		e.data.ejectVideo();
	});
	$(".player_recue", this.obj).bind('click', this, function(e){
		e.data.recueVideo();
	});
	$(".player_play", this.obj).bind('click', this, function(e){
		if(this.value == 'Pause') e.data.pauseVideo();
		else e.data.playVideo();
	});
	$(".player_skipbackwards", this.obj).bind('click', this, function(e){
		e.data.skipBackwards();
	});
	$(".player_skipforwards", this.obj).bind('click', this, function(e){
		e.data.skipForwards();
	});

	// setup display readout which will be updated by updateInfo()
	$(this.obj).append('<div class="controls"><div>Time: <span class="player_elapsed"></span> / <span class="player_duration"></span></div><div>State: <span class="player_state"></span></div><div>Volume: <span class="player_volume"></span></div><div>Video ID: <span class="player_videoid"></span></div></div');

	this.ready = true;
	this.setVolume(100);
	this.updateInfo();
}

player.prototype.onStateChange = function(newstate){
	// fired every time the player's state is changed
	this.playerState = newstate;
	this.getVideoId();
	
	switch(this.playerState){
		
		case -1: // unstarted 
			$(".player_eject", this.obj).attr('disabled', true);
			$(".player_recue", this.obj).attr('disabled', true);
			$(".player_play" , this.obj).attr('disabled', true);
			$(".player_skipforwards", this.obj).attr('disabled', true);
			$(".player_skipbackwards", this.obj).attr('disabled', true);
			this.ready = true;
			break;
		case 0: // ended
			this.ejectVideo();
			break;
		case 1: // playing
			$(".player_play", this.obj).attr('value', 'Pause');
			this.playlist.itemPlaying(this.videoId);
			break;
		case 2: // paused
			$(".player_play", this.obj).attr('value', 'Play');
			this.playlist.itemCued(this.videoId);
			break;
		case 3: // buffering
			this.playlist.itemBuffering(this.videoId);
			break;
		case 5: // video cued
			this.playlist.itemCued(this.videoId);
			this.ready = false;
			$(".player_eject", this.obj).attr('disabled', false);
			$(".player_recue", this.obj).attr('disabled', false);
			$(".player_play" , this.obj).attr('disabled', false);
			$(".player_skipforwards", this.obj).attr('disabled', false);
			$(".player_skipbackwards", this.obj).attr('disabled', false);
		 	break;
		default:
			break;
		
	}
	
}

// action functions

	player.prototype.cueVideo = function(vid){
		// tell the playlist the last video was unloaded, not by an eject
		//  thus put its status back to pending
		this.playlist.itemPending(this.videoId);
		// load a video into the player and have it paused at the start
		this.ytplayer.cueVideoById(vid);
		$(".player_play", this.obj).attr('value', 'Play');
	}
	player.prototype.playVideo = function(){
		this.ytplayer.playVideo();
	}
	player.prototype.pauseVideo = function(){
		this.ytplayer.pauseVideo();
	}
	player.prototype.recueVideo = function(){
		// go back to the beginning and pause video
		this.ytplayer.pauseVideo();
		this.ytplayer.seekTo(0);
	}
	player.prototype.ejectVideo = function(){	
		// stop and clear the video
		this.ytplayer.stopVideo();
		this.ytplayer.clearVideo();
		this.playlist.itemPlayed(this.videoId);
		this.ready = true;
		$(".player_play", this.obj).attr('value', 'Play');
		// disable buttons
		$(".player_eject", this.obj).attr('disabled', true);
		$(".player_recue", this.obj).attr('disabled', true);
		$(".player_play", this.obj).attr('disabled', true);
		$(".player_skipforwards", this.obj).attr('disabled', true);
		$(".player_skipbackwards", this.obj).attr('disabled', true);
		
		// since we've just ejected something, lets try and load another video
		setTimeout(function(){
			if(dj_controller) dj_controller.loadPlayers();
		}, 1000);
		
	}
	player.prototype.setVolume = function(vol){
		//alert('setting vol: '+vol);
		if(this.ytplayer){
			this.ytplayer.setVolume(vol.toString());
		}
	}
	player.prototype.skipForwards = function(){
		if(this.ytplayer){
			var now = this.ytplayer.getCurrentTime();
			this.ytplayer.seekTo(now+10, true);
		}
	}
	player.prototype.skipBackwards = function(){
		if(this.ytplayer){
			var now = this.ytplayer.getCurrentTime();
			this.ytplayer.seekTo(now-10, true);
		}
	}

// accessor functions

	player.prototype.getDuration = function(){
		if(this.ytplayer){
			var dur = this.ytplayer.getDuration();
			if (dur<0) return '0';
			else return dur;
		} else return false;	
	}
	player.prototype.getElapsed = function(){
		if(this.ytplayer){
			var elapsed = this.ytplayer.getCurrentTime();
			if (elapsed<0) return '0';
			else return elapsed;
		} else return false;	
	}
	player.prototype.getVideoId = function(){
		if(this.ytplayer){
			var vidurl = this.ytplayer.getVideoUrl();
			var qstr = vidurl;
			// get the querystring part
			qstr = qstr.split("?");
			var qs = new Querystring(qstr[1]);
			this.videoId = qs.get('v');
			return this.videoId;
		}else return false
	}
	player.prototype.getPlayerState = function(){
		if(this.ytplayer){
			this.playerState = this.ytplayer.getPlayerState();
			return this.playerState;
		}
	}
	player.prototype.getVolume = function(){
		if(this.ytplayer){
			this.playerVolume = this.ytplayer.getVolume();
			return this.playerVolume;
		}
	}
	player.prototype.getPercentagePlayed = function(){
		if(this.ytplayer){	
			var pc = (this.getElapsed() / this.getDuration() ) * 100;
			if (!isNaN(pc)){
				this.playerPercentPlayed = pc;
				return this.playerPercentPlayed;
			}
		}
		return false;
	}

player.prototype.updateInfo = function(){

	if(this.getElapsed()) {p2cur = this.getElapsed(); $('#'+this.obj.id+' .player_elapsed').html(p2cur); }
	if(this.getDuration()) {p2dur = this.getDuration(); $('#'+this.obj.id+' .player_duration').html(p2dur); }
	if(this.getPlayerState()) $('#'+this.obj.id+' .player_state').html( this.getPlayerState() );
	if(this.getVolume()) $('#'+this.obj.id+' .player_volume').html( this.getVolume() );
	$('#'+this.obj.id+' .player_videoid').html( this.videoId ); 
	
	// function calls itself 5 times a second
	var self = this;
	setTimeout(function(){self.updateInfo();}, 200);
	
}