LiteAccordion
LiteAccordion copied to clipboard
Fix onmouseover animation glitch
Patch prevents animation start when slide is selected.
/*************************************************! *
- project: liteAccordion - a horizontal accordion plugin for jQuery
- author: Nicola Hibbert
- url: http://nicolahibbert.com/liteaccordion-v2/
- demo: http://www.nicolahibbert.com/demo/liteAccordion/
- Version: 2.2.0
- Copyright: (c) 2010-2013 Nicola Hibbert
- Licence: MIT
**************************************************/
;(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