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

    //For single videos
    this.isSingleton = false;
    this.isSingleVideo = false;
    this.autoStartSingleVideo = false;
    this.loopSingleVideo = false;
}

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() > 0) && (ytplayer.getCurrentTime() > (ytplayer.getDuration() - videoEndSlack))) {
                        var localParams = userParamsObjArray[i];
                        if (!localParams.isSingleVideo || localParams.loopSingleVideo) {
                            players[i] = "NONE";
                            _cycleResumeCycle(i);
                        } else {
                            ytplayer.seekTo(.1, true);
                            ytplayer.pauseVideo();
                        }
                    }
                }

            }

        }

    }

    var _prepYouTube = function(cycleIndex, targetElement, autoStart) {
        //Add the player to the players array so it gets checked by
        //_cycleUpdatePlayerInfo()
        players[cycleIndex] = $(targetElement).attr("divId");

        //Get the information to feed to the player itself from the
        //slide's HTML attributes.
        var targetContent = $(targetElement).attr("targetContent");
        var divId = $(targetElement).attr("divId");
        var height = $(targetElement).attr("height");
        var width = $(targetElement).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=" + autoStart +
				    "&enablejsapi=1&playerapiid=" + divId,
        		        divId, width, height, "8", null, null, params,
	  			    { id: divId });
    }

    var _prepFlowplayer = function(cycleIndex, targetElement, autoStart) {

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

        //Get player information from the slide's HTML attributes
        var targetContent = $(targetElement).attr("targetContent");
        var divId = $(targetElement).attr("divId");
        var height = $(targetElement).attr("height");
        var width = $(targetElement).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[cycleIndex]).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: autoStart,
                autoBuffering: true,

                onFinish: function() {
                    var localParams = userParamsObjArray[cycleIndex];
                    if (!localParams.isSingleVideo) {
                        _cycleResumeCycle(cycleIndex);
                    } else {
                        localParams.before = _cycleOnBefore;
                        localParams.delay = 0;
                        localParams.startingSlide = nextSlides[cycleIndex] - 1;
                        initialized = false;

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

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

    //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()
            _prepYouTube(thisCycleIndex, nextSlideElement, 1);

            //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") {

            var autoStart = true;
            var localParams = userParamsObjArray[thisCycleIndex];
            if (localParams.isSingleVideo && !localParams.loopSingleVideo) {
                autoStart = false;
            }
            _prepFlowplayer(thisCycleIndex, $(nextSlideElement), autoStart);

        } 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
        for (i = 0; i < userParamsObjArray.length; i++) {

            var thisParamsObj = userParamsObjArray[i];

            //Get this individual cycle
            var thisCycle = thisParamsObj.id;
            if ("#" + thisCycle == "#") {
                //Do nothing; thisCycle is empty
            } else {

                //Add standard parameters to user object
                var localParams = thisParamsObj;
                localParams.before = _cycleOnBefore;

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

                //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>");

                    }
                }

                //If the cycle consists of a single video, the default is to not autostart,
                //and not loop.  If both of those conditions are overridden, run as usual.
                //If not, handle that separately here.

                //If there's just a single Flowplayer, by now we'll have added a chromeFix, so
                //check for that as well.
                var runCycle = true;
                if ($("#" + thisCycle).children().length == 1 ||
                    ($("#" + thisCycle).children().length == 2 &&
                    $("#" + thisCycle).children("*:first").attr("panelType") == "flowplayer")) {
                    if (localParams.autoStartSingleVideo && localParams.loopSingleVideo) {

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

                    } else {
                        //Save singleVideo status
                        if ($("#" + thisCycle).children("*:first").attr("panelType") == "youtube" ||
                            $("#" + thisCycle).children("*:first").attr("panelType") == "flowplayer") {
                            userParamsObjArray[i].isSingleVideo = true;
                        }

                        //We automatically check restart the cycle when the video is done, so if
                        //it's going to loop, just add a new panel and go on as normal.
                        if (localParams.loopSingleVideo) {
                            if ($("#" + thisCycle).children("*:last").attr("panelType") == "youtube") {
                                $("#" + thisCycle).children("*:last").after("<div panelType='chromeFix'></div>");
                            }
                        }

                        runCycle = false;
                        var autoStart = 0;
                        if (localParams.autoStartSingleVideo) {
                            autoStart = 1;
                        }
                        if ($("#" + thisCycle).children("*:first").attr("panelType") == "youtube") {
                            _prepYouTube(i, $("#" + thisCycle).children("*:first"), autoStart);
                        }
                        if ($("#" + thisCycle).children("*:first").attr("panelType") == "flowplayer") {
                            _prepFlowplayer(i, $("#" + thisCycle).children("*:first"), localParams.autoStartSingleVideo);
                        }


                    }
                }

                //Now go ahead and actually run the cycle
                $("#" + thisCycle).show();
                if (runCycle) {
                    $("#" + 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;
}
