function lvsysCyclerParams() {
    this.id = "";
    this.fx = 'fade';
    this.speed = 500;   // transition
    this.timeout = 4000; // still time
}

function lvsysCycler() {

    //Cycle gives the incorrect value for nextSlide when it first runs;
    //This variable enables us to compensate.
    var initialized = false;

    //How often to check YouTube videos, in milliseconds
    var pollInterval = 500;
    //How long before the end of a YouTube video to start
    //moving on, in seconds.
    var videoEndSlack = .25;

    //Storage variables, initialized in _cycleStartSlides()
    var nextIndex = 0;
    var cycleIndexes = new Array();
    var cycles = new Array();
    var players = new Array();
    //Managed from _cycleOnBefore
    var nextSlides = new Array();

    //Parameters for YouTube videos
    var params = { allowScriptAccess: "always" };

    //Parameters passed in by the user
    var userParamsObjArray = new Array;

    //For YouTube video resumption of the cycle;
    //Start over with the next slide
    var _cycleResumeCycle = function(cycleNumber) {
        var localParams = userParamsObjArray[cycleNumber];

        localParams.before = _cycleOnBefore;
        localParams.delay = 0;
        localParams.startingSlide = nextSlides[cycleNumber];
        initialized = false;

        var currentPanelType = $("#" + cycles[cycleNumber]).children().eq(localParams.startingSlide).attr("panelType");
        if (currentPanelType == 'chromeFix') {
            localParams.timeout = 5;
        }

        $("#" + cycles[cycleNumber]).cycle(localParams);
    }

    //Check any active YouTube players to see if those
    //cycles are ready to move on.
    var _cycleUpdatePlayerInfo = function() {

        //For each YouTube player, make sure it exists, and if it
        //does, check to see if the video is over.  If it is, clear
        //the player (originally set in _cycleOnBefore) and move on with the
        //next slide in that cycle.

        var numKeys = players.length;
        for (var i = 0; i < numKeys; i++) {
            if (players[i] != "NONE") {
                var ytplayer = document.getElementById(players[i]);
                if (ytplayer && ytplayer.getDuration) {
                    if (ytplayer.getCurrentTime() > (ytplayer.getDuration() - videoEndSlack)) {
                        players[i] = "NONE";
                        _cycleResumeCycle(i);
                    }
                }

            }

        }

    }

    //The _cycleOnBefore function runs before every transition in the cycle
    var _cycleOnBefore = function(currSlideElement, nextSlideElement,
			options, forwardFlag) {

        //The first time this runs, currSlide is incorrect; it's set as 0 twice.
        //The first time through, compensate so nextSlides is set properly.
        var nextSlideNumber = options.nextSlide;
        if (!initialized) {
            nextSlideNumber = nextSlideNumber - 1;
            initialized = true;
        }

        //Initialize general variables
        var thisCycle = $(nextSlideElement).parent().attr("id");
        var currentSlideType = $(nextSlideElement).attr("panelType");
        var thisCycleIndex = cycleIndexes[thisCycle];

        //Set the next slide.  nextSlideNumber is actually the CURRENT
        //slide, so when we're done with the current video (if applicable)
        //we want to move on to nextSlideNumber + 1.
        nextSlides[thisCycleIndex] = nextSlideNumber + 1;

        if (currentSlideType == "chromeFix") {
          //  $('#' + thisCycle).cycle('next');
        } else

        //Check the slide type.  Images don't need any processing, and
        //youtube and flowplayer need to be handled separately.
            if (currentSlideType == "youtube") {

                $('#' + thisCycle).cycle('pause');

            //Add the player to the players array so it gets checked by
            //_cycleUpdatePlayerInfo()
            players[thisCycleIndex] = $(nextSlideElement).attr("divId");

            //Get the information to feed to the player itself from the
            //slide's HTML attributes.
            var targetContent = $(nextSlideElement).attr("targetContent");
            var divId = $(nextSlideElement).attr("divId");
            var height = $(nextSlideElement).attr("height");
            var width = $(nextSlideElement).attr("width");

            //Create the actual video.  Make sure it starts right away,
            //and that javascript is enabled.
            swfobject.embedSWF(
				"http://www.youtube.com/v/" +
				targetContent + "?border=0&showsearch=0&showinfo=0&autoplay=1" +
				"&enablejsapi=1&playerapiid=" + divId,
        		    divId, width, height, "8", null, null, params,
	  			{ id: divId });

            //Pause the cycle.  We'll actually be restarting it with the
            //next slide, but if we STOP it instead the whole thing stops,
            //so pause instead.
            $('#' + thisCycle).cycle('pause');


        } else if (currentSlideType == "flowplayer") {

            //Set this player to "NONE", because only YouTube videos
            //need to be processed by _cycleUpdatePlayerInfo().
            players[thisCycleIndex] = "NONE";

            //Get player information from the slide's HTML attributes
            var targetContent = $(nextSlideElement).attr("targetContent");
            var divId = $(nextSlideElement).attr("divId");
            var height = $(nextSlideElement).attr("height");
            var width = $(nextSlideElement).attr("width");
            $("#" + divId).css("height", height).css("width", width);

            //Stop the current cycle.  We'll restart it from the
            //onFinish event, but onBegin doesn't happen fast enough
            //to stop the cycle on the first load in some browsers,
            //so we'll do it here.  And again, we need to pause so that
            //the video loads.
            $('#' + cycles[thisCycleIndex]).cycle('pause');

            //Create the flowplayer player and set it to 
            //restart it when the video ends.
            $f(divId, "http://releases.flowplayer.org/swf/flowplayer-3.1.5.swf", {
                clip: {
                    url: targetContent,
                    autoPlay: true,
                    autoBuffering: true,

                    onFinish: function() {
                        _cycleResumeCycle(thisCycleIndex);
                    }
                }
            });
        } else {

            //Clear the player; this is likely an image, and in any
            //case doesn't need to be processed by _cycleUpdatePlayerInfo().
            players[thisCycleIndex] = "NONE";
        }



    }

    //This function MUST be named onYouTubePlayerReady() because the YouTube API expects it.
    var onYouTubePlayerReady = function(playerId) {
    }

    //Initialize the cycles for all divs of the appropriate class.
    //(Actually, you can us any JQuery selector here.)
    var _cycleStartSlides = function(slideShowClass, paramsObjArray) {

        //If the user passes only one set of paramters, use it for
        //every cycle.  If there are fewer parameter sets than cycles,
        //use the last one to fill in the rest.
        if (paramsObjArray == null || paramsObjArray.length == 0) {
            for (i = 0; i < $(slideShowClass).length; i++) {
                var newParams = new lvsysCyclerParams();
                userParamsObjArray[i] = newParams;
            }
        } else if (paramsObjArray.length < $(slideShowClass).length) {
            for (i = 0; i < paramsObjArray.length; i++) {
                userParamsObjArray[i] = paramsObjArray[i];
            }
            for (i = paramsObjArray.length; i < $(slideShowClass).length; i++) {
                userParamsObjArray[i] = paramsObjArray[paramsObjArray.length - 1];
            }
        } else {
            userParamsObjArray = paramsObjArray;
        }


        //Loop through the cycle constructs
        //      $(slideShowClass).each(function() {
        for (i = 0; i < userParamsObjArray.length; i++) {

            var thisParamsObj = userParamsObjArray[i];

            //Get this individual cycle
            //var thisCycle = $(this).attr("id");
            var thisCycle = thisParamsObj.id;

            //Correct for crashes on Chrome when video follows Flowplayer
            $(".chrome *[panelType='flowplayer']").each(function() {
                if ($(this).next().attr("panelType") == "youtube" || $(this).next().attr("panelType") == "flowplayer") {
                    $(this).after("<div panelType='chromeFix'></div>");
                }
            }
			);
            //Same fix, but applies only if the first panel is video, and the last panel is Flowplayer
            if ($("#" + thisCycle).children("*:last").attr("panelType") == "flowplayer") {
                if ($("#" + thisCycle).children("*:first").attr("panelType") == "flowplayer" ||
                        $("#" + thisCycle).children("*:first").attr("panelType") == "youtube") {

                    $("#" + thisCycle).children("*:last").after("<div panelType='chromeFix'></div>");

                }
            }

            //Cycle must have at least two panels
            if ($("#" + thisCycle).children().length == 1) {
                if ($("#" + thisCycle).children("*:last").attr("panelType") == "youtube") {
                    $("#" + thisCycle).children("*:last").after("<div panelType='chromeFix'></div>");
                }
            }

            //Now go ahead and actually run the cycle
            $("#" + thisCycle).show();

            //Add standard parameters to user object
            var localParams = thisParamsObj; //userParamsObjArray[nextIndex];
            localParams.before = _cycleOnBefore;

            //Initialize the storage arrays
            /*cycles[nextIndex] = thisCycle;
            cycleIndexes[thisCycle] = nextIndex;
            players[nextIndex] = "NONE";*/
            cycles[i] = thisCycle;
            cycleIndexes[thisCycle] = i;
            players[i] = "NONE";

       //     nextIndex++;


            $("#" + thisCycle).cycle(localParams);


        }
        //});

        //Start the player monitor.  If there are no
        //current players, it won't do anything.
        setInterval(_cycleUpdatePlayerInfo, pollInterval);
    }
    //This function must be externalized so it can be called from within the page.
    this.startSlides = _cycleStartSlides;
}
