function jsPlayer(callbacks) {
    var self = this;
    var swf;
    var eiAttempts = 0;
    
    this.songID = 0;
    this.songObject = null;
    this.callbacks = callbacks

    // ensure debug exists
    if(!window.debug) {
        window.debug = function(){
            if(window.console && window.console.log) {
                window.console.log(arguments);
            }
        };
    }
    
    this.init = function()
    {
        var playerUrl;
        var filename = 'JSPlayer.swf';
        if(location.host.match(/.svn|staging|dev/)) {
            // staging screwing up playback for ie/webkit
            //hostname = "staging.cowbell.grooveshark.com";
            //playerUrl = "http://staging.listen.grooveshark.com/" + filename;
            hostname = "cowbell.grooveshark.com";
            playerUrl = location.protocol + "//listen.grooveshark.com/" + filename;
        } else {
            hostname = "cowbell.grooveshark.com";
            playerUrl = location.protocol + "//listen.grooveshark.com/" + filename;
        }
        //If no baseURL is passed in, the SWF should grab it from the location bar
        var vars = {hostname: hostname};
        var params = {allowScriptAccess: "always"};
        var attributes = {id:'jsPlayerEmbed', name:'jsPlayerEmbed'}; // give an id to the flash object
        var swfWrapper = 'swfWrapper';  // where player object should go
        
        // html element with id="swfWrapper" MUST be included on page
        if(!document.getElementById('swfWrapper')) {
            $(document.body).append('<div id="swfWrapper"></div>');
        }
        swfobject.embedSWF(playerUrl, "swfWrapper", "1", "1", "9.0.0", null, vars, params, attributes);
        setTimeout(self.getFlash, 500);
    }

    this.setErrorCallback = function(functionName)
    {
        if (swf && swf.setErrorCallback) {
            swf.setErrorCallback(functionName);
        }
    }
    
    this.setStatusCallback = function(functionName)
    {
        if (swf && swf.setStatusCallback) {
            swf.setStatusCallback(functionName);
        }
    }
    
    this.setSongCompleteCallback = function(functionName)
    {
        if (swf && swf.setSongCompleteCallback) {
            swf.setSongCompleteCallback(functionName);
        }
    }
    
    this.getCallback = function(status)
    {
        //debug(status);
    }
    
    // these must be string references or they will not be set
    this.setCallbacks = function()
    {
        if(self.callbacks && self.callbacks.errorCallback) {
            self.setErrorCallback(self.callbacks.errorCallback);
        } else {
            self.setErrorCallback('player.playerError');
        }
        if(self.callbacks && self.callbacks.statusCallback) {
            self.setStatusCallback(self.callbacks.statusCallback);
        } else {
            self.setStatusCallback('player.playerStatus');
        }
        if(self.callbacks && self.callbacks.songCompleteCallback) {
            self.setSongCompleteCallback(self.callbacks.songCompleteCallback);
        } else {
            self.setSongCompleteCallback('player.playerSongComplete');
        }
    }
    
    this.playerError = function(msg)
    {
        $("#searchError").html(msg);
        if($('#searchError').is(':visible')) {
            // do nothing???
        } else {
            $('#searchError').fadeIn();
        }
    }
    
    this.playerStatus = function(msg)
    {
        debug('new player status: ', msg);
        switch(msg) {
            case 'loading':
                debug('IS LOADING');
                //this.setSongAsLoading('#sr-'+this.songID);
                //this.setSongAsLoading('#pl-'+this.songID);
                this.setSongAsLoading();
                this.setSongAsLoading();
                break;
            
            case 'playing':
                debug('IS PLAYING');
                //this.setSongAsPlaying('#sr-'+this.songID);
                //this.setSongAsPlaying('#pl-'+this.songID);
                this.setSongAsPlaying();
                this.setSongAsPlaying();
                break;
            case 'failed':  // return player back to normal
            default:
                debug('IS FAILED');
                //this.setSongAsPaused('#sr-'+this.songID);
                //this.setSongAsPaused('#pl-'+this.songID);
                this.setSongAsPaused();
                this.setSongAsPaused();
                break;
        }
    }
    
    this.playerSongComplete = function(msg)
    {
        debug('player song complete CALLBACK',msg);
        // this message was undefined
        // change object to reflect not playing
        //this.setSongAsPaused('#sr-'+this.songID);
        //this.setSongAsPaused('#pl-'+this.songID);
        this.setSongAsPaused();
        this.setSongAsPaused();
    }
    
    this.getPlayerFromObj = function(obj)
    {
        return (gs.util.isObject(obj)) ? obj : $(obj).find('a.player:first');
    }
    
    this.setSongAsPlaying = function(obj)
    {
        debug('setSongAsPlaying: play obj', obj, $(obj));
        obj = obj || this.songObject;
        obj = this.getPlayerFromObj(obj);
        debug('final obj', obj, $(obj));
        $(obj).addClass('pause').removeClass('play').removeClass('loading');
    }
    this.setSongAsLoading = function(obj)
    {
        debug('setSongAsLoading: loading obj', obj, $(obj));
        obj = obj || this.songObject;
        if(!obj) {
            debug('could not find object to load, return', obj);
            return;
        }
        debug('next obj, songObj', obj, this.songObject);
        obj = this.getPlayerFromObj(obj);
        debug('final obj', obj, $(obj));
        $(obj).addClass('loading').removeClass('pause').removeClass('play');
    }
    this.setSongAsPaused = function(obj)
    {
        debug('setSongAsPaused: pause obj', obj, $(obj));
        obj = obj || this.songObject;
        obj = this.getPlayerFromObj(obj);
        debug('final obj to set as paused: add "play", remove "pause,loading"', obj, $(obj));
        $(obj).addClass('play').removeClass('pause').removeClass('loading');
    }
    this.toggleSong = function(obj, songID)
    {
        var className = obj.className.replace('player','');
        debug('curr obj and songid: '+this.songID, this.songObject);
        debug('future obj and songid: '+songID, obj);
        if(className.match('play')) {
            // change to play song
            this.setSongAsPlaying(obj);
            if(this.songID == songID) {
                this.resumeStream();
            } else {
                // first time playing this song
                this.playSong(songID);
                if(this.songID) {
                    // make old songid look paused
                    //this.setSongAsPaused('#sr-'+this.songID);
                    //this.setSongAsPaused('#pl-'+this.songID);
                    this.setSongAsPaused();
                    this.setSongAsPaused();
                }
                // make new one loading
                this.setSongAsLoading(obj);
            }
        } else {
            // pause song
            this.setSongAsPaused(obj);
            this.pauseStream();
        }
        this.songID = songID;
        this.songObject = obj;
        $(obj).blur();
    }
    
    this.playSong = function(songID)
    {
        debug('player.playSong, songid:'+songID);
        if (swf && swf.playSong) {
            debug('good swf, play song');
            swf.playSong(songID);
        }
    }
    
    this.playFile = function(fileID)
    {
        if (swf && swf.playFile) {
            swf.playFile(fileID);
        }
    }
    
    this.pauseStream = function()
    {
        if (swf && swf.pauseStream) {
            swf.pauseStream();
        }
    }
    
    this.resumeStream = function()
    {
        if (swf && swf.resumeStream) {
            swf.resumeStream();
        }
    }
    
    this.stopStream = function()
    {
        if (swf && swf.stopStream) {
            swf.stopStream();
        }
    }
    
    this.getFlash = function()
    {
        if (eiAttempts < 10) {
            // prevent 'cyclic __proto__' errors 
                try {
                    var flash = null;
                    if (window.jsPlayerEmbed) {
                        flash = window.jsPlayerEmbed;
                    } else if(document.jsPlayerEmbed){
                        flash = document.jsPlayerEmbed;
                    }
                    flash = flash || document.getElementBy('jsPlayerEmbed');
                } catch(e) {
                    eiAttempts++;
                        setTimeout(self.getFlash, 500);
                }
                
                if (flash != null) {
                    swf = flash;
                setTimeout(self.setCallbacks, 1000);
                } else {
                    eiAttempts++;
                        setTimeout(self.getFlash, 500);
                }
        } else {
            debug("ExternalInterface failed to load after "+eiAttempts+" attempts.");
        }
    }
    
    this.init();
    return this;
}
