﻿/**
 * Carousel
 * @author: pop-id [kh]
 * @description: Carousel
 * @version: 0.1
 * @dependencies: jQuery 1.4.x (or greater)
 * @todo: remove hardcoded values -- pass in as options instead
 */
BF.carousel = function (target, options) {

    var self = this; // scope alias

    this.options = $.extend({
        activeSlide: 'active', // CSS class of the active, or current, slide, @TODO: IMPLEMENT
        animateOnSetup: false, // start off with animation?
        animationDuration: 500, // Time, in milliseconds, it takes for the animation to complete
        appendTo: false, // selector of element (ul/ol) to append content to (if not false expects string)
        autoPlay: true, // should the carousel advance to the next slide automatically
        baseClass: 'carousel', // Base CSS class for carousel
        cufon: true, // are we using Cufon?
        content: window.homeCarouselData, // are the slides already in the DOM, if not this should be an array of slides
        index: 0, // slide to start on / show by default (array style count, 0 is lowest)
        keyboard: true, // use keyboard navigation to invoke previous / next?
        loop: true, //should the carousel loop, i.e. start over, if the user has advanced to the last item
        navClass: 'nav', // nav (pager & prev/next buttons... if they exist)
        nextPrev: true, // create next and previous buttons?
        pager: true, // create a pager - numbered navigation?
        pagerActiveClass: 'on', // active pager CSS class <li>
        replaceExisting: true, // remove existing elements within the target, should only be used if content is dynamic
        restTime: 5000, // time in milliseconds to wait before advancing to next slide. Only applicable if autoPlay is true
        shuffle: false // randomize the order of the carousel
    }, options || {});

    // instance variables
    this.index = this.options.index;
    this.slides = null;
    this.timer = null;
    this.previousIndex = -1;
    this.restTime = this.options.restTime;

    this.target = $(target);
    this.target.addClass(this.options.baseClass);

    // setup elements
    this.setup();

    if (this.options.keyboard) {
        this.keyboardNav();
    }

    // events
    this.target.bind('mouseover mouseenter', function(e) {
        if (self.options.autoPlay) {
            self.stopTimer();
        }
        $.event.trigger("BF:carousel:mouseenter");
    });

    this.target.bind('mouseleave', function(e) {
        if (self.options.autoPlay) {
            self.startTimer();
        }
        $.event.trigger("BF:carousel:mouseleave");
    });



};

BF.carousel.prototype = {

    // animate the slide
    animate: function (index) {
        this.index = index;
        var self = this,
            next = $(this.slides[index]),
            nextTitle = next.find('span.title'),
            nextSub = next.find('span.subtext'),
            previous = $(this.slides),
            previousTitle = previous.find('span.title'),
            previousSub = previous.find('span.subtext');

        // stop the timer
        self.stopTimer();

        // fade out previous slides
        if (this.index !== this.previousIndex) {
            this.previousIndex = index;
            previous.filter(':not(:animated)').animate({
                opacity: 0,
                left: '+=10'
            }, 100, function (e) {
                self.updatePager();
                // reset CSS
                previous.css({ left: 0, opacity: 0 }).hide();
                previousTitle.css({ opacity: 0, left: 0 }).hide();
                previousSub.css({ opacity: 0, left: 0 }).hide();

                // animate in next
                next.css({ opacity: 1 }).show();
                nextTitle.show();
                nextSub.show();

                nextTitle.filter(':not(:animated)').css({ opacity: 0 }).animate({
                    opacity: 1,
                    left: '25'
                }, self.options.animationDuration, function (e) {
                    next.css({ 'z-index': 3 });
                    nextSub.filter(':not(:animated)').animate({
                        opacity: 1,
                        left: '25'
                    }, self.options.animationDuration);
                    previous.dequeue();
                    $.event.trigger("BF:carousel:show", [self.target]); // fire custom event show
                });
            });
        }
        // restart timer
        self.startTimer();
    },

    // Keyboard navigation
    keyboardNav: function () {
        // listening to events on the document isn't necessarily the best idea
        // but it's helpful to have the keyboard shortcuts during debugging/developement
        var self = this;
        $(document).bind('keydown', function (e) {
            e = e || window.event;
            switch (e.keyCode) {
                case 37: // left arrow
                    self.previous();
                    break;
                case 39: // right arrow
                    self.next();
                    break;
            }

        });
    },

    next: function () {
        var next = this.index + 1;
        if (next >= this.slides.length) {
            if (this.options.loop) {
                next = 0;
                this.animate(next);
            }
        } else {
            this.animate(next);
        }
        $.event.trigger("BF:carousel:next"); // fire custom event next
    },

    previous: function () {
        var previous = this.index - 1;
        if (previous < 0) {
            if (this.options.loop) {
                previous = this.slides.length - 1;
                this.animate(previous);
            }
            else { }
        }
        else {
            this.animate(previous);
        }
        $.event.trigger("BF:carousel:previous"); // fire custom event previous
    },

    setup: function () {

        if (typeof this.options.content === 'object' && this.options.content.length > 0) {
            if (this.options.appendTo) {
                this.content = this.target.find(this.options.appendTo);
            } else {
                this.content = $('<ul class="headlines"/>');
            }

            for (var i = 0, l = this.options.content.length; i < l; i++) {
                var listItem = $('<li/>');
                var title = $('<span class="title"/>').text(this.options.content[i].Title).css({ 'opacity': 0, 'display': 'none' });
                listItem.append(title);
                if (this.options.content[i].CTAText !== '') {
                    var sub = $('<span class="subtext"/>').css({ 'opacity': 0, 'display': 'none' });
                    if (this.options.content[i].CTALink !== '') {
                        var cta = $('<a/>').attr('href', this.options.content[i].CTALink).text(this.options.content[i].CTAText);
                        sub.append(cta);
                    } else {
                        sub.text(this.options.content[i].CTAText);
                    }
                    listItem.append(sub);
                }
                this.content.append(listItem);
            }
            // cufon?
            if (this.options.cufon && Cufon) {
                Cufon.refresh('span.title');
            }

        } else if (typeof this.options.content === 'string') {
            this.content = this.target.find(this.options.content);
        } else {
            throw 'Error: Content needs to be either an array of objects, or a selector (string)';
        }

        if (this.options.replaceExisting) {
            this.target.empty();
        }

        this.slides = this.content.children();

        if (this.options.shuffle) {
            this.slides = this.shuffle(this.slides);
        }

        // append content
        this.target.append(this.content);

        if (this.options.pager || this.options.nextPrev) {
            var nav = $('<nav/>');
            this.target.append(nav);

            if (this.options.pager) {
                this.setupPager(nav);
            }

            if (this.options.nextPrev) {
                this.setupNextPrev(nav);
            }
        }

        if (this.options.animateOnSetup && this.slides.length > 1) {
            this.init = true;
            this.animate(this.index);
        } else {
            $(this.slides[this.index]).children().css({ opacity: 1, left: '25px' }).show();
            this.startTimer();
        }

    },

    // previous / next buttons
    setupNextPrev: function (nav) {
        var self = this,
            previous = $('<a class="previous"/>').append('<span>Previous</span>'),
            next = $('<a class="next"/>').append('<span>Next</span>');

        nav.append(previous).append(next);

        previous.bind('click', function (e) {
            e.preventDefault();
            self.previous();
        });

        next.bind('click', function (e) {
            e.preventDefault();
            self.next();
        });
    },

    // paging / pagination / states
    setupPager: function (nav) {
        var self = this,
            pager = $('<ul/>').fadeTo(0, 0);

        for (var i = 0, l = this.slides.length; i < l; i++) {
            var item = $('<li/>');
            var span = $('<span/>').text(i);
            item.append(span);
            if (this.index === i) {
                item.attr('class', this.options.pagerActiveClass);
            }
            pager.append(item);
        }

        nav.append(pager);
        pager.fadeTo(500, 1);

        pager.delegate('li', 'click', function (e) {
            e.preventDefault();
            var index = $(this).index();
            self.animate(index);
        });

        this.pager = pager;

    },

    // shuffle array
    shuffle: function (els) {
        var i = els.length;
        if (i > 0) {
            while (--i) {
                var j = Math.floor(Math.random() * (i + 1));
                var tempi = els[i];
                var tempj = els[j];
                els[i] = tempj;
                els[j] = tempi;
            }
            return els;
        }
        else {
            return false;
        }
    },

    // start the timer
    startTimer: function () {
        var self = this;
        if (this.options.autoPlay) {
            this.timer = setInterval(function () {
                self.next();
            }, this.restTime);
        }
    },

    // stop the timer
    stopTimer: function () {
        clearInterval(this.timer);
        $.event.trigger("BF:carousel:paused", [this.target]); // fire custom event pause
    },

    // update pagination nav
    updatePager: function () {
        var els = this.pager.children();
        els.removeClass(this.options.pagerActiveClass);
        $(els[this.index]).addClass(this.options.pagerActiveClass);
    }

};
