/*
 * Author: Will Gallego
 * YouCaption
 * Version: .7b
 * This is a work in progress and is not to be used on any other sites. Please don't steal any parts of the code either
 * I promise, I'll eventually open source this and will be happy to share it.
 */

var youCaption = {
/*
 *  Some variables to customize
 */
    height : 425,
    width : 356,
    progressDragging : false,
    volumeDragging : false,
    volumeTimeout : false,
    seekingTime : 0,
    authorColors : ['F00','00F','080','800','008','880','808','088','F80','F08','F88','88F','484','448','844','884'],

    // wrapper for any other instance of the onYouTubePlayerReady function that may be on the page
    ytReadyVar : onYouTubePlayerReady,

    players : {}, // Object that holds all the info on each YouTube player with comments, using the api id returned as the key
    
    createYTPlayer : function(videoId,comments){
        if(typeof(videoId) == 'undefined' || videoId == '') return;
        if(typeof(comments) != 'object') comments = {};
 
        var ytKey = videoId; // for now, just use the videoId. If need be, expand
        var params = { allowScriptAccess: "always" };
        var myYTPlayerId = 'myytplayer'+ytKey;
        var atts = { id: myYTPlayerId };
        var playerAPIId = 'ytplayer'+ytKey;
        var divPlayerId = 'divytplayer'+ytKey;
        var videoDurationId = 'videoDuration'+ytKey;
        var progressBarId = 'progressBar'+ytKey;
        var progressNodeId = 'progressNode'+ytKey;
        var progressBorderId = 'progressBorder'+ytKey;
        var commentsId = 'comments'+ytKey;
        var playpauseId = 'playpause'+ytKey;
        var volumeId = 'volume'+ytKey;
        var volumeSliderId = 'volumeSlider'+ytKey;
        var volumeTrackId = 'volumeTrack'+ytKey;
        var volumeNodeId = 'volumeNode'+ytKey;
        var controlsId = 'controls'+ytKey;
        var wrapperId = 'wrapper'+ytKey;
        var slicesId = 'slices'+ytKey;
        var currentTimeId = 'currentTime'+ytKey;
        var slashId = 'slash'+ytKey;
        var totalTimeId = 'totalTime'+ytKey;
        var clockId = 'clock'+ytKey;
    
        // Initializing variables
        youCaption.players[playerAPIId] = {
            'videoId' : videoId,
            'ytKey' : ytKey,
            'updatingComments' : false,
            'comments' : comments, // contains the actual comments inserted below the video
            'myYTPlayerId' : myYTPlayerId,
            'commentRow' : 'ytCommentEven',
            'authors' : [],
            'authorColorIndex' : 0
        };

// TODO, chain some of these together to reduce code?
        // creating divs to be replaced
        var playerDiv = document.createElement('div');
        playerDiv.setAttribute('id',divPlayerId);
        playerDiv.appendChild = document.createTextNode('You need Flash player 8+ and JavaScript enabled to view this video.');
    
        // Setting up div for comments
        var commentsDiv = document.createElement('div');
        commentsDiv.setAttribute('id',commentsId);
        commentsDiv.className = 'ytComments';

        // setting up control panel
        var controlsDiv = document.createElement('div');
        controlsDiv.setAttribute('id',controlsId);
        controlsDiv.className = 'ytControls';

        // Everything involved in progress bar
        // Play/Pause toggle button
        var playpauseElm = document.createElement('div');
        controlsDiv.appendChild(playpauseElm);
        playpauseElm.setAttribute('id',playpauseId);
        playpauseElm.className = 'ytPause';
        playpauseElm.onmouseover = function(){
          var re = /ytPlay/;
          hoverClass = (this.className.match(re)) ? 'ytPlayHover' : 'ytPauseHover';
          youCaption.elementChangeHover(this,hoverClass,true);
        };
        playpauseElm.onmouseout = function(){
          var re = /ytPlay/;
          hoverClass = (this.className.match(re)) ? 'ytPlayHover' : 'ytPauseHover';
          youCaption.elementChangeHover(this,hoverClass,false);
        };

        // Duration div, wrapper for all progress bar elements
        var durationDiv = document.createElement('div');
        durationDiv.setAttribute('id',videoDurationId);
        durationDiv.className = 'ytDuration';
        durationDiv.onmousedown = function(event){youCaption.onProgressMouseDown(event,playerAPIId); return false;};
        // outer border for progress bar
        var progressBorder = document.createElement('div');
        progressBorder.setAttribute('id',progressBorderId)
        progressBorder.className = 'ytProgressBorder';
        // slices - divisions in timeline where comments appear
        var slices = document.createElement('div');
        slices.setAttribute('id',slicesId)
        slices.className = 'ytSlices';
        progressBorder.appendChild(slices);
        // Progress bar, visual representation of progress through video
        var progressBar = document.createElement('div');
        progressBar.setAttribute('id',progressBarId)
        progressBar.className = 'ytProgressBar';
	progressBar.appendChild(document.createElement('span')); // IE hack, fills in div, otherwise height is thrown off
        progressBorder.appendChild(progressBar);
        // Progress Node, clickable/draggable node through timeline
        var progressNode = document.createElement('div');
        progressNode.setAttribute('id',progressNodeId)
        progressNode.className = 'ytProgressNode';
	progressNode.appendChild(document.createElement('span')); // Same IE hack here
        progressNode.onmouseover = function(){this.className = 'ytProgressNodeHover'};
        progressNode.onmouseout = function(){this.className = 'ytProgressNode'};
        progressBorder.appendChild(progressNode);
        durationDiv.appendChild(progressBorder);
        controlsDiv.appendChild(durationDiv);

        // Clock div, incorporates all elements that are part of the clock
	var clockDiv = document.createElement('div');
	clockDiv.setAttribute('id',clockId);
	clockDiv.className = 'ytClock';
        // Current time, numeric representation of how far through video user is
	var currentTimeDiv = document.createElement('div');
	currentTimeDiv.setAttribute('id',currentTimeId);
	currentTimeDiv.className = 'ytCurrentTime';
	currentTimeDiv.appendChild(document.createTextNode('0:00'));
        clockDiv.appendChild(currentTimeDiv);
        // slash, just a divider between current and total time
        var slash = document.createElement('div');
        slash.appendChild(document.createTextNode('/'));
        slash.setAttribute('id',slashId);
        slash.className = 'ytSlash';
        clockDiv.appendChild(slash);
        // total time of video playing
	var totalTimeDiv = document.createElement('div');
	totalTimeDiv.setAttribute('id',totalTimeId);
	totalTimeDiv.className = 'ytTotalTime';
	totalTimeDiv.appendChild(document.createTextNode('0:00'));
        clockDiv.appendChild(totalTimeDiv);
        controlsDiv.appendChild(clockDiv);

        // Volume Controls
        // Volume toggle button
        var volumeElm = document.createElement('div');
        volumeElm.setAttribute('id',volumeId);
        volumeElm.className = 'ytVolume';
        volumeElm.onmouseover = function(event) {
          youCaption.onVolumeMouseOver(event,playerAPIId);
//          var ytPlayer = document.getElementById(youCaption.players[playerAPIId]['myYTPlayerId']);
          var ytPlayer = youCaption.getPlayer(playerAPIId);
          var hoverClass = (ytPlayer.isMuted()) ? 'ytVolumeMuteHover' : 'ytVolumeHover';
          youCaption.elementChangeHover(this,hoverClass,true);
        };
        volumeElm.onmouseout = function(event) {
          youCaption.onVolumeMouseOut(event,playerAPIId);
//          var ytPlayer = document.getElementById(youCaption.players[playerAPIId]['myYTPlayerId']);
          var ytPlayer = youCaption.getPlayer(playerAPIId);
          var hoverClass = (ytPlayer.isMuted()) ? 'ytVolumeMuteHover' : 'ytVolumeHover';
          youCaption.elementChangeHover(this,hoverClass,false);
        };
        volumeElm.onmouseup = function(event) {youCaption.toggleVolume(event,playerAPIId); return false;};
        // Volume slider
        var volumeSlider = document.createElement('div');
        volumeSlider.setAttribute('id',volumeSliderId);
        volumeSlider.className = 'ytVolumeSlider';
        volumeSlider.onmousedown = function(event) {youCaption.onVolumeMouseDown(event,playerAPIId);}
        volumeSlider.onmouseup = function(event) {youCaption.onVolumeMouseUp(event,playerAPIId,true);}
        // Volume track
        var volumeTrack = document.createElement('div');
        volumeTrack.setAttribute('id',volumeTrackId);
        volumeTrack.className = 'ytVolumeTrack';
        // Volume node
        var volumeNode = document.createElement('div');
        volumeNode.setAttribute('id',volumeNodeId);
        volumeNode.className = 'ytVolumeNode';
        volumeNode.appendChild(document.createElement('span')); // IE hack
        // Attach node to slider, slider and toggle button to controls
        volumeSlider.appendChild(volumeNode);
        volumeSlider.appendChild(volumeTrack);
        volumeElm.appendChild(volumeSlider);
        controlsDiv.appendChild(volumeElm);

        // wrapper to hold all dom elements
        var wrapperDiv = document.getElementById(wrapperId);
        if(!wrapperDiv){
            document.write('<div id="'+wrapperId+'" class="ytWrapper"></div>');
            wrapperDiv = document.getElementById(wrapperId);
        }

        // onmousemove and onmouseout for disabling progressDragging elements with their respective calls
        wrapperDiv.onmousemove = function(event) {youCaption.onProgressMouseMove(event,playerAPIId); youCaption.onVolumeMouseMove(event,playerAPIId);};
        wrapperDiv.onmouseout = function (event) {youCaption.onWrapperMouseOut(event,playerAPIId);};
        wrapperDiv.onmouseup = function(event) {youCaption.onProgressMouseUp(event,playerAPIId); youCaption.onVolumeMouseUp(event,playerAPIId,false);};

        wrapperDiv.appendChild(playerDiv);
        wrapperDiv.appendChild(controlsDiv);
        wrapperDiv.appendChild(commentsDiv);

        swfobject.embedSWF("http://www.youtube.com/apiplayer?enablejsapi=1&rel=0&playerapiid="+playerAPIId, 
                           divPlayerId, youCaption.height, youCaption.width, "8", null, null, params, atts);
    },

    /*
     * Switches back and forth between playing and pausing the video
     * @param playerAPIId - id used as a key
     */
    togglePlaypause : function(playerAPIId){
//        var ytPlayer = document.getElementById(youCaption.players[playerAPIId]['myYTPlayerId']);
        var ytPlayer = youCaption.getPlayer(playerAPIId);
        if(ytPlayer){
            var state = ytPlayer.getPlayerState();
            if(state == 0 || state == 2 || state == 5){
                youCaption.playVideo(playerAPIId);
            } else {
                youCaption.pauseVideo(playerAPIId);
            }
        }
    },

    /*
     *
     */
    setPlaypauseClass : function(playerAPIId,isPlaying){
//        var playPause = document.getElementById('playpause'+youCaption.players[playerAPIId]['ytKey']);
        var playPause = youCaption.getEl(playerAPIId,'playpause');
        var classes = playPause.className.split(' ');
        var newClasses = [];

        for(var i in classes){
            if(classes[i] != 'ytPlay' && classes[i] != 'ytPause'
               && classes[i] != 'ytPlayHover' && classes[i] != 'ytPauseHover'){ // add any classes other than play
                newClasses.push(classes[i]);
            }
        }

        // if we need to add a play, add play and start playing the video. otherwise set class to pause and pause it
        newClasses.push(isPlaying ? 'ytPlay' : 'ytPause');

        // append all classes to the object
        playPause.className = newClasses.join(' ');
    },

    /*
     *
     */
    playVideo : function(playerAPIId){
//        var ytPlayer = document.getElementById(youCaption.players[playerAPIId]['myYTPlayerId']);
        var ytPlayer = youCaption.getPlayer(playerAPIId);
        if (ytPlayer) {
            ytPlayer.playVideo();
        }
    },

    /*
     *
     */
    pauseVideo : function(playerAPIId){
//        var ytPlayer = document.getElementById(youCaption.players[playerAPIId]['myYTPlayerId']);
        var ytPlayer = youCaption.getPlayer(playerAPIId);
        if (ytPlayer) {
            ytPlayer.pauseVideo();
            // Clear the interval
            if(typeof(youCaption.players[playerAPIId]['interval']) != 'undefined'){
                clearInterval(youCaption.players[playerAPIId]['interval']);
            }
        }
    },

    /*
     *
     */
    cueVideo : function(playerAPIId, videoId, startSeconds) {
//        var ytPlayer = document.getElementById(youCaption.players[playerAPIId]['myYTPlayerId']);
        var ytPlayer = youCaption.getPlayer(playerAPIId);
        if (ytPlayer) {
            if(isNaN(startSeconds) || startSeconds < 0) startSeconds = 0;
            ytPlayer.cueVideoById(videoId, startSeconds);
        }
    },

    /*
     *
     */
    onYTPlayerStateChange : function(playerAPIId,state){
        switch(state){
            case -1: // "Unstarted"
                break;
            case 0: // Ended
                break;
            case 1: // Playing
                youCaption.setPlaypauseClass(playerAPIId,true);
                // Have to wait until the video has loaded before creating, otherwise duration is thrown off
                if(!youCaption.players[playerAPIId]["firstLoaded"] && youCaption.getDuration(playerAPIId) > 0){
                    youCaption.players[playerAPIId]["firstLoaded"] = true;
                    youCaption.createTimeline(playerAPIId);
                }

                youCaption.updateYTPlayerInfo(playerAPIId);
                clearInterval(youCaption.players[playerAPIId]['interval']);
                youCaption.players[playerAPIId]['interval'] = setInterval('youCaption.updateYTPlayerInfo("'+playerAPIId+'");', 250);
                break;
            case 2: // Paused
                youCaption.setPlaypauseClass(playerAPIId,false);
                break;
            case 3: // Buffering
                break;
            case 5: // Video Cued
//                var playPause = document.getElementById('playpause'+youCaption.players[playerAPIId]['ytKey']);
                var playPause = youCaption.getEl(playerAPIId,'playpause');
                playPause.onclick = function(){youCaption.togglePlaypause(playerAPIId); this.blur(); return false;}
                break;
        }
    },

/*
 * Functions for dragging progress bar
 */
    onProgressMouseDown : function(evt,playerAPIId){
        if (!evt) evt = window.event; // grab the event if evt undefined
        // disable right clicks on duration bar
        // TODO, combine these two checks into one function
        if (navigator.appName == 'Netscape' && ( evt.which == 2 || evt.which == 3)){
            return;
        }else if (navigator.appName == 'Microsoft Internet Explorer' && (evt.button == 2 || evt.button == 3)){
            evt.returnValue=false;
            return;
        }

        // Prevents firefox from dragging divs around
// TODO, still selecting text though...need to figure that one out...
//        var durationDiv = document.getElementById('videoDuration'+youCaption.players[playerAPIId]['ytKey']);
        var durationDiv = youCaption.getEl(playerAPIId,'videoDuration');
        durationDiv.focus();

// TODO, if I'm only having one "progressDragging", what if I play multiple videos at once? will they all drag? I need to investigate this
        youCaption.progressDragging = playerAPIId;
        youCaption.startProgressDrag(evt,playerAPIId);
    },

    /*
     *
     */
    onProgressMouseUp : function(evt,playerAPIId){
        youCaption.stopProgressDrag(evt,playerAPIId);
    },

    /*
     *
     */
    onProgressMouseMove : function(evt){
        var playerAPIId = youCaption.progressDragging;
        if(playerAPIId !== false && typeof(youCaption.players[playerAPIId]) != 'undefined'){
            youCaption.startProgressDrag(evt,playerAPIId);
        }
    },
 
    /*
     *
     */
    onWrapperMouseOut : function(evt,playerAPIId){
        if(youCaption.progressDragging !== false || youCaption.volumeDragging !== false){
            if (!evt) evt = window.event; // grab the event if evt undefined

            // focusing on wrapper out here
//            var wrapperDiv = document.getElementById('wrapper'+youCaption.players[playerAPIId]['ytKey']);
            var wrapperDiv = youCaption.getEl(playerAPIId,'wrapper');

            // go through parents of related target (where we're moving to)
            // If one of the ancestors is the wrapper, then we're not moving outside, but around inside the wrapper, so return
            var reltg = (evt.relatedTarget) ? evt.relatedTarget : evt.toElement;
            while (reltg && reltg != wrapperDiv && reltg.nodeName != 'BODY'){
                reltg= reltg.parentNode;
            }
            if (reltg == wrapperDiv) return;

            // Still here? then we moved somewhere outside the wrapper, so stop progressDragging/volumeDragging
            if(youCaption.progressDragging !== false){
                youCaption.stopProgressDrag(evt,playerAPIId);
            } else {
                youCaption.stopVolumeDrag(evt,playerAPIId);
                youCaption.volumeTimeout = setTimeout("youCaption.hideVolumeSlider('"+playerAPIId+"')",1000);
            }
        } 
    },

/*
 * Functions for dragging volume
 */
    /*
     *
     */
    onVolumeMouseOver : function(evt,playerAPIId){
        if(youCaption.volumeTimeout !== false){
            clearTimeout(youCaption.volumeTimeout);
            youCaption.volumeTimeout = false;
        }

        if (!evt) evt = window.event; // grab the event if evt undefined

//        var volumeSlider = document.getElementById('volumeSlider'+youCaption.players[playerAPIId]['ytKey']);
        var volumeSlider = youCaption.getEl(playerAPIId,'volumeSlider');
        youCaption.show(volumeSlider);
    },

    /*
     *
     */
    onVolumeMouseDown : function(evt,playerAPIId){
        if (!evt) evt = window.event; // grab the event if evt undefined
        // disable right clicks on duration bar
        if (navigator.appName == 'Netscape' && ( evt.which == 2 || evt.which == 3)){
            return;
        }else if (navigator.appName == 'Microsoft Internet Explorer' && (evt.button == 2 || evt.button == 3)){
            evt.returnValue=false;
            return;
        }
// TODO, if I'm only having one "progressDragging", what if I play multiple videos at once? will they all drag? I need to investigate this
        youCaption.volumeDragging = playerAPIId;
        youCaption.startVolumeDrag(evt,playerAPIId);
    },

    /*
     *
     */
    onVolumeMouseUp : function(evt,playerAPIId,overVolumeSlider){
        youCaption.stopVolumeDrag(evt,playerAPIId);

        // Finds where mouse is over, climbs up DOM tree until hits body, undef or volume slider. If it's the volume slider, don't close
        if (!evt) evt = window.event; // grab the event if evt undefined
//        var volumeSlider = document.getElementById('volumeSlider'+youCaption.players[playerAPIId]['ytKey']);
        var volumeSlider = youCaption.getEl(playerAPIId,'volumeSlider');
        var target = evt.target;
        while (target && target != volumeSlider && target.nodeName != 'BODY' && target != volumeSlider.parentNode){
            target= target.parentNode;
        }

        evt.cancelBubble = true;

        if (target == volumeSlider || target == volumeSlider.parentNode) return;

        youCaption.volumeTimeout = setTimeout("youCaption.hideVolumeSlider('"+playerAPIId+"')",1000);
        
    },

    /*
     *
     */
    onVolumeMouseOut : function(evt,playerAPIId){
        if (!evt) evt = window.event; // grab the event if evt undefined
//        var volumeSlider = document.getElementById('volumeSlider'+youCaption.players[playerAPIId]['ytKey']);
        var volumeSlider = youCaption.getEl(playerAPIId,'volumeSlider');
        // go through parents of related target (where we're moving to)
        // If one of the ancestors is the wrapper, then we're not moving outside, but around inside the wrapper, so return
        var reltg = (evt.relatedTarget) ? evt.relatedTarget : evt.toElement;
        while (typeof(reltg) != 'undefined' && reltg != volumeSlider && reltg.nodeName != 'BODY'){
            reltg= reltg.parentNode;
        }
        if (reltg== volumeSlider) return;

        if(youCaption.volumeDragging === false){
            youCaption.volumeTimeout = setTimeout("youCaption.hideVolumeSlider('"+playerAPIId+"')",1000);
        }
    },

    /*
     *
     */
    onVolumeMouseMove : function(evt){
        var playerAPIId = youCaption.volumeDragging;
        if(playerAPIId !== false && typeof(youCaption.players[playerAPIId]) != 'undefined'){
            youCaption.startVolumeDrag(evt,playerAPIId);
        }
    },
 
    /*
     *
     */
    hide : function(obj){
        obj.style.visibility = 'hidden';
    },

    /*
     *
     */
    show : function(obj){
        obj.style.visibility = 'visible';
    },

    elementChangeHover : function(el,hoverClass,isHovering){
        var classes = el.className.split(' ');
        var newClasses = [];

        // strip out volume classes
        for(var i in classes){
            if(classes[i] != hoverClass){
                newClasses.push(classes[i]);
            }
        }

        if(isHovering) newClasses.push(hoverClass);

        // append all classes to the element
        el.className = newClasses.join(' ');
    },

    /*
     *
     */
    startProgressDrag : function(evt,playerAPIId){
        // interval is set when playing video, need to turn it off
        clearInterval(youCaption.players[playerAPIId]['interval']);

        // Take the  mouse position, subtract duration div's position from it, so you get how far from the start
        // of the div the mouse click was
        // divide that distance by the max width of the div and you have the approx percentage of how far through
        // the video the user wishes to jump
//        var progressBorder = document.getElementById('progressBorder'+youCaption.players[playerAPIId]['ytKey']);
        var progressBorder = youCaption.getEl(playerAPIId,'progressBorder');

        // in the returned arrays, 0 = X, 1 = Y coordinates
        var durationPos = youCaption.findPos(progressBorder);
        var mousePos = youCaption.findMousePos(evt);
        var scrollLeft = (document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft);

        // Now we know where the click is. Figure out the scale and from the scale, use the total time to figure out the time
        var durationWidth = progressBorder.offsetWidth;
        if(durationWidth <= 0) durationWidth = 1;

        var diff = mousePos[0] + scrollLeft - durationPos[0];

        // figure out the percentage across the duration div that the user has clicked
        // can't be exactly 0, otherwise left is set to 0 and the bar jumps back to start position while dragging
        var scale = (diff / durationWidth);
        if(isNaN(scale) || scale <= 0) {scale = 0.001;}else if(scale > 1){ scale = 1;}

        // take that percentage and mutiply it with the total duration of the video to get the current time to jump to
        var duration = youCaption.getDuration(playerAPIId);
        // If duration < 0, play hasn't started yet. Started the video, check the duration again
        if(duration < 0) {
            youCaption.playVideo(playerAPIId);
            duration = youCaption.getDuration(playerAPIId);
        }

        var time = duration * scale;

        // We now know the time to move to, so update progress bar and current time. On mouse up, jump to that time
        youCaption.seekingTime = time;
        youCaption.updateProgressBar(playerAPIId,time);
        youCaption.updateCurrentTime(playerAPIId,time);
    },

    /*
     *
     */
    stopProgressDrag : function(evt,playerAPIId){
        if(youCaption.progressDragging !== false){
            youCaption.progressDragging = false;
            youCaption.jumpTo(playerAPIId,youCaption.seekingTime);
        }
    },

    hideVolumeSlider : function(playerAPIId){
        var volumeSlider = youCaption.getEl(playerAPIId,'volumeSlider');
        youCaption.hide(volumeSlider);
        clearTimeout(youCaption.volumeTimeout);
        youCaption.volumeTimeout = false;
    },

    startVolumeDrag : function(evt,playerAPIId){
        // Take the  mouse position, subtract volume track div's position from it, so you get how far from the start
        // of the div the mouse click was
        // divide that distance by the max width of the div and you have the approx percentage of the volume to set
//        var volumeTrackDiv = document.getElementById('volumeTrack'+youCaption.players[playerAPIId]['ytKey']);
        var volumeTrackDiv = youCaption.getEl(playerAPIId,'volumeTrack');

        // Prevents firefox from dragging divs around
// TODO, still selecting text though...need to figure that one out...
        volumeTrackDiv.focus();

        // in the returned arrays, 0 = X, 1 = Y coordinates
        var volumeTrackPos = youCaption.findPos(volumeTrackDiv);
        var mousePos = youCaption.findMousePos(evt);
        var scrollTop = (document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop);
  
        // Now we know where the click is. Figure out the scale (take off height of volume node)
        var volumeTrackHeight = volumeTrackDiv.offsetHeight;
        if(volumeTrackHeight <= 0) volumeTrackHeight = 1;

        var diff = mousePos[1] + document.documentElement.scrollTop - volumeTrackPos[1];
        if(diff < 0) {diff = 0;}else if(diff > volumeTrackHeight){ diff = volumeTrackHeight-1;}

        // figure out the percentage down the volumeTrack div that the user has clicked
        var scale = Math.floor((diff / volumeTrackHeight)*100);
        if(isNaN(scale) || scale < 0) {scale = 0;}else if(scale > 100){ scale = 100;}

        // We now know the time to move to, so move the volume slider node
        youCaption.changeVolume(playerAPIId,scale);
    },

    /*
     *
     */
    stopVolumeDrag : function(evt,playerAPIId){
        if(youCaption.volumeDragging !== false){
            youCaption.volumeDragging = false;
        }
    },

    /* Gets the absolute position of an element on a page
     * @param obj - the DOM element to find the absolute x and y coordinates
     */
    findPos : function(obj) {
	var curleft = curtop = 0;
        if (obj.offsetParent) {
            do {
                curleft += obj.offsetLeft;
                curtop += obj.offsetTop;
            } while (obj = obj.offsetParent);
        }
        return [curleft,curtop];
    },

    /* Finds the mouse x and y coordinates
     * @param evt - a mouse event to track the mouse's x and y coordinates from
     */
    findMousePos : function(evt){
        var e = evt ? evt : window.event;
        return [e.clientX,e.clientY];
    },
 
    /*
     *
     */
    getCurrentTime : function(playerAPIId){
//        var ytPlayer = document.getElementById(youCaption.players[playerAPIId]['myYTPlayerId']);
        var ytPlayer = youCaption.getPlayer(playerAPIId);
        if(typeof(ytPlayer) != 'undefined'){
            return ytPlayer.getCurrentTime();
        }
    },

    /*
     *
     */
    getDuration : function(playerAPIId){
//        var ytPlayer = document.getElementById(youCaption.players[playerAPIId]['myYTPlayerId']);
        var ytPlayer = youCaption.getPlayer(playerAPIId);
        if(typeof(ytPlayer) != 'undefined'){
            return ytPlayer.getDuration();
        }
    },
    
    /*
     *
     */
    updateYTPlayerInfo : function(playerAPIId){
        youCaption.updateProgressBar(playerAPIId);
        youCaption.updateCurrentTime(playerAPIId);
        youCaption.updateComments(playerAPIId);
    },

    /*
     *
     */
    updateProgressBar : function(playerAPIId,currentTime){
        if(!currentTime || isNaN(currentTime) || currentTime < 0) currentTime = youCaption.getCurrentTime(playerAPIId);
        var duration = youCaption.getDuration(playerAPIId);
        var scale = (duration <= 0) ? 0 : (currentTime / duration);
//        var progressBorder = document.getElementById('progressBorder'+youCaption.players[playerAPIId]['ytKey']);
        var progressBorder = youCaption.getEl(playerAPIId,'progressBorder');
        var durationWidth = progressBorder.offsetWidth;

        if(!scale || scale < 0) {
            scale = 0;
        } else if(scale > 1){
            scale = 1;
        }
//        var progressBar = document.getElementById('progressBar'+youCaption.players[playerAPIId]['ytKey']);
        var progressBar = youCaption.getEl(playerAPIId,'progressBar');
//        var progressNode = document.getElementById('progressNode'+youCaption.players[playerAPIId]['ytKey']);
        var progressNode = youCaption.getEl(playerAPIId,'progressNode');
        var progressBarWidth = (durationWidth*scale > durationWidth-4) ? durationWidth-4: durationWidth*scale;
        progressBar.style.width = progressBarWidth+'px';
        progressNode.style.left = Math.ceil(durationWidth*scale) - (progressNode.offsetWidth/2)+'px';
    },

    /*
     *
     */
    updateCurrentTime : function(playerAPIId, currentTime){
        if(!currentTime || isNaN(currentTime) || currentTime < 0) currentTime = youCaption.getCurrentTime(playerAPIId);
//        var ytPlayer = document.getElementById(youCaption.players[playerAPIId]['myYTPlayerId']);
        var ytPlayer = youCaption.getPlayer(playerAPIId);
        if(typeof(ytPlayer) != 'undefined'){
            // set current time
//            var currentTimeDiv = document.getElementById('currentTime'+youCaption.players[playerAPIId]['ytKey']);
            var currentTimeDiv = youCaption.getEl(playerAPIId,'currentTime');
            currentTimeDiv.innerHTML = youCaption.formatTime(currentTime);
        }
    },

    /*
     *
     */
    createTimeline : function(playerAPIId){

        var comments = youCaption.players[playerAPIId]['comments'];
//        var slices = document.getElementById('slices'+youCaption.players[playerAPIId]['ytKey']);
        var slices = youCaption.getEl(playerAPIId,'slices');

        var duration = youCaption.getDuration(playerAPIId);
//        var progressBorder = document.getElementById('progressBorder'+youCaption.players[playerAPIId]['ytKey']);
        var progressBorder = youCaption.getEl(playerAPIId,'progressBorder');
        var durationWidth = progressBorder.offsetWidth;

        for(var time in comments){
            var scale = (duration <= 0) ? 0 : (time / duration);
            if(isNaN(scale) || scale < 0) {
                scale = 0;
            } else if(scale > 1){
                scale = 1;
            }

            var slice = document.createElement('div');
            slice.className = 'ytSlice';

            // percent through the video times width of the duration div
            var progressBarWidth = (durationWidth*scale > durationWidth-4) ? durationWidth-4: durationWidth*scale;
            slice.style.left = Math.ceil(progressBarWidth)+'px';
            slices.appendChild(slice);
        }

        // set total time
//        totalTime = document.getElementById('totalTime'+youCaption.players[playerAPIId]['ytKey']);
        var totalTime = youCaption.getEl(playerAPIId,'totalTime');
        totalTime.innerHTML = youCaption.formatTime(duration);
    },

    /*
     *
     */
    formatTime : function(secs){
        var intSecs = parseInt(secs);
        if(isNaN(intSecs) || intSecs < 0) intSecs = 0;
        var stringSecs = String(intSecs%60 < 10 ? '0'+intSecs%60 : intSecs%60);
        return Math.floor(intSecs/60)+':'+(stringSecs);
    },


    /*
     *
     */
    // time is integer seconds to jump to, playerAPIId is obvious, content must be a DOM element
    createSeekLink : function(time,playerAPIId,content){
        if(isNaN(time) || time < 0 || typeof(youCaption.players[playerAPIId]) == 'undefined') return '';

        var newAnchor = document.createElement('a');
        newAnchor.setAttribute('href','#');
        newAnchor.className = 'ytCommentTime';

        newAnchor.onclick = function(){youCaption.jumpTo(playerAPIId,time); return false;}
        newAnchor.appendChild(content);

        return newAnchor;
    },

    /*
     *
     */
    jumpTo : function(playerAPIId,time){
//        var ytPlayer = document.getElementById(youCaption.players[playerAPIId]['myYTPlayerId']);
        var ytPlayer = youCaption.getPlayer(playerAPIId);
        if(ytPlayer && !isNaN(time) && time >= 0){
            ytPlayer.seekTo(time,true);
        }

        // Prevents progress bar from jumping around while seeking and updates when clicking seek links
        var state = ytPlayer.getPlayerState();
        if(state == 1){
          clearInterval(youCaption.players[playerAPIId]['interval']);
          youCaption.players[playerAPIId]['interval'] = setInterval('youCaption.updateYTPlayerInfo("'+playerAPIId+'");', 250);
        }
    },

    /*
     *
     */
    updateVolumeBar : function(playerAPIId,vol){
//        var ytPlayer = document.getElementById(youCaption.players[playerAPIId]['myYTPlayerId']);
        var ytPlayer = youCaption.getPlayer(playerAPIId);
        if(ytPlayer){
            if(!vol || isNaN(vol)) vol = ytPlayer.getVolume();
            if(isNaN(vol) || ytPlayer.isMuted()) {vol = 0;}
            var scale = vol / 100;

//            var volumeTrackDiv = document.getElementById('volumeTrack'+youCaption.players[playerAPIId]['ytKey']);
            var volumeTrackDiv = youCaption.getEl(playerAPIId,'volumeTrack');
//            var volumeNode = document.getElementById('volumeNode'+youCaption.players[playerAPIId]['ytKey']);
            var volumeNode = youCaption.getEl(playerAPIId,'volumeNode');
            var volumeNodeHeight = volumeNode.offsetHeight;

            var volumeTrackHeight = volumeTrackDiv.offsetHeight - volumeNodeHeight;
            if(volumeTrackHeight <= 0) volumeTrackHeight = 1;

            var volTop = Math.ceil(scale * volumeTrackHeight);

            // the top to set the volume is scale * height of the volume track
//            var volumeNode = document.getElementById('volumeNode'+youCaption.players[playerAPIId]['ytKey']);
            var volumeNode = youCaption.getEl(playerAPIId,'volumeNode');
            volumeNode.style.top = Math.ceil(scale*volumeTrackHeight)+'px';
        }
    },

    /*
     *
     */
    changeVolume : function(playerAPIId,vol){
//        var ytPlayer = document.getElementById(youCaption.players[playerAPIId]['myYTPlayerId']);
        var ytPlayer = youCaption.getPlayer(playerAPIId);
        if(ytPlayer){
            // Making legit
            if(isNaN(vol)||vol < 0){vol=0;}else if(vol>100){vol=100;}
            ytPlayer.setVolume(Math.floor(vol));
            youCaption.updateVolumeBar(playerAPIId,vol);
            if(vol == 0){
                youCaption.setVolumeClass(playerAPIId,true);
            } else {
                ytPlayer.unMute(playerAPIId);
                youCaption.setVolumeClass(playerAPIId,false);
            }
        }
    },

    /*
     *
     */
    toggleVolume : function(evt,playerAPIId){
//        var ytPlayer = document.getElementById(youCaption.players[playerAPIId]['myYTPlayerId']);
        var ytPlayer = youCaption.getPlayer(playerAPIId);
        if (!evt) evt = window.event; // grab the event if evt undefined
//        var volume = document.getElementById('volume'+youCaption.players[playerAPIId]['ytKey']);
        var volume = youCaption.getEl(playerAPIId,'volume');
        if(ytPlayer.isMuted()){
            // if volume was set to 0 then muted, don't change the class, because you still won't hear anything
            var vol = ytPlayer.getVolume();
            if(isNaN(vol) || vol == 0) return;

            ytPlayer.unMute(playerAPIId);
            youCaption.setVolumeClass(playerAPIId,false);
            // no need to pass in volume here, should grab it from the player
            youCaption.updateVolumeBar(playerAPIId);
            var hoverClass = 'ytVolumeHover';
            youCaption.elementChangeHover(volume,hoverClass,true);
        } else {
            ytPlayer.mute(playerAPIId);
            youCaption.setVolumeClass(playerAPIId,true);
            youCaption.updateVolumeBar(playerAPIId,0);
            var hoverClass = 'ytVolumeMuteHover';
            youCaption.elementChangeHover(volume,hoverClass,true);
        }
    },

    /*
     *
     */
    setVolumeClass : function(playerAPIId, mute){
//        var volume = document.getElementById('volume'+youCaption.players[playerAPIId]['ytKey']);
        var volume = youCaption.getEl(playerAPIId,'volume');
        var classes = volume.className.split(' ');
        var newClasses = [];

        // strip out volume classes
        for(var i in classes){
            if(classes[i] != 'ytVolume' && classes[i] != 'ytVolumeMute'
               && classes[i] != 'ytVolumeHover' && classes[i] != 'ytVolumeMuteHover'){
                newClasses.push(classes[i]);
            }
        }

       newClasses.push((mute) ? 'ytVolumeMute': 'ytVolume');

        // append all classes to the object
        volume.className = newClasses.join(' ');
    },

    getAuthorColor : function(playerAPIId){
      // shift color off the top of the array, add to the end
      var color = '#'+youCaption.authorColors[youCaption.players[playerAPIId]['authorColorIndex']];
      youCaption.players[playerAPIId]['authorColorIndex'] = (youCaption.players[playerAPIId]['authorColorIndex'] + 1) % youCaption.authorColors.length;
      return color;
    },
    
    /*
     *
     */
    updateComments : function(playerAPIId){
        if(youCaption.players[playerAPIId]['updatingComments']) return; // if already cycling through updating comments, don't try to update over it
    
        var currentTime = youCaption.getCurrentTime(playerAPIId);
        youCaption.players[playerAPIId]['updatingComments'] = true;
        var comments = youCaption.players[playerAPIId]['comments'];
    
        for(var time in comments){
            if(currentTime > time && typeof(comments[time]['set']) == 'undefined'){
//                var commentsDiv = document.getElementById('comments'+youCaption.players[playerAPIId]['ytKey']);
                var commentsDiv = youCaption.getEl(playerAPIId,'comments');
                var author;
                var comment;
   
                // needs some cleanup
                for(var i in comments[time]){
                    var textNode = document.createTextNode('('+youCaption.formatTime(time)+')');
                    var seekLink = youCaption.createSeekLink(time,playerAPIId,textNode);
                    author = comments[time][i][0];
                    comment = comments[time][i][1];

                    var authorSpan = document.createElement('span');
                    authorSpan.className = 'ytAuthor';
                    authorSpan.appendChild(document.createTextNode(author+':'));

                    // Encapsulates the time with link and the author in colored text
                    var commentHead = document.createElement('span');
                    commentHead.appendChild(seekLink);
                    commentHead.appendChild(authorSpan);

                    if(typeof(youCaption.players[playerAPIId]['authors'][author]) == 'undefined'){
                      youCaption.players[playerAPIId]['authors'][author] = youCaption.getAuthorColor(playerAPIId);
                    }

                    commentHead.style.color = youCaption.players[playerAPIId]['authors'][author];

                    // TODO, make sure this works with non-html safe text
                    commentBody = document.createElement('span');
                    commentBody.className = 'ytCommentBody';
                    commentBody.appendChild(document.createTextNode(comment));

                    var commentElm = document.createElement('div');

                    // Alternates background colors for rows, toggles for next row
                    commentRow = youCaption.players[playerAPIId]['commentRow'];
                    youCaption.players[playerAPIId]['commentRow'] = (commentRow == 'ytCommentEven') ? 'ytCommentOdd' : 'ytCommentEven';
                    
                    commentElm.className = 'ytComment '+commentRow;

                    // Add comment head (containing time and author) to encapsulating coment div
                    commentElm.appendChild(commentHead);
                    // Add the body of the comment to the div as well
                    commentElm.appendChild(commentBody);

                    // Now that you've built the individual comment, append to div of comments
                    commentsDiv.appendChild(commentElm);
                }
 
                // Automatically scrolls
                commentsDiv.scrollTop = commentsDiv.scrollHeight;
                comments[time]['set'] = true;
            }
        }
        youCaption.players[playerAPIId]['updatingComments'] = false;
    },

    /*
     *
     */
    ytPlayerReady : function(playerAPIId){
        if(youCaption && youCaption.players && youCaption.players[playerAPIId]){
//            var ytPlayer = document.getElementById(youCaption.players[playerAPIId]['myYTPlayerId']);
            var ytPlayer = youCaption.getPlayer(playerAPIId);
            if(ytPlayer){
                var vol = ytPlayer.getVolume();
                if(ytPlayer.isMuted() || isNaN(vol) || vol == 0){
                    youCaption.setVolumeClass(playerAPIId,true);
                }

                youCaption.updateVolumeBar(playerAPIId);

                youCaption.cueVideo(playerAPIId, youCaption.players[playerAPIId]['videoId'], null);

                // ugly hack. storing function, calling that function later on in callback, wrapping the onstatechange
                youCaption.players[playerAPIId]['stateChange'] = function(state){
                    var _playerAPIId = playerAPIId;
                    youCaption.onYTPlayerStateChange(_playerAPIId,state);
                }
                var callback = 'youCaption.players["'+playerAPIId+'"]["stateChange"]';
                ytPlayer.addEventListener('onStateChange', callback);
                youCaption.players[playerAPIId]["firstLoaded"] = false;
            }
        }
    },

    getPlayer : function(playerAPIId){
      return document.getElementById(youCaption.players[playerAPIId]['myYTPlayerId']);
    },

    /*
     * utility function to grab elements inside the wrapper
     * @param - Element to grab
     * returns - If found, the element. Else, it returns false
     */
    getEl : function(playerAPIId,elname){
      if(typeof(elname) != 'string' || elname == '') return false;

//      console.log(elname+youCaption.players[playerAPIId]['ytKey']);
      var el = document.getElementById(elname+youCaption.players[playerAPIId]['ytKey']);

      return (typeof(el) != 'undefined') ? el : false;
    }
}

/* Needs to be outside the class, otherwise the youtube player can't call it
 * @param playerAPIId
 */
var onYouTubePlayerReady = function (playerAPIId) {
    if(typeof(youCaption.ytReadyVar) == 'function'){
        youCaption.ytReadyVar();
    }
    youCaption.ytPlayerReady(playerAPIId);
}
