/*
* Moving Boxes v2.0.3
* by Chris Coyier
* http://css-tricks.com/moving-boxes/
*/

(function ($) {
    var timer = true;
    $.movingBoxes = function (el, options) {
        // To avoid scope issues, use 'base' instead of 'this'
        // to reference this class from internal events and functions.
        var base = this;

        // Access to jQuery and DOM versions of element
        base.$el = $(el).addClass('mb-slider');
        base.el = el;

        // Add a reverse reference to the DOM object
        base.$el.data('movingBoxes', base);

        base.init = function () {
            base.options = $.extend({}, $.movingBoxes.defaultOptions, options);

            // Setup formatting (to reduce the amount of initial HTML)
            base.$el.wrap('<div class="movingBoxes mb-wrapper"><div class="mb-scroll" /></div>');

            // defaults
            base.$window = base.$el.parent();
            base.$wrap = base.$window.parent()
				.css({ width: base.options.width }) // override css width
				.prepend('<a class="mb-scrollButtons mb-left"></a>')
				.append('<a class="mb-scrollButtons mb-right"></a><div class="mb-left-shadow"></div><div class="mb-right-shadow"></div>');
            base.$panels = base.$el.find(base.options.panelType).addClass('mb-panel');
            base.runTime = $('.mb-slider').index(base.$el) + 1; // Get index (run time) of this slider on the page
            base.regex = new RegExp('slider' + base.runTime + '=(\\d+)', 'i'); // hash tag regex

            base.initialized = false;
            base.currentlyMoving = false;
            base.curPanel = 1;

            // code to run to update MovingBoxes when the number of panels change
            base.update();

            // Set up click on left/right arrows
            base.$wrap.find('.mb-right').click(function () {
                timer = false;
                base.goForward();
                return false;
            }).end().find('.mb-left').click(function () {
                timer = false;
                base.goBack();
                return false;
            });

            // go to clicked panel
            base.$el.delegate('.mb-panel', 'click', function () {
                timer = false;
                base.change(base.$panels.index($(this)) + 1);
            });

            // Activate moving box on click or when an internal link obtains focus
            base.$wrap.click(function () {
                timer = false;
                base.active();
            });
            base.$panels.delegate('a', 'focus', function () {
                timer = false;
                // focused link centered in moving box
                var loc = base.$panels.index($(this).closest('.mb-panel')) + 1;
                if (loc !== base.curPanel) { base.change(base.$panels.index($(this).closest('.mb-panel')) + 1, {}, false); }
            });

            // Add keyboard navigation
            $(document).keyup(function (e) {
                // ignore arrow/space keys if inside a form element
                if (e.target.tagName.match('TEXTAREA|INPUT|SELECT')) { return; }
                switch (e.which) {
                    case 39: case 32: // right arrow & space
                        timer = false;
                        if (base.$wrap.is('.mb-active-slider')) {
                            base.goForward();
                        }
                        break;
                    case 37: // left arrow
                        timer = false;
                        if (base.$wrap.is('.mb-active-slider')) {
                            base.goBack();
                        }
                        break;
                }
            });
            var startPanel = 1;
            setTimeout(function () {
                base.change(startPanel, function () {
                    // Bind Events
                    $.each('initialized.movingBoxes initChange.movingBoxes beforeAnimation.movingBoxes completed.movingBoxes'.split(' '), function (i, o) {
                        var evt = o.split('.')[0];
                        if ($.isFunction(base.options[evt])) {
                            base.$el.bind(o, base.options[evt]);
                        }
                    });
                    base.initialized = true;
                    base.$el.trigger('initialized.movingBoxes', [base, startPanel]);
                });
                base.auto();
            }, base.options.speed * 2);
        };
        base.auto = function () {
            if (timer) {
                base.goForward();
                setTimeout(function () {
                    base.auto();
                }, 4000);
            }
        };
        base.update = function () {
            var t;
            // Set up panes & content sizes; default: panelWidth = 50% of entire width
            base.$panels = base.$el.find(base.options.panelType)
				.addClass('mb-panel')
				.css({ width: base.options.width * base.options.panelWidth })
            // inner wrap of each panel
				.each(function () {
				    if ($(this).find('.mb-inside').length === 0) {
				        $(this).wrapInner('<div class="mb-inside" />');
				    }
				});
            base.totalPanels = base.$panels.length;

            // save 'cur' numbers (current larger panel size), use stored sizes if they exist
            t = base.$panels.eq(base.curPanel - 1);
            base.curWidth = 650; // base.curWidth || t.outerWidth();
            base.curImgWidth = 600; // base.curImgWidth || t.find('img').outerWidth(true);
            base.curImgHeight = 350; // base.curImgHeight || base.curImgWidth / base.options.imageRatio; // set images fit a 4:3 ratio

            // save 'reg' (reduced size) numbers
            base.regWidth = base.curWidth * base.options.reducedSize;
            base.regImgWidth = base.curImgWidth * base.options.reducedSize;
            base.regImgHeight = base.curImgHeight * base.options.reducedSize;

            // set image heights so base container height is correctly set
            base.$panels
				.css({ width: base.curWidth, fontSize: '1em' }) // make all panels big
				.find('img').css({ height: base.curImgHeight, width: base.curImgWidth });

            // save each panel height... script will resize container as needed
            base.heights = base.$panels.map(function (i, e) { return $(e).outerHeight(true); }).get();

            base.returnToNormal(base.curPanel, true); // resize new panel
            base.growBigger(base.curPanel, true);

            // make base container wide enough to contain all the panels
            base.$el.css({
                position: 'absolute',
                // add a bit more width to each box (100px should cover margin/padding, etc; then add 1/2 overall width in case only one panel exists
                width: (base.curWidth + 100) * base.totalPanels + (base.options.width - base.curWidth) / 2,
                height: Math.max.apply(this, base.heights) + 10
            });
            base.$window.css({ height: (base.options.fixedHeight) ? Math.max.apply(this, base.heights) : base.heights[base.curPanel - 1] });

            //            base.$window.css({ opacity: 0.5 });

            // add padding so scrollLeft = 0 centers the left-most panel (needed because scrollLeft cannot be < 0)
            base.$panels.eq(0).css({ 'margin-left': (base.options.width - base.curWidth) / 2 });

            base.buildNav();

            base.change(base.curPanel, {}, false); // initialize from first panel... then scroll to start panel

        };

        // Creates the numbered navigation links
        base.buildNav = function () {

            base.$navLinks = {};
            if (base.$nav) { base.$nav.remove(); }

            if (base.options.buildNav && (base.totalPanels > 1)) {
                base.$nav = $('<div class="mb-controls"><a class="mb-testing"></a></div>').appendTo(base.$wrap);
                var j, a = '',
				navFormat = $.isFunction(base.options.navFormatter),
                // need link in place to get CSS properties
				hiddenText = parseInt(base.$nav.find('.mb-testing').css('text-indent'), 10) < 0;
                base.$panels.each(function (i) {
                    j = i + 1;
                    a += '<a href="#" class="mb-panel' + j;
                    // If a formatter function is present, use it
                    if (navFormat) {
                        var tmp = base.options.navFormatter(j, $(this));
                        a += (hiddenText) ? ' ' + base.options.tooltipClass + '" title="' + tmp : '';
                        a += '">' + tmp + '</a> ';
                        // Add formatting to title attribute if text is hidden
                    } else {
                        a += '">' + j + '</a> ';
                    }
                });
                base.$navLinks = base.$nav
					.html(a)
					.find('a').bind('click', function () {
					    base.change(base.$navLinks.index($(this)) + 1);
					    return false;
					});
            }
        };

        // Resize panels to normal
        base.returnToNormal = function (num, quick) {
            base.$panels.not(':eq(' + (num - 1) + ')')
				.removeClass(base.options.currentPanel)
				.animate({ width: base.regWidth, fontSize: base.options.reducedSize + 'em' }, (quick) ? 0 : base.options.speed)
				.find('img').animate({ width: base.regImgWidth, height: base.regImgHeight }, (quick) ? 0 : base.options.speed);

        };

        // Zoom in on selected panel
        base.growBigger = function (num, quick) {
            base.$panels.eq(num - 1)
			.addClass(base.options.currentPanel)
			.animate({ width: base.curWidth, fontSize: '1em' }, (quick) ? 0 : base.options.speed)
			.find('img').animate({ width: base.curImgWidth, height: base.curImgHeight }, (quick) ? 0 : base.options.speed, function () {
			    // completed event trigger
			    // even though animation is not queued, trigger is here because it is the last animation to complete

			    if (base.initialized) { base.$el.trigger('completed.movingBoxes', [base, num]); }
			});

        };

        // go forward/back
        base.goForward = function () { base.change(base.curPanel + 1); };

        base.goBack = function () { base.change(base.curPanel - 1); };

        // Change view to display selected panel
        base.change = function (curPanel, callback, flag) {

            // make sure it's a number and not a string
            curPanel = parseInt(curPanel, 10);

            if (base.initialized) {
                // make this moving box active
                base.active();
                // initChange event - has extra parameter with targeted panel (not cleaned)
                base.$el.trigger('initChange.movingBoxes', [base, curPanel]);
            }

            // psuedo wrap - it's a pain to clone the first & last panel then resize them correctly while wrapping AND make it look good
            if (base.options.wrap) {
                if (curPanel < 1) { curPanel = base.totalPanels; }
                if (curPanel > base.totalPanels) { curPanel = 1; }
            } else {
                if (curPanel < 1) { curPanel = 1; }
                if (curPanel > base.totalPanels) { curPanel = base.totalPanels; }
            }

            // don't do anything if it's the same panel
            if (base.initialized && base.curPanel == curPanel && !flag) { return false; }

            // abort if panel is already animating
            if (!base.currentlyMoving) {
                base.currentlyMoving = true;

                // center panel in scroll window
                var ani, leftValue = base.$panels.eq(curPanel - 1).position().left - (base.options.width - base.curWidth) / 2;
                // when scrolling right, add the difference of the larger current panel width
                if (curPanel > base.curPanel) { leftValue -= (base.curWidth - base.regWidth); }

                ani = (base.options.fixedHeight) ? { scrollLeft: leftValue} : { scrollLeft: leftValue, height: base.heights[curPanel - 1] };

                // before animation trigger
                if (base.initialized) { base.$el.trigger('beforeAnimation.movingBoxes', [base, curPanel]); }

                // animate the panels
                base.$window.animate(ani,
					{
					    queue: false,
					    duration: base.options.speed,
					    easing: base.options.easing,
					    complete: function () {
					        base.curPanel = curPanel;
					        if (base.initialized) {
					            //								base.$panels.eq(curPanel - 1).find('a').focus();
					            base.$window.scrollTop(0); // Opera fix - otherwise, it moves the focus link to the middle of the viewport
					        }
					        base.currentlyMoving = false;
					        if (typeof (callback) === 'function') { callback(base); }
					    }
					}
				);

                base.returnToNormal(curPanel);
                base.growBigger(curPanel);
                if (base.options.hashTags) { base.setHash(curPanel); }
            }
            base.$wrap.find('.mb-controls a')
				.removeClass(base.options.currentPanel)
				.eq(curPanel - 1).addClass(base.options.currentPanel);
        };

        // get & set hash tags
        base.getHash = function () {
            var n = window.location.hash.match(base.regex);
            return (n === null) ? '' : parseInt(n[1], 10);
        };

        base.setHash = function (n) {
            var s = 'slider' + base.runTime + "=",
				h = window.location.hash;
            if (typeof h !== 'undefined') {
                window.location.hash = (h.indexOf(s) > 0) ? h.replace(base.regex, s + n) : h + "&" + s + n;
            }
        };

        // Make moving box active (for keyboard navigation)
        base.active = function (el) {
            $('.mb-active-slider').removeClass('mb-active-slider');
            base.$wrap.addClass('mb-active-slider');
        };

        // get: var currentPanel = $('.slider').data('movingBoxes').currentPanel();  // returns # of currently selected/enlarged panel
        // set: var currentPanel = $('.slider').data('movingBoxes').currentPanel(2, function(){ alert('done!'); }); // returns and scrolls to 2nd panel
        base.currentPanel = function (panel, callback) {
            if (typeof (panel) !== 'undefined') {
                base.change(panel, callback); // parse in case someone sends a string
            }
            return base.curPanel;
        };

        // Run initializer
        base.init();
    };

    $.movingBoxes.defaultOptions = {
        // Appearance
        startPanel: 2,         // start with this panel
        width: 948,       // overall width of movingBoxes
        panelWidth: 0.5,       // current panel width adjusted to 50% of overall width
        reducedSize: 1,       // non-current panel size: 80% of panel size
        imageRatio: 4 / 3,       // Image ratio set to 4:3
        fixedHeight: true,     // if true, slider height set to max panel height; if false, slider height will auto adjust.

        // Behaviour
        speed: 300,       // animation time in milliseconds
        hashTags: true,      // if true, hash tags are enabled
        wrap: true,     // if true, the panel will "wrap" (it really rewinds/fast forwards) at the ends
        buildNav: false,     // if true, navigation links will be added
        navFormatter: null,      // function which returns the navigation text for each panel
        easing: 'swing',   // anything other than "linear" or "swing" requires the easing plugin

        // Selectors & classes
        currentPanel: 'current', // current panel class
        tooltipClass: 'tooltip', // added to the navigation, but the title attribute is blank unless the link text-indent is negative
        panelType: '> div',   // selector to find the immediate (">") children "div" of the movingBoxes object)

        // Callbacks
        initialized: null,   // callback when MovingBoxes has completed initialization
        initChange: null,   // callback upon change panel initialization
        beforeAnimation: null,   // callback before any animation occurs
        completed: function (e, slider, tar) {
            $(".mb-left").show();
            $(".mb-right").show();
            if (tar == slider.totalPanels) {
                $(".mb-right").hide();
            }
            if (tar == 1) {
                $(".mb-left").hide();
            }
        }
    };

    $.fn.movingBoxes = function (options, callback) {
        var num, mb = this.data('movingBoxes');
        return this.each(function () {
            // initialize the slider but prevent multiple initializations
            if ((typeof (options)).match('object|undefined')) {
                if (mb) {
                    mb.update();
                } else {
                    (new $.movingBoxes(this, options));
                }
            } else if (/\d/.test(options) && !isNaN(options) && mb) {
                num = (typeof (options) === "number") ? options : parseInt($.trim(options), 10); // accepts "  4  "
                // ignore out of bound panels
                if (num >= 1 && num <= mb.totalPanels) {
                    mb.change(num, callback); // page #, autoplay, one time callback
                }
            }
        });
    };

    // Return the movingBoxes object
    $.fn.getMovingBoxes = function () {
        return this.data('movingBoxes');
    };

})(jQuery);
