/*
 * mtcTabs- A jQuery plugin for responsive tabs
 * Version : 1.1
 * Author : Paul McAvoy
 */

/*
 * Things to improve on:
 * Acitve state stays if clicked and window is resized
 * Use of chosen, might need to allow for chosen options to be applied
 * height stay same when animating
 * ability to disable some of the tabs programmatically
 * destroy on breakpoint
 * change animtion of accordion to open and close at sametime - option
 */

;(function ($, window, document, undefined) {

    "use strict";

    // Create the defaults once
    var plugin_name = 'mtcTabs',
        increment_id = 0;

    // The actual plugin constructor
    function mtcTabs(element, options) {
        this.element = element;
        this.$this = $(this.element);
        this.defaults = {
            active: 1,
            animation_type: 'fade', // fade, slide
            content_selector: '.tabContent',
            content_wrap_selector: '.tabsContentWrap',
            easing: 'linear',
            mode: 'tabs', // tabs, accordion, select
            responsive: [],
            speed: 400,
            afterChange: '',
            beforeChange: '',
            onChange: '',
            onDestroy: '',
            onInit: ''
        };
        this.all_settings = $.extend(true, {}, this.defaults, options);
        this.settings = $.extend({}, this.defaults, options);
        this._defaults = this.defaults;
        this._name = plugin_name;
        this.active_index = this.settings.active;

        this.init();
    }

    // Avoid mtcTabs.prototype conflicts
    $.extend(mtcTabs.prototype, {

        checkResponsive: function () {
            // set some vars
            var plugin = this;
            // set the default as tablet
            this.settings = this.all_settings;
            // iterate through settings and find the right one
            if (this.all_settings.responsive) {

                // loop through responsive settings
                $.each(this.all_settings.responsive, function (i, value) {
                    // find one that is active
                    if (matchesMediaQuery(0, value.breakpoint)) {
                        plugin.settings = value.settings;
                    }
                });
            }
        },

        readResponsive: function () {
            // prepare the responsive array
            var plugin = this,
                previous_breakpoints_settings = plugin.all_settings;

            if (plugin.all_settings.responsive) {
                // loop through responsive settings
                $.each(plugin.all_settings.responsive, function (i, value) {
                    // extend breakpoints settings to include ones from previous
                    value.settings = $.extend(true, {}, previous_breakpoints_settings, value.settings);
                    // get rid of responsive duplicate
                    delete value.settings.responsive;
                    // previous now become the current ones
                    previous_breakpoints_settings = value.settings;
                });
            }
        },

        init: function () {
            // set some vars
            var plugin = this,
                $this = plugin.$this;

            // update increment_id
            increment_id += 1;

            // call responsive settings
            plugin.readResponsive();
            plugin.checkResponsive();

            // add class to plugin object div
            $this.addClass('mtcTabsInitialised');
            // add unique id to tabs
            $this.attr('id', 'tabs_' + increment_id);
            // wrap inner content
            $this.wrapInner($('<div />', {
                class: plugin.settings.content_wrap_selector.replace('.', '')
            }));
            // define $tabs_content object for use within the plugin
            plugin.$tabs_content = $this.find('> ' + plugin.settings.content_wrap_selector + ' > ' + plugin.settings.content_selector);

            // update active to 0 index
            plugin.active_index = plugin.settings.active - 1;

            // build navigation and set open element
            plugin.setOpen();
            plugin.buildNavigation();

            plugin.runCallbackFunction('onInit');

            // resize function
            $(window).on('resize.mtcTabs', debouncer(function () {
                plugin.resize();
            }));
        },

        resize: function () {
            // set some vars
            var plugin = this,
                cached_mode = plugin.settings.mode;
            // update responsive settings
            plugin.checkResponsive();

            // if mode changes. rebuild the nav
            if (cached_mode !== plugin.settings.mode) {
                // destroy navigation element before destroying the tabs
                plugin.destroy('navigation', cached_mode);
                /* if cached mode does not equal tabs then we need
                 * to run the destroy for that mode
                 */
                if (cached_mode !== 'tabs') {
                    plugin.destroy(cached_mode);
                }
                plugin.buildNavigation();
            }
        },

        buildNavigation: function () {
            var plugin = this;

            // fire function depending on which mode is set
            switch (plugin.settings.mode) {
                case 'accordion':
                    plugin.accordionNav();
                    break;
                case 'select':
                    plugin.selectNav();
                    break;
                default:
                    // tabs is the default
                    plugin.tabNav();
                    break;
            }

            // add unique id to content section
            plugin.$tabs_content.each(function (i) {
                // add unique ids to element
                $(this).attr('data-tab-id', '#tab_' + i);
            });

            plugin.switchTrigger();
        },

        setOpen: function () {
            // some vars
            var plugin = this,
                content_container = plugin.$tabs_content,
                queries = plugin.getQueryString(),
                query_tab_eq = queries.tab,
                query_active_tab = queries.active_tab - 1,
                this_id = plugin.$this.attr('id');

            // if there is a query string set the active tab
            if (query_active_tab > -1 && (this_id === 'tabs_' + query_tab_eq)) {
                plugin.active_index = query_active_tab;
            }

            // set active element
            if (plugin.settings.active > 0) {
                content_container.eq(plugin.active_index).addClass('open').show();
            }
        },

        tabNav: function () {
            // some vars
            var plugin = this,
                $this = plugin.$this,
                content_container = plugin.$tabs_content,
                tab_nav = $('<nav />', {
                    class: 'tabsNav'
                }).prepend('<ul />'); // create nav element and prepend ul to it

            // add nav to the page
            $this.prepend(tab_nav);
            // add links to the nav element
            content_container.each(function (i) {
                var $tab_title = $(this).find('> .tabTitle'),
                    tab_title_contents = $tab_title.html(), // get titles from tabs
                    is_click_thru = $tab_title.data('click-thru') !== undefined && $tab_title.data('click-thru') === true;

                /* if tab_title doesn't contain an a tag then we want to wrap
                 * it with a tabable a tag, otherwise we just use the
                 * title as it is and add a class to the parent
                 */
                if (is_click_thru !== true) {
                    tab_title_contents = '<li><a href="#tab_' + i + '">' + tab_title_contents + '</a></li>';
                } else {
                    tab_title_contents = '<li class="clickThruLink">' + tab_title_contents + '</li>';
                }

                // find nav and nav elements to it.
                tab_nav
                    .find('ul')
                    .append(tab_title_contents);
                // add active class to specified active nav element
                tab_nav.find('li').eq(plugin.getActive()).addClass('active');
            });

            /* access to nav within callback functions has limitations
             * as nav is built dynamically
             */
            plugin.$nav = tab_nav;
        },

        tabTrigger: function (nav_element) {
            // some vars
            var plugin = this;

            // on click of the tabs nav element
            nav_element.find('a').on('click.mtcTabs', function (e) {
                var clicked = $(this),
                    element_id = clicked.attr('href'),
                    content_container = clicked.closest('.tabsNav').next(),
                    $parent = clicked.parent();

                /* if parent doesn't have clickThruLink then change the tab
                 * else we go to the link in the tab
                 */
                if (!$parent.hasClass('clickThruLink')) {
                    // preventDefault
                    e.preventDefault();

                    // do below only if this nav element isn't the active one
                    if (!$parent.hasClass('active')) {
                        // set active nav element
                        clicked.parent().addClass('active').siblings().removeClass('active');
                        // run beforeChange function before running any animation
                        plugin.runCallbackFunction('beforeChange');
                        // fire function depending on which mode is set
                        plugin.switchAnimationType(element_id, content_container);
                    }
                }
            });
        },

        accordionNav: function (active) {
            // some vars
            var plugin = this,
                content_container = plugin.$tabs_content;

            // add nav element to before each content_container
            content_container.each(function (i) {
                var $this = $(this),
                    $tab_title = $(this).find('> .tabTitle'),
                    tab_title_contents = $tab_title.html(), // get titles from tabs
                    is_click_thru = $tab_title.data('click-thru') !== undefined && $tab_title.data('click-thru') === true,
                    tab_nav = $('<nav />', {
                        class: 'tabsNav'
                    }); // create nav element

                /* if tab_title doesn't contain an a tag then we want to wrap
                 * it with a tabable a tag, otherwise we just use the
                 * title as it is
                 */
                if (is_click_thru !== true) {
                    tab_title_contents = '<a href="#tab_' + i + '">' + tab_title_contents + '</a>';
                } else {
                    tab_nav.addClass('clickThruLink');
                }

                // wrap tab content in container
                $this.wrap('<div class="accordionWrap" />');
                // build nav element
                tab_nav
                    .insertBefore($this)
                    .prepend(tab_title_contents);
                // add active class to specified active nav element
                if (i === plugin.getActive()) {
                    tab_nav.addClass('active');
                }
            });
        },

        accordionTrigger: function (nav_element) {
            // set some vars
            var plugin = this;

            // on click of the tabs nav element
            nav_element.find('a').on('click.mtcTabs', function (e) {

                var clicked = $(this),
                    element_id = clicked.attr('href'),
                    content_container = clicked.closest('.accordionWrap').parent(),
                    $parent = clicked.parent();

                /* if parent doesn't have clickThruLink then change the tab
                 * else we go to the link in the tab
                 */
                if (!$parent.hasClass('clickThruLink')) {
                    // preventDefault
                    e.preventDefault();

                    // do below only if this nav element isn't the active one
                    if (!$parent.hasClass('active')) {
                        // set active nav element
                        content_container.find('.tabsNav').removeClass('active');
                        clicked.parent().addClass('active');
                        // run beforeChange function before running any animation
                        plugin.runCallbackFunction('beforeChange');
                        // fire function depending on which mode is set
                        plugin.switchAnimationType(element_id, content_container);
                    } else {
                        // set active nav element
                        content_container.find('.tabsNav').removeClass('active');
                        // run beforeChange function before running any animation
                        plugin.runCallbackFunction('beforeChange');
                        // fire function depending on which mode is set
                        plugin.switchAnimationType(element_id, content_container, true);
                    }
                }
            });
        },

        selectNav: function () {
            // some vars
            var plugin = this,
                $this = plugin.$this,
                content_container = plugin.$tabs_content,
                tab_nav = $('<nav />', {
                    class: 'tabsNav'
                }).prepend('<select />'); // create nav element and prepend select to it

            // add nav to the page
            $this.prepend(tab_nav);
            // add links to the nav element
            content_container.each(function (i) {
                var $tab_title = $(this).find('> .tabTitle'),
                    tab_title_contents = $tab_title.html(), // get titles from tabs
                    is_click_thru = $tab_title.data('click-thru') !== undefined && $tab_title.data('click-thru') === true;

                /* if tab_title doesn't contain an a tag then we want to wrap
                 * it with a tabable option, otherwise we want to wrap it
                 * in an option with a link
                 */
                if (is_click_thru !== true) {
                    tab_title_contents = '<option value="#tab_' + i + '">' + tab_title_contents + '</option>';
                } else {
                    tab_title_contents = '<option value="' + $tab_title.find('a').attr('href') + '">' + tab_title_contents + '</option>';
                }

                // find nav and nav elements to it.
                tab_nav
                    .find('select')
                    .append(tab_title_contents);
                // set active option
                tab_nav
                    .find('option')
                    .eq(plugin.getActive())
                    .attr('selected', 'selected');
            });

            // initiate chosen
            tab_nav.find('select').chosen({
                disable_search_threshold: 8
            });
        },

        selectTrigger: function (nav_element) {
            // set some vars
            var plugin = this;

            // on change of the select
            nav_element.find('select').on('change.mtcTabs', function (e) {
                // preventDefault
                e.preventDefault();

                var clicked = $(this),
                    element_id = clicked.val(),
                    content_container = clicked.closest('.tabsNav').next();

                /* if element_id doesn't contain #tab_ at start then we want to
                 * go to the link
                 */
                if (element_id.indexOf('#tab_') !== 0) {
                    window.location.href = element_id;
                } else {
                    // run beforeChange function before running any animation
                    plugin.runCallbackFunction('beforeChange');
                    // fire function depending on which mode is set
                    plugin.switchAnimationType(element_id, content_container);
                }
            });
        },

        switchTrigger: function () {
            // set some vars
            var plugin = this,
                $this = plugin.$this,
                content_container = plugin.$tabs_content,
                current_mode = plugin.settings.mode,
                nav_element = $this.children('.tabsNav');

            if (current_mode === 'accordion') {
                nav_element = content_container.siblings('.tabsNav');
            }

            switch (current_mode) {
                case 'accordion':
                    plugin.accordionTrigger(nav_element);
                    break;
                case 'select':
                    plugin.selectTrigger(nav_element);
                    break;
                default:
                    plugin.tabTrigger(nav_element);
                    break;
            }
        },

        switchAnimationType: function (element_id, content_container, close_active) {
            // set some vars
            var plugin = this,
                $content_container = content_container;

            // if accordion mode is active change the content_container
            if (plugin.settings.mode === 'accordion') {
                $content_container = $content_container.find('> .accordionWrap');
            }

            // force accordion animation_type to slide if animation_type is set to fade
            if (plugin.settings.mode === 'accordion' && plugin.settings.animation_type === 'fade') {
                plugin.settings.animation_type = 'slide';
            }

            switch (plugin.settings.animation_type) {
                case 'slide':
                    plugin.slide(element_id, $content_container, close_active);
                    break;
                default:
                    // fade is the default
                    plugin.fade(element_id, $content_container);
                    break;
            }
        },

        slide: function (element_id, content_container, close_active) {
            // set some vars
            var plugin = this;
            // slideFade all siblings
            content_container
                .find('> .open').removeClass('open')
                .stop(true, true)
                .slideFadeToggle(plugin.settings.speed, plugin.settings.easing);

            plugin.runCallbackFunction('onChange');

            // close_active does not equal true then open the one clicked
            if (close_active !== true) {
                // slideFade clicked element
                content_container
                    .find('> [data-tab-id="' + element_id + '"]')
                    .stop(true, true)
                    .slideFadeToggle(plugin.settings.speed, plugin.settings.easing, function () {
                        // run afterChange function
                        plugin.runCallbackFunction('afterChange');
                    })
                    .addClass('open');
            }
        },

        fade: function (element_id, content_container, close_active) {
            // set some vars
            var plugin = this;

            // hide open content
            content_container
                .find('> .open')
                .removeClass('open')
                .hide();

            plugin.runCallbackFunction('onChange');

            // fade in clicked element
            content_container
                .find('> [data-tab-id="' + element_id + '"]')
                .stop(true, true)
                .fadeIn(plugin.settings.speed, plugin.settings.easing, function () {
                    // run afterChange function
                    plugin.runCallbackFunction('afterChange');
                })
                .addClass('open');
        },

        getActive: function () {
            // set some vars
            var plugin = this,
                active = plugin.$this.find('.open');

            // determine the mode and change how the active element its found
            switch (plugin.settings.mode) {
                case 'accordion':
                    return plugin.active_index = active.parent('.accordionWrap').index();
                default:
                    // tabs and select have same markup
                    return plugin.active_index = active.index();
            }
        },

        runCallbackFunction: function (functionName) {
            // set some vars
            var plugin = this;
            // check if a function and run it
            if (typeof plugin.settings[functionName] === 'function') {
                plugin.settings[functionName](plugin);
            }
        },

        getQueryString: function () {
            // set some vars
            var query_string = window.location.search,
                querys = {}, q;

            // if query_string contains queries
            if (query_string !== '') {
                query_string = query_string.replace('?', '').split('&');

                // go over each query and pull out the relevant ones
                for(var i = 0; i < query_string.length; i++) {
                    q = query_string[i].split('=');
                    if (q[0] === 'tab' || q[0] === 'active_tab') {
                        querys[q[0]] = q[1];
                    }
                }

                // if querys is not empty then return it
                if (!$.isEmptyObject(querys)) {
                    return querys;
                } else {
                    // else return false
                    return false;
                }

            } else {
                // else return false
                return false;
            }
        },

        destroy: function (mode, cached_mode) {
            // set some vars
            var plugin = this,
                $this = plugin.$this,
                content_container = plugin.$tabs_content,
                new_mode = plugin.settings.mode,
                cached_mode = cached_mode || false,
                $tabs_nav = $this.children('.tabsNav');

            // if accordion cached_mode then we need to find $tabs_nav differently
            if (cached_mode === 'accordion') {
                $tabs_nav = content_container.siblings('.tabsNav');
            }

            switch (mode) {
                case 'navigation':
                    // remove click and chenge events
                    $tabs_nav.find('a, select').off('click.mtcTabs change.mtcTabs');
                    // remove nav from dom
                    $tabs_nav.remove();
                    break;
                case 'accordion':
                    content_container.unwrap('.accordionWrap');
                    break;
                case 'select':
                    $tabs_nav.find('select').chosen('destroy');
                    break;
                default:
                    // remove data-tab-id added by plugin
                    content_container.removeAttr('data-tab-id style');
                    // remove plugin data from trigger
                    $this.removeData('plugin_' + plugin_name);
                    // destroy navigation
                    plugin.destroy('navigation');
                    /* if new mode does not equal tabs then we need
                     * to run the destroy for that mode
                     */
                    if (new_mode !== 'tabs') {
                        plugin.destroy(new_mode);
                    }
                    // unwrap inner content
                    content_container.unwrap();
                    // remove intialised class
                    $this.removeClass('mtcTabsInitialised');

                    $(window).off('resize.mtcTabs');

                    plugin.runCallbackFunction('onDestroy');
                    break;
            }
        }
    });

    // A really lightweight plugin wrapper around the constructor,
    // preventing against multiple instantiations
    $.fn[plugin_name] = function (options) {
        return this.each(function () {
            var plugin, _name;
            plugin = $.data(this, 'plugin_' + plugin_name);

            if (typeof options === 'string') {
                if (plugin !== null) {
                    if (typeof plugin[_name = options] === 'function') {
                        return plugin[_name]();
                    } else {
                        return void 0;
                    }
                } else {
                    return void 0;
                }
            } else if (!plugin) {
                $.data(this, 'plugin_' + plugin_name, new mtcTabs(this, options));
            }
        });
    };

}(jQuery, window, document));
