/*
* glide.js
* ver: 1.0.4
* simple & efficient jquery slider
* autor: @jedrzejchalubek
* url: http://jedrzejchalubek.com
* licensed under the mit license
*/
/*
*
* autor qietu.com,inc.
* url: http://www.qietu.com
* autor: @qietuwang
* 解决状态栏(点点)在iphone下多几个点点问题 2015.10.20
*/
;(function ($, window, document, undefined) {
var name = 'glide',
defaults = {
// {int or bool} false for turning off autoplay
autoplay: 4000,
/**
* animation time
* !!! important !!!
* that option will be use only, when css3 are not suported
* if css3 are supported animation time is set in css declaration inside .css file
* @type {int}
*/
animationtime: 500,
/**
* {bool or string} show/hide/appendto arrows
* true for append arrows to slider wrapper
* false for not appending arrows
* id or class name (e.g. '.class-name') for appending to specific html markup
*/
arrows: true,
// {string} arrows wrapper class
arrowswrapperclass: 'slider-arrows',
// {string} main class for both arrows
arrowmainclass: 'slider-arrow',
// {string} right arrow
arrowrightclass: 'slider-arrow--right',
// {string} right arrow text
arrowrighttext: 'next',
// {string} left arrow
arrowleftclass: 'slider-arrow--left',
// {string} left arrow text
arrowlefttext: 'prev',
/**
* {bool or string} show/hide/appendto bullets navigation
* true for append arrows to slider wrapper
* false for not appending arrows
* id or class name (e.g. '.class-name') for appending to specific html markup
*/
nav: true,
// {bool} center bullet navigation
navcenter: true,
// {string} navigation class
navclass: 'slider-nav',
// {string} navigation item class
navitemclass: 'slider-nav__item',
// {string} current navigation item class
navcurrentitemclass: 'slider-nav__item--current',
// {int or bool} touch settings
touchdistance: 60,
slidecurrentitemclass: 'slide__item--current'
};
/**
* slider constructor
* @param {object} parent
* @param {object} options
*/
function glide(parent, options) {
var _ = this;
_.options = $.extend({}, defaults, options);
// sidebar
_.parent = parent;
// slides wrapper
_.wrapper = _.parent.children();
// slides
_.slides = _.wrapper.children();
// current slide id
_.currentslide = 0;
// css3 animation support
_.css3support = true;
// initialize
_.init();
// build dom
_.build();
// start autoplay
_.play();
/**
* controller
* touch events
*/
if (_.options.touchdistance) {
// init swipe
_.swipe();
}
/**
* controller
* keyboard left and right arrow keys
*/
$(document).on('keyup', function(k) {
// next
if (k.keycode === 39) _.slide(1);
// prev
if (k.keycode === 37) _.slide(-1);
});
/**
* controller
* mouse over slider
* when mouse is over slider, pause autoplay
* on out, start autoplay again
*/
_.parent.add(_.arrows).add(_.nav).on('mouseover mouseout', function (e) {
// pasue autoplay
_.pause();
// when mouse left slider or touch end, start autoplay anew
if (e.type === 'mouseout') _.play();
});
/**
* controller
* when resize browser window
* pause autoplay in fear of escalation
* reinit plugin for new slider dimensions
* correct crop to current slide
* start autoplay from beginning
*/
$(window).on('resize', function() {
// reinit plugin (set new slider dimensions)
_.init();
// crop to current slide
_.slide(0);
});
/**
* returning api
*/
return {
current: function() {
return -(_.currentslide) + 1;
},
play: function() {
_.play();
},
pause: function() {
_.pause();
},
next: function(callback) {
_.slide(1, false, callback);
},
prev: function(callback) {
_.slide(-1, false, callback);
},
jump: function(distance, callback) {
_.slide(distance-1, true, callback);
},
nav: function(target) {
/**
* if navigation wrapper already exist
* remove it, protection before doubled navigation
*/
if (_.navwrapper) {
_.navwrapper.remove();
}
_.options.nav = (target) ? target : _.options.nav;
// build
_.navigation();
},
arrows: function(target) {
/**
* if arrows wrapper already exist
* remove it, protection before doubled arrows
*/
if (_.arrowswrapper) {
_.arrowswrapper.remove();
}
_.options.arrows = (target) ? target : _.options.arrows;
// build
_.arrows();
}
};
}
/**
* building slider dom
*/
glide.prototype.build = function() {
var _ = this;
/**
* arrows
* if option is true and there is more than one slide
* append left and right arrow
*/
if (_.options.arrows) _.arrows();
/**
* navigation
* if option is true and there is more than one slide
* append navigation item for each slide
*/
if (_.options.nav) _.navigation();
};
/**
* building navigation dom
*/
glide.prototype.navigation = function() {
var _ = this;
if (_.slides.length > 1) {
// cache
var o = _.options,
/**
* setting append target
* if option is true set default target, that is slider wrapper
* else get target set in options
* @type {bool or string}
*/
target = (_.options.nav === true) ? _.parent : _.options.nav;
// navigation wrapper
_.navwrapper = $('
', {
'class': o.navclass
}).appendto(target);
// cache
var nav = _.navwrapper,
item;
// generate navigation items
for (var i = 0; i < _.slides.length; i++) {
item = $('').appendto(nav);
nav[i+1] = item;
}
// cache
var navchildren = nav.children();
// add navcurrentitemclass to the first navigation item
navchildren.eq(0).addclass(o.navcurrentitemclass);
// qietu.com addons start
_.slides.eq(0).addclass(o.slidecurrentitemclass);
//qietu.com addons end
// if centered option is true
if (o.navcenter) {
// center bullet navigation
nav.css({
'left': '50%',
'width': navchildren.outerwidth(true) * navchildren.length,
'margin-left': -nav.outerwidth(true)/2
});
}
/**
* controller
* on click in arrows or navigation, get direction and distance
* then slide specified distance
*/
navchildren.on('click touchstart', function(e) {
// prevent normal behaviour
e.preventdefault();
// slide distance specified in data attribute
_.slide( $(this).data('distance'), true );
});
}
};
/**
* building arrows dom
*/
glide.prototype.arrows = function() {
var _ = this;
if (_.slides.length > 1) {
var o = _.options,
/**
* setting append target
* if option is true set default target, that is slider wrapper
* else get target set in options
* @type {bool or string}
*/
target = (_.options.arrows === true) ? _.parent : _.options.arrows;
// arrows wrapper
_.arrowswrapper = $('', {
'class': o.arrowswrapperclass
}).appendto(target);
// cache
var arrows = _.arrowswrapper;
// right arrow
arrows.right = $('', {
'href': '#',
'class': o.arrowmainclass + ' ' + o.arrowrightclass,
// direction and distance -> one forward
'data-distance': '1',
'html': o.arrowrighttext
}).appendto(arrows);
// left arrow
arrows.left = $('', {
'href': '#',
'class': o.arrowmainclass + ' ' + o.arrowleftclass,
// direction and distance -> one backward
'data-distance': '-1',
'html': o.arrowlefttext
}).appendto(arrows);
/**
* controller
* on click in arrows or navigation, get direction and distance
* then slide specified distance
*/
arrows.children().on('click touchstart', function(e) {
// prevent normal behaviour
e.preventdefault();
// slide distance specified in data attribute
_.slide( $(this).data('distance'), false );
});
}
};
/**
* slides change & animate logic
* @param {int} distance
* @param {bool} jump
* @param {function} callback
*/
glide.prototype.slide = function(distance, jump, callback) {
// cache elements
var _ = this,
currentslide = (jump) ? 0 : _.currentslide,
slideslength = -(_.slides.length-1),
navcurrentclass = _.options.navcurrentitemclass,
slidesspread = _.slides.spread;
/*qietu.com*/
slidecurrentclass = _.options.slidecurrentitemclass,
/**
* stop autoplay
* clearing timer
*/
_.pause();
/**
* check if current slide is first and direction is previous, then go to last slide
* or current slide is last and direction is next, then go to the first slide
* else change current slide normally
*/
if ( currentslide === 0 && distance === -1 ) {
currentslide = slideslength;
} else if ( currentslide === slideslength && distance === 1 ) {
currentslide = 0;
} else {
currentslide = currentslide + (-distance);
}
/**
* crop to current slide.
* mul slide width by current slide number.
*/
var translate = slidesspread * currentslide + 'px';
// while css3 is supported
if (_.css3support) {
// croping by increasing/decreasing slider wrapper translate
_.wrapper.css({
'-webkit-transform': 'translate3d('+ translate +', 0px, 0px)',
'-moz-transform': 'translate3d('+ translate +', 0px, 0px)',
'-ms-transform': 'translate3d('+ translate +', 0px, 0px)',
'-o-transform': 'translate3d('+ translate +', 0px, 0px)',
'transform': 'translate3d('+ translate +', 0px, 0px)'
});
// else use $.animate()
} else {
// croping by increasing/decreasing slider wrapper margin
_.wrapper.stop().animate({ 'margin-left': translate }, _.options.animationtime);
}
// set to navigation item current class
if (_.options.nav) {
_.navwrapper.children()
.eq(-currentslide)
.addclass(navcurrentclass)
.siblings()
.removeclass(navcurrentclass);
}
// update current slide globaly
_.currentslide = currentslide;
// qietu.com addons start
_.slides.eq(-currentslide).addclass(slidecurrentclass).siblings().removeclass(slidecurrentclass);
//qietu.com addons end
// callback
if ( (callback !== 'undefined') && (typeof callback === 'function') ) callback();
/**
* start autoplay
* after slide
*/
_.play();
};
/**
* autoplay logic
* setup counting
*/
glide.prototype.play = function() {
var _ = this;
if (_.options.autoplay) {
_.auto = setinterval(function() {
_.slide(1, false);
}, _.options.autoplay);
}
};
/**
* autoplay pause
* clear counting
*/
glide.prototype.pause = function() {
if (this.options.autoplay) {
this.auto = clearinterval(this.auto);
}
};
/**
* change sildes on swipe event
*/
glide.prototype.swipe = function() {
// cache
var _ = this,
touch,
touchdistance,
touchstartx,
touchstarty,
touchendx,
touchendy,
touchhypotenuse,
touchcathetus,
touchsin,
mathpi = 180 / math.pi,
subexsx,
subeysy,
powex,
powey;
/**
* touch start
* @param {object} e event
*/
_.parent.on('touchstart', function(e) {
// cache event
touch = e.originalevent.touches[0] || e.originalevent.changedtouches[0];
// get touch start points
touchstartx = touch.pagex;
touchstarty = touch.pagey;
});
/**
* touch move
* from swipe length segments calculate swipe angle
* @param {obejct} e event
*/
_.parent.on('touchmove', function(e) {
// cache event
touch = e.originalevent.touches[0] || e.originalevent.changedtouches[0];
// get touch end points
touchendx = touch.pagex;
touchendy = touch.pagey;
// calculate start, end points
subexsx = touchendx - touchstartx;
subeysy = touchendy - touchstarty;
// bitwise subexsx pow
powex = math.abs( subexsx << 2 );
// bitwise subeysy pow
powey = math.abs( subeysy << 2 );
// calculate the length of the hypotenuse segment
touchhypotenuse = math.sqrt( powex + powey );
// calculate the length of the cathetus segment
touchcathetus = math.sqrt( powey );
// calculate the sine of the angle
touchsin = math.asin( touchcathetus/touchhypotenuse );
// while touch angle is lower than 32 degrees, block vertical scroll
if( (touchsin * mathpi) < 32 ) e.preventdefault();
});
/**
* touch end
* @param {object} e event
*/
_.parent.on('touchend', function(e) {
// cache event
touch = e.originalevent.touches[0] || e.originalevent.changedtouches[0];
// calculate touch distance
touchdistance = touch.pagex - touchstartx;
// while touch is positive and greater than distance set in options
if ( touchdistance > _.options.touchdistance ) {
// slide one backward
_.slide(-1);
// while touch is negative and lower than negative distance set in options
} else if ( touchdistance < -_.options.touchdistance) {
// slide one forward
_.slide(1);
}
});
};
/**
* initialize
* get & set dimensions
* set animation type
*/
glide.prototype.init = function() {
var _ = this,
// get sidebar width
sliderwidth = _.parent.width();
// get slide width
_.slides.spread = sliderwidth;
// set wrapper width
_.wrapper.width(sliderwidth * _.slides.length);
// set slide width
_.slides.width(_.slides.spread);
// if css3 transition isn't supported switch css3support variable to false and use $.animate()
if ( !iscsssupported("transition") || !iscsssupported("transform") ) _.css3support = false;
};
/**
* function to check css3 support
* @param {string} declaration name
* @return {boolean}
*/
function iscsssupported(declaration) {
var supported = false,
prefixes = 'khtml ms o moz webkit'.split(' '),
clone = document.createelement('div'),
declarationcapital = null;
declaration = declaration.tolowercase();
if (clone.style[declaration] !== undefined) supported = true;
if (supported === false) {
declarationcapital = declaration.charat(0).touppercase() + declaration.substr(1);
for( var i = 0; i < prefixes.length; i++ ) {
if( clone.style[prefixes[i] + declarationcapital ] !== undefined ) {
supported = true;
break;
}
}
}
if (window.opera) {
if (window.opera.version() < 13) supported = false;
}
return supported;
}
$.fn[name] = function (options) {
return this.each(function () {
if ( !$.data(this, 'api_' + name) ) {
$.data(this, 'api_' + name,
new glide($(this), options)
);
}
});
};
})(jquery, window, document);