﻿(function($) {
    //namespace
    var AccordianSlideshow = window.AccordianSlideshow = {};

    AccordianSlideshow.Abstract = {};
    /**
    * For managing options
    * @abstract
    */
    AccordianSlideshow.Abstract.Options = {
        /**
        * @var Object
        */
        options: {},

        /**
        * @param Object options
        * @return void
        */
        setOptions: function(options) {
            $.extend(this.options, options);
        },

        /**
        * @param String key
        * @param Mixed value
        * @return void
        */
        setOption: function(key, value) {
            this.options[key] = value;
        },

        /**
        * @param void
        * @return Object
        */
        getOptions: function() {
            return this.options;
        },

        /**
        * @param String key
        * @param Mixed default
        * @return Mixed
        */
        getOption: function(key, defaultValue) {
            if (!this.objHasAttribute(this.options, key) && !defaultValue) {
                return null;
            } else if (!this.objHasAttribute(this.options, key) && defaultValue && this.options[key]) {
                return defaultValue;
            }
            return this.options[key];
        },

        objHasAttribute: function(obj, attr) {
            for (key in obj) {
                if (attr == key) {
                    return true;
                }
            }
            return false;
        }
    };
})(jQuery);

(function($) {

    var o = AccordianSlideshow.slider = function(el, options) {
        this.$el = $(el);

        var defaultOptions = {
            initialOffset: 10,
            openOffset: 200,
            bumpOffset: 0,
            zIndexFrom: 0,
            slideClass: 'slide',
            slideLinkClass: 'slide-link',
            stackTop: 'left',
            startSlide: 2,
            openAnimateTime: 200,
            closeAnimateTime: 300,
            bumpAnimateTime: 0,
            openEasing: 'easeInSine',
            closeEasing: 'easeOutSine',
            bumpEasing: null,
            animateSlideBackground: true,
            //slideBackgroundPositions: { 0: [0, 100], 1: [0, 100], 2: [0, 100], 3: [0, 100] }, //, 4: [0, 100]
            slideBackgroundOpenAnimateTime: 200,
            slideBackgroundcloseAnimateTime: 300,
            slideBackgroundOpenEasing: 'easeInSine',
            slideBackgroundCloseEasing: 'easeOutSine',
            donePrepCallback: null,
            openOnClick: true,
            openOnMouseEnter: false,
            openOnMouseLeave: false,
            closeOnMouseEnter: false,
            closeOnMouseLeave: false,
            closeOnClick: true,
            slideOpenCallback: null,
            slideCloseCallback: null,
            slideClickCallback: null,
            slideMouseEnterCallback: null,
            slideMouseLeaveCallback: null
        }

        this.setOptions(defaultOptions);
        this.setOptions(options);

        this.slides = $('.' + this.getOption('slideClass'), this.$el);
        this.slidesLen = this.slides.length;
        this.slideCache = {};
        this.counter = 0;
        this.slideLock = false;

        //prepare strip
        _prepShow.call(this);

        //attach events
        _initEvents.call(this);

        //make it visible
        this.$el.css({ visibility: 'visible' });

        //setup listeners

    }, p = o.prototype;

    //add initial styles
    var _prepShow = function() {
        offset = this.getOption('initialOffset');
        initZ = this.getOption('zIndexFrom');
        endOffset = (this.slidesLen - 1) * offset;
        endZ = initZ + this.slidesLen;
        topOrder = this.getOption('stackTop');
        if (topOrder == 'left') {
            myZ = initZ;
        } else if (topOrder == 'right') {
            myZ = endZ;
        }
        for (var i = this.slidesLen; i--; ) {

            $(this.slides[i]).addClass('slide-' + i);
            $(this.slides[i]).data('slideData', { pos: i });

            $(this.slides[i]).css({
                zIndex: myZ,
                left: endOffset
            });

            endOffset = endOffset - offset;

            //decrement the two
            if (topOrder == 'left') {
                myZ = myZ + 1;
            } else if (topOrder == 'right') {
                myZ = myZ - 1;
            }
            //mark all as open
        }

        //close the one after start slide
        _updateSlideCache.call(this, true)
        start = this.getOption('startSlide');
        if (start < this.slidesLen - 1) {
            _toggleSlide.call(this, start, false);
        }

        //call the prep done callback
        var done = this.getOption('donePrepCallback');
        var slidesCopy = this.slides.slice();
        var startSlide = slidesCopy.splice(start, 1);
        if (done != null) {
            done(startSlide, slidesCopy, this.slides);
        }

    }

    var _initEvents = function() {
        var _this = this;
        //prevent the slide links from bubbling
        $("." + this.getOption('slideLinkClass')).bind('click', {}, function() {
            window.location = $(this).attr("href");
            return false;
        });

        if (this.getOption('openOnClick') == true || this.getOption('closeOnClick') == true || this.getOption('slideClickCallback') != null) {
            this.$el.delegate("." + this.getOption('slideClass'), 'click', function(e) { _preOpenSlide.call(_this, e, this); });
        }

        if (this.getOption('openOnMouseEnter') == true || this.getOption('closeOnMouseEnter') == true || this.getOption('slideMouseEnterCallback') != null) {
            this.$el.delegate("." + this.getOption('slideClass'), 'mouseenter', function(e) { _preOpenSlide.call(_this, e, this); });
        }

        if (this.getOption('openOnMouseLeave') == true || this.getOption('closeOnMouseLeave') == true || this.getOption('slideMouseLeaveCallback') != null) {
            this.$el.delegate("." + this.getOption('slideClass'), 'mouseleave', function(e) { _preOpenSlide.call(_this, e, this); });
        }
    }

    var _updateSlideCache = function(start, update) {
        if (update != undefined) {
            var iter = update.length;
            var store = update;
        } else {
            var iter = this.slides.length;
            var store = this.slides;
        }
        for (var i = iter; i--; ) {
            $curr = $(store[i]);
            slidePos = $curr.data('slideData').pos;
            var newValues = {
                zIndex: $curr.css('zIndex'),
                left: $curr.css('left'),
                pos: $curr.position(),
                off: $curr.offset()
            }

            this.slideCache[slidePos] = $.extend(this.slideCache[slidePos], newValues);
            if (start == true) {
                this.slideCache[slidePos] = $.extend(this.slideCache[slidePos], {
                    startPos: $curr.position(),
                    startOffset: $curr.offset(),
                    open: true
                });
            }
        }
        this.counter++;
    }

    var _preOpenSlide = function(event, element) {
        $elm = $(element);
        //only allow non-visible elements to trigger events
        if (!$elm.hasClass('visible')) {
            slidePos = $elm.data('slideData').pos;
            $meData = this.slideCache[slidePos];
            var slidesCopy = this.slides.slice();
            var target = slidesCopy.splice(slidePos, 1);

            callbacks = {
                click: (this.getOption('clickCallback') != null) ? this.getOption('clickCallback') : false,
                mouseenter: (this.getOption('slideMouseEnterCallback') != null) ? this.getOption('slideMouseEnterCallback') : false,
                mouseleave: (this.getOption('slideMouseLeaveCallback') != null) ? this.getOption('slideMouseLeaveCallback') : false,
                open: (this.getOption('slideOpenCallback') != null) ? this.getOption('slideOpenCallback') : false,
                close: (this.getOption('slideCloseCallback') != null) ? this.getOption('slideCloseCallback') : false
            }

            if ($meData.open == true) {
                var dir = 'close';
            } else {
                var dir = 'open';
            }
            if (event.type == 'mouseenter') {
                if (this.getOption(dir + 'OnMouseEnter')) {
                    _toggleSlide.call(this, slidePos, true, [callbacks[dir], callbacks.mouseenter])
                } else if (callbacks.mouseenter != false) {
                    callbacks.mouseenter(slidePos, target, slidesCopy, this.slides)
                }
            } else if (event.type == 'mouseleave') {
                if (this.getOption(dir + 'OnMouseLeave')) {
                    console.log(event.type, dir + 'OnMouseLeave', this.getOption(dir + 'OnMouseLeave'));
                    _toggleSlide.call(this, slidePos, true, [callbacks[dir], callbacks.mouseleave], slidePos)
                } else if (callbacks.mouseleave != false) {
                    callbacks.mouseleave(slidePos, target, slidesCopy, this.slides)
                }
            } else if (event.type == 'click') {
                if (this.getOption(dir + 'OnClick')) {
                    _toggleSlide.call(this, slidePos, true, [callbacks[dir], callbacks.click])
                } else if (callbacks.mouseenter != false) {
                    callbacks.click(slidePos, target, slidesCopy, this.slides)
                }
            }
        }
    }

    var _toggleSlide = function(slide, animoot, fns) {
        if (!this.slideLock) {
            this.slideLock = true;
            $me = $(this.slides[slide]);
            $meData = this.slideCache[slide];
            var move = null;

            //am i open or closed?
            var imOpen = ($meData.open == true) ? true : false;

            var offsetDiff = this.getOption('openOffset') - this.getOption('initialOffset')


            //behavior is different if this is the first slide
            //var visSlide;  
            if (imOpen == true) {
                move = this.slides.slice(slide + 1);
                var moveTo = $meData.startPos.left + offsetDiff;
                var direction = 'close';
                //mark slide - 1 as visible
                $(this.slides).removeClass('visible');
                $(this.slides[slide]).addClass('visible');
                visSlide = slide;
            } else {
                move = this.slides.slice(1, slide);
                addOffset = 0;
                var moveTo = $meData.startPos.left;
                var direction = 'open';
                //mark slide as visible
                $(this.slides).removeClass('visible');
                $me.addClass('visible');
                visSlide = slide;
            }


            moveLen = move.length

            //move the slide background
            if (this.getOption('animateSlideBackground') == true) {
                _moveSlideBackground.call(this, visSlide, true);
            }

            //move me only if i am closed
            if (!imOpen) {
                if ($meData.startPos.left != 0 && direction) {
                    _moveSlide.call(this, slide, $me, moveTo, animoot, direction)
                }
            }

            //move my minions
            if (moveLen > 0) {
                for (var i = moveLen; i--; ) {
                    $unit = $(move[i]);
                    $unitData = this.slideCache[$unit.data('slideData').pos]
                    var slidePos = $unit.data('slideData').pos;
                    var newPos = (direction == 'open') ? $unitData.startPos.left : $unitData.startPos.left + offsetDiff
                    _moveSlide.call(this, slidePos, $unit, newPos, animoot, direction);
                }
            }




            _updateSlideCache.call(this, false, move);


            //issue the callback
            if (fns != undefined) {
                var R = fns.length;
                var slidesCopy = this.slides.slice();
                var opened = slidesCopy.splice($me.data('slideData').pos, 1);
                for (var i = R; i--; ) {
                    if (fns[i] != false) {
                        (fns[i])($me.data('slideData').pos, opened, slidesCopy, this.slides);
                    }
                }
            }
        }
    }

    var _moveSlide = function(slidePos, slide, pos, animoot, direction) {
        var easing = this.getOption(direction + 'Easing');
        this.slideCache[slidePos].open = (direction == 'open') ? true : false;
        var time = this.getOption(direction + 'AnimateTime');
        var _this = this;
        if (animoot == true) {
            slide.animate({ left: pos }, time, easing, function() { _this.slideLock = false; });
        } else {
            slide.css({ left: pos });
            this.slideLock = false;
        }
    }

    var _moveSlideBackground = function(visibleSlidePos, animoot) {

        for (var i = this.slidesLen; i--; ) {
            var $slideImg = $(".slide-background > img", this.slides[i]);
            var bkgdPosition = this.getOption('slideBackgroundPositions')[i];
            var time, easing, newPos;
            if (i == visibleSlidePos) {
                //move to open pos
                time = this.getOption('slideBackgroundOpenAnimateTime');
                easing = this.getOption('slideBackgroundOpenEasing');
                newPos = bkgdPosition[0];
            } else {
                //move to close
                time = this.getOption('slideBackgroundCloseAnimateTime');
                easing = this.getOption('slideBackgroundCloseEasing');
                newPos = bkgdPosition[1];
                //newPos = bkgdPosition[1];
            }

            if (animoot == true) {
                $slideImg.animate({ left: newPos }, time, easing);
                //$slideImg.css({ left: newPos });
            } else {
                $slideImg.css({ left: newPos });
            }
        }
    }


    //extend the object to include the abstract classes
    $.extend(p, AccordianSlideshow.Abstract.Options);

})(jQuery);


    



