nikki / LiteAccordion

A lightweight horizontal accordion plugin for jQuery.
MIT License
224 stars 130 forks source link

Fix onmouseover animation glitch #71

Open mociepka opened 11 years ago

mociepka commented 11 years ago

Patch prevents animation start when slide is selected.

Casper1131 commented 11 years ago

/*****! *

;(function($) {

var LiteAccordion = function(elem, options) {

    var defaults = {
        containerWidth : 960,                   // fixed (px)
        containerHeight : 320,                  // fixed (px)
        headerWidth : 48,                       // fixed (px)

        activateOn : 'click',                   // click or mouseover
        firstSlide : 1,                         // displays slide (n) on page load
        slideSpeed : 800,                       // slide animation speed
        onTriggerSlide : function(e) {},        // callback on slide activate
        onSlideAnimComplete : function() {},    // callback on slide anim complete

        autoPlay : false,                       // automatically cycle through slides
        pauseOnHover : false,                   // pause on hover
        cycleSpeed : 6000,                      // time between slide cycles
        easing : 'swing',                       // custom easing function

        theme : 'basic',                        // basic, dark, light, or stitch
        rounded : false,                        // square or rounded corners
        enumerateSlides : false,                // put numbers on slides
        linkable : false                        // link slides via hash
    },

    // merge defaults with options in new settings object
        settings = $.extend({}, defaults, options),

    // 'globals'
        slides = elem.children('ol').children('li'),
        header = slides.children(':first-child'),
        slideLen = slides.length,
        slideWidth = settings.containerWidth - slideLen * settings.headerWidth,

    // public methods
        methods = {

            // start elem animation
            play : function(index) {
                console.log(index);
                var next = core.nextSlide(index && index);

                if (core.playing) return;

                // start autoplay
                core.playing = setInterval(function() {
                    header.eq(next()).trigger('click.liteAccordion');
                }, settings.cycleSpeed);
            },

            // stop elem animation
            stop : function() {
                clearInterval(core.playing);
                core.playing = 0;
            },

            // trigger next slide
            next : function() {
                methods.stop();
                header.eq(core.currentSlide === slideLen - 1 ? 0 : core.currentSlide + 1).trigger('click.liteAccordion');
            },

            // trigger previous slide
            prev : function() {
                methods.stop();
                header.eq(core.currentSlide - 1).trigger('click.liteAccordion');
            },

            // destroy plugin instance
            destroy : function() {
                // stop autoplay
                methods.stop();

                // remove hashchange event bound to window
                $(window).off('.liteAccordion');

                // remove generated styles, classes, data, events
                elem
                    .attr('style', '')
                    .removeClass('liteAccordion basic dark light stitch')
                    .removeData('liteAccordion')
                    .off('.liteAccordion')
                    .find('li > :first-child')
                    .off('.liteAccordion')
                    .filter('.selected')
                    .removeClass('selected')
                    .end()
                    .find('b')
                    .remove();

                slides
                    .removeClass('slide')
                    .children()
                    .attr('style', '');
            },

            // poke around the internals (NOT CHAINABLE)
            debug : function() {
                return {
                    elem : elem,
                    defaults : defaults,
                    settings : settings,
                    methods : methods,
                    core : core
                };
            },
            resize:function(data){
                settings = $.extend({}, defaults, data),
                slides = elem.children('ol').children('li'),
                header = slides.children(':first-child'),
                slideLen = slides.length,
                slideWidth = settings.containerWidth - slideLen * settings.headerWidth;

                core.setStyles();
            }
        },

    // core utility and animation methods
        core = {

            // set style properties
            setStyles : function() {
                // set container height and width, theme and corner style
                elem
                    .width(settings.containerWidth)
                    .height(settings.containerHeight)
                    .addClass('liteAccordion')
                    .addClass(settings.rounded && 'rounded')
                    .addClass(settings.theme);

                // set slide heights
                slides
                    .addClass('slide')
                    .children(':first-child')
                    .height(settings.headerWidth);

                // set slide positions
                core.setSlidePositions();
            },

            // set initial positions for each slide
            setSlidePositions : function() {
                var selected = header.filter('.selected');

                // account for already selected slide
                if (!selected.length) header.eq(settings.firstSlide - 1).addClass('selected');

                header.each(function(index) {
                    var $this = $(this),
                        left = index * settings.headerWidth,
                        margin = header.first().next(),
                        offset = parseInt(margin.css('marginLeft'), 10) || parseInt(margin.css('marginRight'), 10) || 0;

                    // compensate for already selected slide on resize
                    if (selected.length) {
                        if (index > header.index(selected)) left += slideWidth;
                    } else {
                        if (index >= settings.firstSlide) left += slideWidth;
                    }

                    // set each slide position
                    $this
                        .css('left', left)
                        .width(settings.containerHeight)
                        .next()
                            .width(slideWidth - offset)
                            .css({ left : left, paddingLeft : settings.headerWidth });

                    // add number to bottom of tab
                    settings.enumerateSlides && $this.append('<b>' + (index + 1) + '</b>');

                });
            },

            // bind events
            bindEvents : function() {
                // bind click and mouseover events
                if (settings.activateOn === 'click') {
                    header.on('click.liteAccordion', core.triggerSlide);
                } else if (settings.activateOn === 'mouseover') {
                    header.on('click.liteAccordion mouseover.liteAccordion', core.triggerSlide);
                }

                // bind hashchange event
                if (settings.linkable) {
                    $(window).on('hashchange.liteAccordion', function(e) {
                        var url = slides.filter(function() {
                            return $(this).attr('data-slide-name') === window.location.hash.split('#')[1];
                        });

                        // if slide name exists
                        if (url.length) {
                            // trigger slide
                            core.triggerSlide.call(url.children('h2')[0], e);
                        }
                    });
                }

                // pause on hover (can't use custom events with $.hover())
                if (settings.pauseOnHover && settings.autoPlay) {
                    elem
                        .on('mouseover.liteAccordion', function() {
                            core.playing && methods.stop();
                        })
                        .on('mouseout.liteAccordion', function() {
                            !core.playing && methods.play(core.currentSlide);
                        });
                }
            },

            // counter for autoPlay (zero index firstSlide on init)
            currentSlide : settings.firstSlide - 1,

            // next slide index
            nextSlide : function(index) {
                var next = index + 1 || core.currentSlide + 1;

                // closure
                return function() {
                    return next++ % slideLen;
                };
            },

            // holds interval counter
            playing : 0,

            slideAnimCompleteFlag : false,

            // trigger slide animation
            triggerSlide : function(e) {
                var $this = $(this),
                    tab = {
                        elem : $this,
                        index : header.index($this),
                        next : $this.next(),
                        prev : $this.parent().prev().children('h2'),
                        parent : $this.parent()
                    };

                // current hash not correct?
                if (settings.linkable && tab.parent.attr('data-slide-name')) {
                    if (tab.parent.attr('data-slide-name') !== window.location.hash.split('#')[1]) {
                        // exit early and try again (prevents double trigger (issue #60))
                        return window.location.hash = '#' + tab.parent.attr('data-slide-name');
                    }
                }

                // update core.currentSlide
                core.currentSlide = tab.index;

                // reset onSlideAnimComplete callback flag
                core.slideAnimCompleteFlag = false;

                // trigger callback in context of sibling div (jQuery wrapped)
                settings.onTriggerSlide.call(tab.next, $this);

                // animate
                if ($this.hasClass('selected') && $this.position().left < slideWidth / 2) {
                    // animate single selected tab
                    core.animSlide.call(tab);
                } else {
                    // animate groups
                    core.animSlideGroup(tab);
                }

                // stop autoplay, reset current slide index in core.nextSlide closure
                if (settings.autoPlay) {
                    methods.stop();
                    methods.play(header.index(header.filter('.selected')));
                }
            },

            animSlide : function(triggerTab) {
                var _this = this;

                // set pos for single selected tab
                if (typeof this.pos === 'undefined') this.pos = slideWidth;

                // remove, then add selected class
                header.removeClass('selected').filter(this.elem).addClass('selected');

                // if slide index not zero
                if (!!this.index) {
                    this.elem
                        .add(this.next)
                        .stop(true)
                        .animate({
                            left : this.pos + this.index * settings.headerWidth
                        },
                            settings.slideSpeed,
                            settings.easing,
                            function() {
                                // flag ensures that fn is only called one time per triggerSlide
                                if (!core.slideAnimCompleteFlag) {
                                    // trigger onSlideAnimComplete callback in context of sibling div (jQuery wrapped)
                                    settings.onSlideAnimComplete.call(triggerTab ? triggerTab.next : _this.prev.next());
                                    core.slideAnimCompleteFlag = true;
                                }
                            });

                        // remove, then add selected class
                        header.removeClass('selected').filter(this.prev).addClass('selected');

                }
            },

            // animates left and right groups of slides
            animSlideGroup : function(triggerTab) {
                var group = ['left', 'right'];

                $.each(group, function(index, side) {
                    var filterExpr, left;

                    if (side === 'left')  {
                        filterExpr = ':lt(' + (triggerTab.index + 1) + ')';
                        left = 0;
                    } else {
                        filterExpr = ':gt(' + triggerTab.index + ')';
                        left = slideWidth;
                    }

                    slides
                        .filter(filterExpr)
                        .children('h2')
                        .each(function() {
                            var $this = $(this),
                                tab = {
                                    elem : $this,
                                    index : header.index($this),
                                    next : $this.next(),
                                    prev : $this.parent().prev().children('h2'),
                                    pos : left
                                };

                            // trigger item anim, pass original trigger context for callback fn
                            core.animSlide.call(tab, triggerTab);
                        });

                });

                // remove, then add selected class
                header.removeClass('selected').filter(triggerTab.elem).addClass('selected');
            },

            ieClass : function(version) {
                if (version < 7) methods.destroy();
                if (version >= 10) return;
                if (version === 7 || version === 8) {
                    slides.each(function(index) {
                        $(this).addClass('slide-' + index);
                    });
                }

                elem.addClass('ie ie' + version);
            },

            init : function() {
                var ua = navigator.userAgent,
                    index = ua.indexOf('MSIE');

                // test for ie
                if (index !== -1) {
                    ua = ua.slice(index + 5, index + 7);
                    core.ieClass(+ua);
                }

                // init styles and events
                core.setStyles();
                core.bindEvents();

                // check slide speed is not faster than cycle speed
                if (settings.cycleSpeed < settings.slideSpeed) settings.cycleSpeed = settings.slideSpeed;

                // init autoplay
                settings.autoPlay && methods.play();
            }
        };

    // init plugin
    core.init();

    // expose methods
    return methods;

};

$.fn.liteAccordion = function() {
    var method=arguments[0];
    var elem = this,
        instance = elem.data('liteAccordion');

    // if creating a new instance
    if (typeof method === 'object' || !method) {
        return elem.each(function() {
            var liteAccordion;

            // if plugin already instantiated, return
            if (instance) return;

            // otherwise create a new instance
            liteAccordion = new LiteAccordion(elem, method);
            elem.data('liteAccordion', liteAccordion);
        });

    // otherwise, call method on current instance
    } else if (typeof method === 'string' && instance[method]) {
         arguments = Array.prototype.slice.call(arguments, 1);      
        // debug method isn't chainable b/c we need the debug object to be returned
        if (method === 'debug') {
            return instance[method].call(elem);
        } else { // the rest of the methods are chainable though
            instance[method].apply(elem, arguments);

            return elem;
        }
    }
};

})(jQuery); Added a resize event $('#elem').LiteAccordion('resize'{containerWidth:200,containerHeight:200}); Also fixed issue of play index; Thank you i really hate writing plugins from stracth this save me some time