/**
 *
 * @id              $Id: fcl.tabs.js 1581 2010-09-14 03:56:13Z blundenr $
 * @author          Ryan Blunden
 * @modifiedby      $LastChangedBy: blundenr $
 * @copyright       Copyright Flight Centre Ltd. All rights reserved.
 * @version         $Revision: 1581 $
 * @lastmodified    $Date: 2010-09-14 13:56:13 +1000 (Tue, 14 Sep 2010) $
 * @requires        FCL, FCL.UTIL
 */

(function($)
{
    FCL.UTIL.handleErrors(FCL.TABS);

    // Create jQuery tabs API
    $.fn.fclTabs = function(options)
    {
        // Define list of public methods
        var getters = ['showTab'];

        // Create array of arguments from the first argument onwards (as arg 0 is options)
        // We need these in case one of our getters requires additional arguments
        var otherArgs = Array.prototype.slice.call(arguments, 1);

        // If the value for options matches an item in getters, then because of the naming
        // convention of adding a "_" before the name of each getter, call this function,
        // setting the context to FCL.TABS (so "this" refers to FCL.TABS) and send any
        // additional arguments (if any) to this function
        if ($.inArray(options, getters) > -1)
        {
            return FCL.TABS['_' + options].apply(FCL.TABS, [this[0]].concat(otherArgs));
        }

        // Loop through all element instances, ensuring we return the instance so the call
        // to "fclTabs" is chainable
        return this.each(function()
        {
            // If options is a string, then this is a call to a public method
            if (typeof options == 'string')
            {
                FCL.TABS['_' + options].apply(FCL.TABS, [this].concat(otherArgs));
            }
            // Otherwise, this is a standard call to initialise the tabs with a set of options
            else
            {
                FCL.TABS.init(this, options || {});
            }
        });
    };

    /**
     * Basic tabs jQuery plugin - see code for private methods documentation.
     * @class
     * @static
     */
    FCL.TABS =
    {
        /**
         * Name of this class (used for error handling and/or debugging purposes)
         * @type String
         */
        name: 'FCL.TABS',

        /**
         * Initialise tabs
         * @param {Object} [options]
         * @param {Boolean} [options.persistent=false] Store the last opened tab and set this to be the opened tab on next page load
         * @param {String} [options.cookieTitle="tabCookie"] Set the name of the tab cookie for persistent tabs
         * @param {Number} [options.selectedTabIndex=0] Zero index tab number that is open by default
         * @param {Boolean} [options.sameHeight=false] Set to true if you want height of tab container to remain constant
         * @example
         * // To enable tabs
         * $('#tabs').fclTabs(options);
         *
         * // To show the third tab
         * $('#tabs a:nth(2)').fclTabs.showTab();
         */
        init: function(tabs, options)
        {
            var $tabs = (typeof tabs.jquery == 'undefined' ? $(tabs) : tabs);
            var selectedTabIndex = 0;
            var defaults =
            {
                persistent: false,
                cookieTitle: 'tabCookie',
                selectedTabIndex: 0,
                sameHeight: false
            };
            var options = $.extend(defaults, options);

            /*___ DETERMINE SELECTED TAB INDEX ___*/

            // See if we're meant to show tabs as they were last viewed
            if(options.persistent)
            {
                // Will fail without $.cookie plugin or if no cookie value is stored
                try
                {
                    var $targetTab = $tabs.find('a[href="'+ $.cookie(options.cookieTitle) +'"]');
                    if($targetTab.length > 0)
                    {
                        options.selectedTabIndex = $($targetTab.attr('href')).index();
                    }
                }
                catch(e){}
            }

            options.selectedTabIndex = (typeof options.selectedTabIndex == 'undefined') ? 0 : parseInt(options.selectedTabIndex, 10);

            // Check to see if supplied tab index is valid
            if($tabs.find('a:eq('+ options.selectedTabIndex +')').length === 0)
            {
                options.selectedTabIndex = 0;
            }

            if(options.sameHeight)
            {
                this._setupChangeListener();
            }

            $tabs.find('a').each(function()
            {
                // Only go to load tab content if href is a hash (honour normal links)
                if(FCL.TABS._isContentTab($(this)) == false)
                {
                    return;
                }

                if(options.sameHeight)
                {
                    $($(this).attr('href')).tabContentChange(function()
                    {
                        FCL.TABS._refreshTabs($tabs);
                    });
                }

                // Tab click shows selected tab and remembers tab if persistent is set to true
                $(this).bind('click', function(e)
                {
                    e.preventDefault();
                    FCL.TABS._showTab(this);

                    if(options.persistent)
                    {
                        $.cookie(options.cookieTitle, $(this).attr('href'));
                    }
                });
            });

            if(options.sameHeight)
            {
                this._refreshTabs($tabs);
            }

            $tabs.find('a:eq('+ options.selectedTabIndex +')').trigger('click');

           return $tabs;
        },

        /**
         * Determine if current tab is a content tab or link to another page
         * @private
         * @param {jQuery} $tab
         * @returns {Boolean}
         */
        _isContentTab: function($tab)
        {
            return ($tab.attr('href').indexOf('#') === 0);
        },

        /**
         * Setup "tabContentChange" listener to detect when the content of a tab changes
         * This is useful when "sameHeight" is true and you want the vertical size of the tabs to change when
         * new content is loaded in via AJAX for example.
         * @private
         * @param {jQuery} $tabs
         * @returns {jQuery} jQuery element selector
         */
        _setupChangeListener: function($tabs)
        {
            var interval;

            $.fn.tabContentChange = function(fn)
            {
                return this.bind('tabContentChange', fn);
            };

            $.event.special.tabContentChange =
            {
                setup: function(data, namespaces)
                {
                    var self = this,
                        $this = $(this),
                        $originalContent = $this.text();
                    interval = setInterval(function()
                    {
                        if($originalContent != $this.text())
                        {
                                $originalContent = $this.text();
                                $.event.special.tabContentChange.handler.call(self);
                        }
                    },500);
                },
                teardown: function(namespaces)
                {
                    clearInterval(interval);
                },
                handler: function(event)
                {
                    $.event.handle.call(this, { type:'tabContentChange' });
                }
            };

            return $tabs;
        },

        /**
         * Determine height of each tab and make tab container height the height of the highest tab
         * @private
         * @param {jQuery} $tabs
         * returns {jQuery} jQuery element selector
         */
        _refreshTabs: function($tabs)
        {
            // Figure out max height of a tab and set container to be that height
            var maxTabHeight = 0;
            $tabs.find('a').each(function(e)
            {
                if(FCL.TABS._isContentTab($(this)) == false)
                {
                    return;
                }

                var $tabContent =  $($(this).attr('href'));
                $tabContent.css({ height: 'auto'});
                maxTabHeight = ($tabContent.height() > maxTabHeight) ? $tabContent.height() : maxTabHeight;
            });

            // Apply max height to all tabs
            $tabs.find('a').each(function(e)
            {
                if(FCL.TABS._isContentTab($(this)) == false)
                {
                    return;
                }

                $($(this).attr('href')).css('height', maxTabHeight);
            });

            return $tabs;
        },

        /**
         * Show the selected tab
         * @param {jQuery} $tab
         * @returns {jQuery} jQuery element selector
         * @private
         */
        _showTab: function(tab)
        {
            var $tab = $(tab);

            // The tabs can't be nested higher than 2 levels within their container
            var $tabsParent = $tab.parent().parent();
            $currentSelectedTab = $tabsParent.find('.selected');
            $tabContent = $($tab.attr('href'));

            // Hide all tabs
            $tabsParent.find('a').each(function()
            {
               if(FCL.TABS._isContentTab($(this)) == false)
                {
                    return;
                }

                if($(this).attr('href') != $tab.attr('href'))
                {
                    $($(this).attr('href')).addClass('hide');
                }
            });

            $currentSelectedTab.removeClass('selected');
            $tab.addClass('selected');
            // Set new tab as selected and show tab content
            if($tabContent.attr('class').indexOf('hide') > -1 || $tabContent.attr('class').indexOf('hidden') > -1)
            {
                $tabContent.css('opacity', 0).removeClass('hide').removeClass('hidden');
                $tabContent.animate(
                {
                    opacity: 1
                }, 350);
            }

            return $tab;
        }
    };
})(jQuery);
