(function($) {
    // Plugin definition
    $.fn.InfoChoiceWidget = function(options) {
        var opts = $.extend({}, $.fn.InfoChoiceWidget.Defaults, options);
        var selectedPanel = "";

        // Define out template object
        var Template = {
            Settings: '<div class="icSettings"><input type="hidden" value="${VisibleDataCount}" class="icVisibleDataCount" /><input type="hidden" value="${ShowPager}" class="icShowPager" /><input type="hidden" value="${IdleTimeOutDuration}" class="icIdleTimeOutDuration" /><input type="hidden" value="" class="icTimerIds" /></div>',
            Header: '<div class="icHeader icSelfClear">${HeaderLogo}${Heading}</div>',
            HeaderLogo: '<a class="icLogo" href="${LogoClickOutUrl}" target="${LogoClickOutTarget}">${LogoImage}</a>',
            HeaderLogoImage: '<img src="${LogoImageUrl}" alt="" />',
            HeaderHeading: '<div class="icTitle">${Heading}</div>',
            TabWrapper: '<ul class="icTabs icSelfClear">${Tabs}</ul>',
            Tab: '<li><a href="javascript:void(0)" class="${TabCssClass}">${TabName}</a></li>',
            ViewPort: '<div class="icViewPort"><img src="${AjaxLoaderImage}" class="icInProgress" alt="Loading..." />${Panels}</div>',
            Panel: '<div class="${Id} icPanel icSelfClear">${Content}</div>',
            PanelData: '<div class="icData icSelfClear">${PanelData}</div>',
            DataLogo: '<div class="icLogo"><table border="0" cellpadding="0" cellspacing="0" width="100%" height="100%"><tr><td align="center" valign="middle">${ImgSrc}</td></tr></table></div>',
            DataDetails: '<div class="icDetails"><div class="icWrapper"><table border="0" cellpadding="0" cellspacing="0" width="100%" height="100%"><tr><td align="left" valign="middle"><span class="icHeading">${Heading}</span><span class="icSubHeading">${SubHeading}</span></td></tr></table></div></div>',
            DataRates: '<div class="icRates"><table border="0" cellpadding="0" cellspacing="0" width="100%" height="100%"><tr><td align="center" valign="middle"><a href="${LinkUrl}" onclick="${LinkOnClickJs}" target="${LinkTarget}" rel="nofollow" class="icWrapper icLink icSelfClear"><span class="icSelfClear icRatesLeft"><span class="icRateHeading icSelfClear">${RatesHeading}</span><span class="icWhole">${Whole}</span><span class="icDot">.</span><span class="icDecimal">${Decimal}</span><span class="icPercentage">%</span></span><span class="icRatesRight"><span class="icArrow">&nbsp;&gt;</span></span></a></td></tr></table></div>',
            Footer: '<div class="icFooter">${Pager} ${FooterLink}</div>',
            FooterLink: '<a href="${Link}" class="icFooterLink">${LinkText}</a>',
            Pager: '<div class="icPager icSelfClear"><a class="icTop" href="javascript:void(0);">Top</a><a class="icPrevious" href="javascript:void(0);">Previous</a><a class="icNext icLast" href="javascript:void(0);">Next</a></div>'
        };

        // debugging helper to log messages to the console widow
        function Log(messageTemplate, tokens) {
            if (opts.IsDebug && window.console && window.console.log) {
                window.console.log(Replace(messageTemplate, tokens));
            }
        };

        // html encoding
        function HtmlEncode(value) {
            return $('<div/>').text(value).html();
        };

        // html decoding
        function HtmlDecode(value) {
            return $('<div/>').html(value).text();
        };

        // return the time difference in milliseconds since the last timestamp was stored
        function GetTimeDiff() {
            var timeDiff = 0;
            var now = new Date().getTime();
            if (window.icLastTimestamp != null) {
                timeDiff = now - window.icLastTimestamp;
            }
            window.icLastTimestamp = now;
            return timeDiff;
        };

        // Init the current timestamp for use with the GetTimeDiff
        function InitTimeDiff() {
            window.icLastTimestamp = new Date().getTime();
        };

        // Log the time difference to the console log
        function LogTimeDiff() {
            if (opts.IsDebug && window.console && window.console.log) {
                var diff = GetTimeDiff();
                window.console.log("Time difference: " + diff + " ms");
            }
        };

        // Plugin main code
        return this.each(function() {
            $this = $(this);
            var o = $.meta ? $.extend({}, opts, $this.data()) : opts;
            Initialise(o);
        });

        // Initialise the widget with the given options
        function Initialise(options) {
            Log("Initialise - " + $this.get(0).id);
            InitTimeDiff();
            $this.html("");
            $.ajax({
                url: options.DataUrl,
                async: options.UseAsyncAjax,
                cache: false,
                dataType: "jsonp",
                contentType: "text/javascript",
                success: function(widgetData) { OnGetDataSuccess($this.get(0).id, widgetData); }
            });
        };

        // Get data success call back handler
        function OnGetDataSuccess(widgetId, widgetData) {
            LogTimeDiff();
            Log("OnGetDataSuccess - " + widgetId);

            widgetData.This = $("#" + widgetId);
            widgetData.WidgetGuid = widgetId;
            widgetData.CanAnimate = true;
            icWidgetDataStore[widgetId] = widgetData;
            BuildHeader(widgetData);
            BuildContent(widgetData);
            BuildFooter(widgetData);

            widgetData.InProgress = widgetData.This.find(".icInProgress");
            InitialiseSizing(widgetData);
            InitialisedSelectedTab(widgetData);
            RunBuildAnimation(widgetData, $this);
            BindEventsHandlers(widgetData);
            InitialisePager($this);
            widgetData.InProgress.hide();
        };

        // Build the widget header
        function BuildHeader(widgetData) {
            LogTimeDiff();
            Log("BuildHeader - " + widgetData.WidgetGuid);

            if (widgetData.ShowHeading || widgetData.ShowLogo) {
                var heading = widgetData.ShowHeading ? Replace(Template.HeaderHeading, { Heading: widgetData.Heading }) : "";
                var headerImage = Replace(Template.HeaderLogoImage, { LogoImageUrl: widgetData.LogoImageUrl });
                var headerLogo = widgetData.ShowLogo ? Replace(Template.HeaderLogo, { LogoImage: headerImage, LogoClickOutUrl: (widgetData.LogoClickOutUrl == "" ? "javascript:void(0);" : widgetData.LogoClickOutUrl), LogoClickOutTarget: widgetData.LogoClickOutTarget }) : "";

                widgetData.This.append(Replace(Template.Header, { Heading: heading, HeaderLogo: headerLogo }));
            }
        };

        // Build the panel content
        function BuildContent(widgetData) {
            LogTimeDiff();
            Log("BuildContent - " + widgetData.WidgetGuid);

            var widget = widgetData.This.append(Replace(Template.ViewPort, { AjaxLoaderImage: options.AjaxLoaderImage, Panels: "" }));
            var viewPort = widget.find(".icViewPort");
            var tabs = "";
            var panelData;
            for (var i = 0; i < widgetData.Panels.length; i++) {
                panelData = widgetData.Panels[i];
                tabs = tabs + Replace(Template.Tab, { TabCssClass: panelData.Id, TabName: panelData.Name });
                if (panelData.Data.length > 0 || panelData.StaticData != "") {
                    var panel = panelData.ContentType == "Report" ? BuildPanel(widgetData, panelData) : BuildStaticPanel(widgetData, panelData);
                    viewPort.append(panel);
                    BindPanelEventHandlers(panelData);
                }
            }

            viewPort.before(Replace(Template.TabWrapper, { Tabs: tabs }));
        };

        // Build the widget tab static panel
        function BuildStaticPanel(widgetData, panel) {            
            return Replace(Template.Panel, { Id: panel.Id, Content: panel.StaticData });
        };

        // Build the widget tab panel
        function BuildPanel(widgetData, panel) {
            var panelData = '';
            var data, dataLogo, dataDetails, dataRates;
            var sourceUrl = '';
            try { sourceUrl = location.protocol + location.hostname + location.pathname; } catch (e) { sourceUrl = 'No-access-to-source-URL'; }
            for (var i = 0; i < panel.Data.length; i++) {
                data = panel.Data[i];
                var clickUrl = data.LinkUrl;
                var linkOnClickJs = data.LinkOnClickJs.replace("UpdateUrl(this);", "");
                if (widgetData.LinkClickOutType == "Apply" && linkOnClickJs != "") {
                    var location = window.top.location;
                    clickUrl = clickUrl + "&CLK=9228123112&LOCBU=" + sourceUrl;
                }
                dataLogo = Replace(Template.DataLogo, { ImgSrc: data.Logo });
                dataDetails = Replace(Template.DataDetails, { Heading: data.Heading, SubHeading: data.SubHeading });
                dataRates = Replace(Template.DataRates, { LinkUrl: clickUrl, LinkOnClickJs: linkOnClickJs, LinkTarget: widgetData.LinkClickOutTarget, RatesHeading: data.RatesHeading, Whole: data.RateWhole, Decimal: data.RateDecimal });
                panelData = panelData + Replace(Template.PanelData, { PanelData: dataLogo + dataDetails + dataRates });
            }
            return Replace(Template.Panel, { Id: panel.Id, Content: panelData });
        };

        // Build the widget footer
        function BuildFooter(widgetData) {
            LogTimeDiff();
            Log("BuildFooter - " + widgetData.WidgetGuid);

            var footerLink = "";
            if (widgetData.ShowPager || widgetData.ShowFooterLink) {
                footerLink = widgetData.ShowFooterLink ? Replace(Template.FooterLink, { Link: widgetData.FooterLinkUrl, LinkText: widgetData.FooterLinkText }) : "";
                widgetData.This.append(Replace(Template.Footer, { Pager: (widgetData.ShowPager ? Template.Pager : ""), FooterLink: footerLink }));
            }
        };

        // Init the widget sizes - ie. widget width and height, panel data height
        function InitialiseSizing(widgetData) {
            LogTimeDiff();
            Log("InitialiseSizing - " + widgetData.WidgetGuid);

            var header = widgetData.This.find(" .icHeader");
            var tabs = widgetData.This.find(".icTabs");
            var viewPort = widgetData.This.find(".icViewPort");
            var footer = widgetData.This.find(".icFooter");
            var borderAdjustment = 4;
            if ($.browser.msie) {
                borderAdjustment = 0;
            }

            var parents = widgetData.This.parent();
            if (parents.length > 0) {
                var parent = parents.get(0);
                if (parent.nodeName.toLowerCase() == "body") {
                    $(parent).css("margin", 0).css("padding", 0);
                }
            }

            widgetData.This.css("width", widgetData.Width).css("height", widgetData.Height);
            viewPort.css("width", widgetData.Width).css("height", widgetData.Height - header.height() - tabs.height() - footer.height() - borderAdjustment); // 4 for viewport top & bottom border

            var panel;
            for (var i = 0; i < widgetData.Panels.length; i++) {
                panel = widgetData.Panels[i];
                if (panel.Data.length > 0) {
                    InitialisePanelSizing(widgetData, $("." + panel.Id));
                }
            }

            if (widgetData.ShowPager || widgetData.ShowFooterLink) {
                if (widgetData.ShowPager) {
                    var pager = widgetData.This.find(".icPager");
                    pager.css("left", (widgetData.Width - pager.width()) / 2);
                }
                footer.css("width", widgetData.This.width()); // ie bug fix
            }
        };

        // Initialise panel sizing calculations
        function InitialisePanelSizing(widgetData, panel) {
            var viewPort = widgetData.This.find(".icViewPort");
            var dataHeight = Math.floor(viewPort.height() / widgetData.VisibleDataCount);
            var widgetWidthMin = 361;
            var dataLogoWidth = widgetData.Width > widgetWidthMin ? opts.DataLogoWidthMax : opts.DataLogoWidth;
            var dataRatesWidth = widgetData.Width > widgetWidthMin ? opts.DataRatesWidthMax : opts.DataRatesWidth;
            var detailsWidth = widgetData.Width - (dataLogoWidth + dataRatesWidth + 2);

            panel.find(".icData").each(function(i) {
                var row = $(this);
                // set column widths
                try { row.find(".icLogo").css("width", dataLogoWidth).css("height", dataHeight); } catch (e) { };
                var rates = row.find(".icRates").css("width", dataRatesWidth).css("height", dataHeight)
                // minus 2 for the border width
                row.find(".icDetails").css("width", detailsWidth).css("height", dataHeight).find(".icSubHeading").each(function(i) {
                    // auto eclipse the subheading text based on available spacing
                    var subHeading = $(this);
                    var content = subHeading.text();
                    if (content != "") {
                        var remainingHeight = dataHeight - this.offsetTop;
                        var maxRow = Math.floor(remainingHeight / 15);
                        var maxChar = Math.floor((subHeading.width() * maxRow) / 5.2) - 3;
                        content = content.substring(0, Math.min(maxChar, content.length));
                        if (maxChar == content.length) {
                            content = content + "...";
                        }
                        subHeading.text(content);
                    }
                });
                row.find(".icRatesRight").css("padding-top", (dataHeight - 21) / 2);
            });
            // fix ie disappearing content
            if ($.browser.msie) {
                panel.css("zoom", "1");
            }
        };

        // Init the first selected tab
        function InitialisedSelectedTab(widgetData) {
            LogTimeDiff();
            Log("InitialisedSelectedTab - " + widgetData.WidgetGuid);

            widgetData.This.find(".icTabs ." + widgetData.SelectedPanel).parent().addClass("icSelected");
            widgetData.This.find(".icPanel").filter(function() { return !$(this).addClass("icSelected").hasClass(widgetData.SelectedPanel); }).removeClass("icSelected").hide();
            InitialiseFooterLink(widgetData.This, widgetData.SelectedPanel);
        };

        // Init the footer link
        function InitialiseFooterLink(widget, selectedPanel) {
            var widgetData = icWidgetDataStore[widget.get(0).id];
            if (widgetData.ShowFooterLink) {
                var footerLink = widget.find(".icFooterLink").get(0);
                var selectedPanelData = GetWidgetPanel(widgetData.Panels, selectedPanel);
                footerLink.href = selectedPanelData.FooterLinkUrl;
                footerLink.innerHTML = selectedPanelData.FooterLinkText;
                footerLink.target = selectedPanelData.FooterLinkTarget;
            }
        };

        // Get a widget panel
        function GetWidgetPanel(panels, id) {
            var selectedPanel;
            for (var i = 0; i < panels.length; i++) {
                if (panels[i].Id == id) {
                    selectedPanel = panels[i];
                    break;
                }
            };
            return selectedPanel;
        };

        // Bind all widget event handlers
        function BindEventsHandlers(widgetData) {
            LogTimeDiff();
            Log("BindEventsHandlers - " + widgetData.WidgetGuid);

            widgetData.This.find(".icTabs a").click(OnTabClick);
            var selectedPanel = FindPanel(widgetData, widgetData.SelectedPanel);
            BindPanelEventHandlers(selectedPanel);

            if (widgetData.ShowPager) {
                widgetData.This.find(".icTop").click(OnTopClick);
                widgetData.This.find(".icNext").click(OnNextClick);
                widgetData.This.find(".icPrevious").click(OnPreviousClick);
            }

            if (widgetData.IdleEffect != null && widgetData.IdleEffect != "") {
                widgetData.This.hover(function() { OnWidgetHover(widgetData, widgetData.This); }, function() { OnWidgetOut(widgetData, widgetData.This); });
                InitialiseIdleTimeOut(widgetData.IdleEffect, widgetData.IdleTimeOutDuration, widgetData.This);
            }
            LogTimeDiff();
        };

        // Bind the panel evenets
        function BindPanelEventHandlers(panelInfo) {
            var panel = $("." + panelInfo.Id);
            var panelData = panel.find(".icData").hover(function() { $(this).addClass("icHover").find(".icArrow").addClass("icArrowHover") }, function() { $(this).removeClass("icHover").find(".icArrow").removeClass("icArrowHover") });
            var panelDataCell = panelData.children().filter(function() { return !$(this).hasClass("icRates"); }).click(OnPanelDataClick);

        };

        // Handle the widget hover event
        function OnWidgetHover(widgetData, widget) {
            Log("OnWidgetHover");

            if (widgetData.TimerIds == null) {
                return;
            }
            widgetData.CanAnimate = false;
            for (var timerId in widgetData.TimerIds) {
                clearTimeout(timerId);
                Log("Cleared timer id = ${Id}", { Id: timerId });
            }
            widgetData.TimerIds = {};
        };

        // Handle the widget hover out event
        function OnWidgetOut(widgetData, widget) {
            Log("OnWidgetOut");
            widgetData.CanAnimate = false;
        };

        // On tab click handler
        function OnTabClick(e) {
            var widget = GetWidget(this);
            var widgetData = icWidgetDataStore[widget.get(0).id];
            var panelId = this.className;
            var panel = FindPanel(widgetData, panelId);

            if (panel != null && panel.Data.length == 0) {
                widgetData.InProgress.show();
                var panelDataUrl = options.DataUrl.replace("RequestType=Data", "RequestType=PanelData") + "&PanelId=" + panel.DbId;
                $.ajax({
                    url: panelDataUrl,
                    async: options.UseAsyncAjax,
                    cache: false,
                    dataType: "jsonp",
                    contentType: "text/javascript",
                    success: function(panelData) {
                        var viewPort = widgetData.This.find("div.icViewPort");
                        var panel = BuildPanel(widgetData, panelData);
                        viewPort.append(panel);
                        BindPanelEventHandlers(panelData);
                        InitialisePanelSizing(widgetData, $("." + panelData.Id));
                        SwitchTab(widget, panelId);
                        SetPanelData(widgetData, panelId, panelData);
                        widgetData.InProgress.hide();
                    }
                });
            } else {
                SwitchTab(widget, this.className);
            }
        };

        // Switch to a tab
        function SwitchTab(widget, tabId, animate) {
            widget.find("ul.icTabs .icSelected").removeClass("icSelected");
            widget.find("div.icViewPort .icSelected").removeClass("icSelected").hide();
            //debugger;
            $(widget.find("." + tabId).parent().get(0)).addClass("icSelected");
            var panel = widget.find("div.icViewPort ." + tabId).addClass("icSelected").show();
            panel.animate({ top: 0 }, opts.ScrollDuration, opts.ScrollEasing, function() { InitialisePager(widget); });
            var cssClassSplit = panel.get(0).className.split(" ");
            InitialiseFooterLink(widget, cssClassSplit[0]);
        };

        // Handle panel data click event
        function OnPanelDataClick(e) {
            var links = $(this.parentNode).find(".icLink");
            if (links.length == 0) {
                return;
            }
            var link = links.get(0);
            // trigger the click event on the button
            try {
                $(link).trigger('click');
            } catch (ex) {
            }

            var url = link.href;
            var target = link.target;
            // open up the url
            if (target == "" || target == "_self" || target == "_parent" || target == "_top") {
                window.top.location = url;
            } else {
                window.open(url, target);
            }
        };

        // Initialise the pager
        function InitialisePager(widget) {
            var widgetData = icWidgetDataStore[widget.get(0).id];
            if (!widgetData.ShowPager) {
                return;
            }
            var visibleDataCount = widgetData.VisibleDataCount;
            var viewPort = widget.find(".icViewPort");
            var panel = viewPort.find(".icSelected");
            var panelPosition = Math.abs(parseInt(panel.css("top").replace("px", "")));
            var top = widget.find(".icTop");
            var next = widget.find(".icNext");
            var previous = widget.find(".icPrevious");

            panel.height() > viewPort.height() ? top.removeClass("icTopDisabled") : (top.hasClass("icTopDisabled") ? void (0) : top.addClass("icTopDisabled"));

            if (panelPosition > 0) {
                previous.removeClass("icPreviousDisabled");
            } else if (!previous.hasClass("icPreviousDisabled")) {
                previous.addClass("icPreviousDisabled");
            }
            if (panel.height() > panelPosition + viewPort.height() + visibleDataCount) {
                next.removeClass("icNextDisabled");
            } else if (!next.hasClass("icNextDisabled")) {
                next.addClass("icNextDisabled");
            }
        };

        // Get the widget given a context reference
        function GetWidget(context) {
            return $(context).parents(".icWidget");
        }

        // Handle the top button click
        function OnTopClick(e) {
            var widget = GetWidget(this);
            widget.find(".icViewPort .icSelected").animate({ top: 0 }, opts.ScrollDuration, opts.ScrollEasing, function() { InitialisePager(widget); });
        };

        // Handle the next button click
        function OnNextClick(e) {
            var widget = GetWidget(this);
            var widgetData = icWidgetDataStore[widget.get(0).id];
            ScrollViewPort(widget, widgetData, "down");
        };

        // Handle the previous button click
        function OnPreviousClick(e) {
            var widget = GetWidget(this);
            var widgetData = icWidgetDataStore[widget.get(0).id];
            ScrollViewPort(widget, widgetData, "up");
        };

        // scroll the view port in the given direction
        function ScrollViewPort(widget, widgetData, direction, speed, callback) {
            var visibleDataCount = widgetData.VisibleDataCount;
            var viewPort = widget.find(".icViewPort");
            var panel = widget.find(".icViewPort .icSelected");
            var panelPosition = Math.abs(parseInt(panel.css("top").replace("px", "")));
            var scrollDuration = opts.ScrollDuration;
            var viewPortHeight = viewPort.innerHeight();
            var panelHeight = panel.innerHeight();
            var adjustment = Math.ceil(visibleDataCount / 2);

            if (direction == "up") {
                if (panelPosition > 0) {
                    scrollAmount = Math.max(0, panelPosition - viewPortHeight - adjustment);
                }
            } else {
                if (panelPosition + viewPortHeight <= panelHeight) {
                    scrollAmount = Math.min(panelPosition + viewPortHeight + adjustment, panelHeight - viewPortHeight);
                }
            }
            panel.animate({ top: -scrollAmount }, speed || opts.ScrollDuration, opts.ScrollEasing, function() {
                InitialisePager(widget);
                if (callback != null && typeof (callback) == "function") {
                    Log("Call callback after scrolling");
                    callback(widget, widgetData, direction, speed, callback);
                }
            });
        };

        // Setup the idle time out animation
        function InitialiseIdleTimeOut(idleEffect, idleTimeOutDuration, widget) {
            if (idleEffect == null || idleEffect == "") {
                return;
            }
            Log("InitialiseIdleTimeOut: idleEffect = ${IdleEffect}, idleTimeOutDuration = ${IdleTimeOutDuration}", { IdleEffect: idleEffect, IdleTimeOutDuration: idleTimeOutDuration });

            var timerId = setTimeout(function() { OnIdleTimeOut(idleEffect, widget); }, idleTimeOutDuration);
            var widgetData = icWidgetDataStore[widget.get(0).id];

            if (widgetData.TimerIds == null) {
                widgetData.TimerIds = {};
            }
            widgetData.TimerIds[timerId] = timerId;
        };

        // Handle the idle time out event
        function OnIdleTimeOut(idleEffect, widget) {
            Log("OnIdleTimeOut");
            // set for the next idle
            var widgetData = icWidgetDataStore[widget.get(0).id];
            if (!widgetData.CanAnimate) {
                return;
            }

            if (idleEffect == "AutoTabSwitching") {
                OnAutoTabSwitching(widget);
            } else if (idleEffect == "AutoScroll") {
                OnAutoScroll(widget);
            } else {
                var animateList = GetDataToAnimate(widget);
                if (animateList.length > 0) {
                    Animate(widgetData, opts.EffectConfig[idleEffect], animateList);
                    InitialiseIdleTimeOut(idleEffect, widgetData.IdleTimeOutDuration, widget);
                }
            }
        };

        // Run the build animation
        function RunBuildAnimation(widgetData, widget) {
            if (widgetData.BuildEffect == "") {
                return;
            }
            var animateList = GetDataToAnimate(widget);
            if (animateList.length > 0) {
                Animate(widgetData, opts.EffectConfig[widgetData.BuildEffect], animateList);
            }
        };

        // Get the panel data that is visible in the view port for animation
        function GetDataToAnimate(widget) {
            var animateList = [];
            var panel = widget.find(".icViewPort .icSelected");
            var children = panel.children();
            if (children.length > 0) {
                var dataHeight = $(children.get(0)).height();
                var startIndex = Math.ceil(Math.abs(panel.css("top").replace("px", "")) / dataHeight);
                var widgetData = icWidgetDataStore[widget.get(0).id];
                animateList = children.slice(startIndex, startIndex + widgetData.VisibleDataCount);
            }
            return animateList;
        };

        // Animate a given element in sequence with the effect and duration
        function Animate(widgetData, effectConfig, elements, startIndex) {
            if (!widgetData.CanAnimate) {
                return;
            }
            startIndex = startIndex || 0;
            if (elements != null && effectConfig != null && startIndex < elements.length) {
                var element = elements[startIndex];
                $(element).effect(effectConfig.EffectType, effectConfig.Options, effectConfig.Duration, function() { Animate(widgetData, effectConfig, elements, startIndex + 1); });
            }
        };

        // Handle the auto tab switching event
        function OnAutoTabSwitching(widget) {
            var tabs = widget.find(".icTabs a");
            AutoSwitchTabs(widget, tabs, 0);
        };

        // Auto switch between tabs
        function AutoSwitchTabs(widget, tabs, startIndex) {
            var widgetData = icWidgetDataStore[widget.get(0).id];
            if (!widgetData.CanAnimate) {
                return;
            }
            startIndex = startIndex || 0;
            if (tabs != null && startIndex < tabs.length) {
                var tab = tabs[startIndex];
                SwitchTab(widget, tab.className, true);
                setTimeout(function() { AutoSwitchTabs(widget, tabs, startIndex + 1) }, opts.EffectConfig.AutoTabSwitching.Duration);
            } else {
                InitialiseIdleTimeOut("AutoTabSwitching", widgetData.IdleTimeOutDuration, widget);
            }
        };

        // perform auto scrolling of the view port
        function AutoScroll(widget, widgetData, direction, speed, callback) {
            if (!widgetData.CanAnimate) {
                return;
            }
            var canScroll = false;
            var visibleDataCount = widgetData.VisibleDataCount;
            var viewPort = widget.find(".icViewPort");
            var panel = widget.find(".icViewPort .icSelected");
            var panelPosition = Math.abs(parseInt(panel.css("top").replace("px", "")));

            if (direction == "up") {
                var previous = widget.find(".icPrevious");
                canScroll = !previous.hasClass("icPreviousDisabled");
                if (!canScroll) {
                    InitialiseIdleTimeOut("AutoScroll", widgetData.IdleTimeOutDuration, widget);
                }
            } else {
                var next = widget.find(".icNext");
                canScroll = !next.hasClass("icNextDisabled");
                if (!canScroll) {
                    direction = "up";
                    canScroll = true;
                }
            }
            if (canScroll) {
                ScrollViewPort(widget, widgetData, direction, speed, callback);
            }
        };

        // Handle the auto scroll event
        function OnAutoScroll(widget) {
            var widgetData = icWidgetDataStore[widget.get(0).id];
            var config = opts.EffectConfig.AutoScroll;

            AutoScroll(widget, widgetData, "down", config.ScrollDuration, AutoScroll);
        };

        // find a panel based on its name
        function FindPanel(widget, id) {
            var panel, tmpPanel;

            for (var i = 0; i < widget.Panels.length; i++) {
                tmpPanel = widget.Panels[i];
                if (tmpPanel.Id == id) {
                    panel = tmpPanel;
                    break;
                }
            }

            return panel;
        };

        // set the panel
        function SetPanelData(widgetData, panelId, panelData) {
            var tmpPanel;

            for (var i = 0; i < widgetData.Panels.length; i++) {
                tmpPanel = widgetData.Panels[i];
                if (tmpPanel.Id == panelId) {
                    widgetData.Panels[i] = panelData;
                    break;
                }
            }
        };

    };

    // String formatting
    function Format(text) {
        if (arguments.length <= 1) {
            return text;
        }
        var tokenCount = arguments.length - 2;
        for (var token = 0; token <= tokenCount; token++) {
            text = text.replace(new RegExp("\\{" + token + "\\}", "gi"), arguments[token + 1]);
        }
        return text;
    };

    // String token replacements
    function Replace(text, replacements) {
        if (replacements == null) {
            return text;
        }
        for (replacement in replacements) {
            text = text.replace(new RegExp("\\\${" + replacement + "\\}", "gi"), replacements[replacement]);
        }
        return text;
    };

    // Plugin defaults
    $.fn.InfoChoiceWidget.Defaults = {
        EffectConfig: {
            Slide: { EffectType: "slide", Options: {}, Duration: 300 },
            Bounce: { EffectType: "bounce", Options: {}, Duration: 300 },
            Pulsate: { EffectType: "pulsate", Options: {}, Duration: 400 },
            AutoTabSwitching: { Duration: 5000 },
            AutoScroll: { ScrollDuration: 5000 }
        },
        IdleTimeOutDuration: 600000,
        FadeDuration: 1000,
        ScrollDuration: 300,
        ScrollEasing: '',
        DataLogoWidth: 60,
        DataLogoWidthMax: 90,
        DataRatesWidth: 95,
        DataRatesWidthMax: 120,
        UseAsyncAjax: false,
        IsDebug: false
    };

})(jQuery);

// setup the widget data store only if it's not already available
if (typeof (icWidgetDataStore) == "undefined") {
    var icWidgetDataStore = new Object();
}

function UpdateUrl(o, clkValue) {
    if (o.href != null && o.href.indexOf("CLK") == -1) {
        o.href = o.href + (o.href.indexOf('?') == -1 ? '?' : '&') + "CLK=" + clkValue;
    }
}

