PageRenderTime 1239ms CodeModel.GetById 37ms RepoModel.GetById 1ms app.codeStats 0ms

/files/foundation/6.1.2/js/foundation.orbit.js

https://gitlab.com/Mirros/jsdelivr
JavaScript | 421 lines | 228 code | 23 blank | 170 comment | 28 complexity | 27634bcf2bfe68686f2bb2ce721cbd7d MD5 | raw file
  1. /**
  2. * Orbit module.
  3. * @module foundation.orbit
  4. * @requires foundation.util.keyboard
  5. * @requires foundation.util.motion
  6. * @requires foundation.util.timerAndImageLoader
  7. * @requires foundation.util.touch
  8. */
  9. !function($, Foundation){
  10. 'use strict';
  11. /**
  12. * Creates a new instance of an orbit carousel.
  13. * @class
  14. * @param {jQuery} element - jQuery object to make into an Orbit Carousel.
  15. * @param {Object} options - Overrides to the default plugin settings.
  16. */
  17. function Orbit(element, options){
  18. this.$element = element;
  19. this.options = $.extend({}, Orbit.defaults, this.$element.data(), options);
  20. this._init();
  21. Foundation.registerPlugin(this, 'Orbit');
  22. Foundation.Keyboard.register('Orbit', {
  23. 'ltr': {
  24. 'ARROW_RIGHT': 'next',
  25. 'ARROW_LEFT': 'previous'
  26. },
  27. 'rtl': {
  28. 'ARROW_LEFT': 'next',
  29. 'ARROW_RIGHT': 'previous'
  30. }
  31. });
  32. }
  33. Orbit.defaults = {
  34. /**
  35. * Tells the JS to loadBullets.
  36. * @option
  37. * @example true
  38. */
  39. bullets: true,
  40. /**
  41. * Tells the JS to apply event listeners to nav buttons
  42. * @option
  43. * @example true
  44. */
  45. navButtons: true,
  46. /**
  47. * motion-ui animation class to apply
  48. * @option
  49. * @example 'slide-in-right'
  50. */
  51. animInFromRight: 'slide-in-right',
  52. /**
  53. * motion-ui animation class to apply
  54. * @option
  55. * @example 'slide-out-right'
  56. */
  57. animOutToRight: 'slide-out-right',
  58. /**
  59. * motion-ui animation class to apply
  60. * @option
  61. * @example 'slide-in-left'
  62. *
  63. */
  64. animInFromLeft: 'slide-in-left',
  65. /**
  66. * motion-ui animation class to apply
  67. * @option
  68. * @example 'slide-out-left'
  69. */
  70. animOutToLeft: 'slide-out-left',
  71. /**
  72. * Allows Orbit to automatically animate on page load.
  73. * @option
  74. * @example true
  75. */
  76. autoPlay: true,
  77. /**
  78. * Amount of time, in ms, between slide transitions
  79. * @option
  80. * @example 5000
  81. */
  82. timerDelay: 5000,
  83. /**
  84. * Allows Orbit to infinitely loop through the slides
  85. * @option
  86. * @example true
  87. */
  88. infiniteWrap: true,
  89. /**
  90. * Allows the Orbit slides to bind to swipe events for mobile, requires an additional util library
  91. * @option
  92. * @example true
  93. */
  94. swipe: true,
  95. /**
  96. * Allows the timing function to pause animation on hover.
  97. * @option
  98. * @example true
  99. */
  100. pauseOnHover: true,
  101. /**
  102. * Allows Orbit to bind keyboard events to the slider, to animate frames with arrow keys
  103. * @option
  104. * @example true
  105. */
  106. accessible: true,
  107. /**
  108. * Class applied to the container of Orbit
  109. * @option
  110. * @example 'orbit-container'
  111. */
  112. containerClass: 'orbit-container',
  113. /**
  114. * Class applied to individual slides.
  115. * @option
  116. * @example 'orbit-slide'
  117. */
  118. slideClass: 'orbit-slide',
  119. /**
  120. * Class applied to the bullet container. You're welcome.
  121. * @option
  122. * @example 'orbit-bullets'
  123. */
  124. boxOfBullets: 'orbit-bullets',
  125. /**
  126. * Class applied to the `next` navigation button.
  127. * @option
  128. * @example 'orbit-next'
  129. */
  130. nextClass: 'orbit-next',
  131. /**
  132. * Class applied to the `previous` navigation button.
  133. * @option
  134. * @example 'orbit-previous'
  135. */
  136. prevClass: 'orbit-previous',
  137. /**
  138. * Boolean to flag the js to use motion ui classes or not. Default to true for backwards compatability.
  139. * @option
  140. * @example true
  141. */
  142. useMUI: true
  143. };
  144. /**
  145. * Initializes the plugin by creating jQuery collections, setting attributes, and starting the animation.
  146. * @function
  147. * @private
  148. */
  149. Orbit.prototype._init = function(){
  150. this.$wrapper = this.$element.find('.' + this.options.containerClass);
  151. this.$slides = this.$element.find('.' + this.options.slideClass);
  152. var $images = this.$element.find('img'),
  153. initActive = this.$slides.filter('.is-active');
  154. if(!initActive.length){
  155. this.$slides.eq(0).addClass('is-active');
  156. }
  157. if(!this.options.useMUI){
  158. this.$slides.addClass('no-motionui');
  159. }
  160. if($images.length){
  161. Foundation.onImagesLoaded($images, this._prepareForOrbit.bind(this));
  162. }else{
  163. this._prepareForOrbit();//hehe
  164. }
  165. if(this.options.bullets){
  166. this._loadBullets();
  167. }
  168. this._events();
  169. if(this.options.autoPlay && this.$slides.length > 1){
  170. this.geoSync();
  171. }
  172. if(this.options.accessible){ // allow wrapper to be focusable to enable arrow navigation
  173. this.$wrapper.attr('tabindex', 0);
  174. }
  175. };
  176. /**
  177. * Creates a jQuery collection of bullets, if they are being used.
  178. * @function
  179. * @private
  180. */
  181. Orbit.prototype._loadBullets = function(){
  182. this.$bullets = this.$element.find('.' + this.options.boxOfBullets).find('button');
  183. };
  184. /**
  185. * Sets a `timer` object on the orbit, and starts the counter for the next slide.
  186. * @function
  187. */
  188. Orbit.prototype.geoSync = function(){
  189. var _this = this;
  190. this.timer = new Foundation.Timer(
  191. this.$element,
  192. {duration: this.options.timerDelay,
  193. infinite: false},
  194. function(){
  195. _this.changeSlide(true);
  196. });
  197. this.timer.start();
  198. };
  199. /**
  200. * Sets wrapper and slide heights for the orbit.
  201. * @function
  202. * @private
  203. */
  204. Orbit.prototype._prepareForOrbit = function(){
  205. var _this = this;
  206. this._setWrapperHeight(function(max){
  207. _this._setSlideHeight(max);
  208. });
  209. };
  210. /**
  211. * Calulates the height of each slide in the collection, and uses the tallest one for the wrapper height.
  212. * @function
  213. * @private
  214. * @param {Function} cb - a callback function to fire when complete.
  215. */
  216. Orbit.prototype._setWrapperHeight = function(cb){//rewrite this to `for` loop
  217. var max = 0, temp, counter = 0;
  218. this.$slides.each(function(){
  219. temp = this.getBoundingClientRect().height;
  220. $(this).attr('data-slide', counter);
  221. if(counter){//if not the first slide, set css position and display property
  222. $(this).css({'position': 'relative', 'display': 'none'});
  223. }
  224. max = temp > max ? temp : max;
  225. counter++;
  226. });
  227. if(counter === this.$slides.length){
  228. this.$wrapper.css({'height': max});//only change the wrapper height property once.
  229. cb(max);//fire callback with max height dimension.
  230. }
  231. };
  232. /**
  233. * Sets the max-height of each slide.
  234. * @function
  235. * @private
  236. */
  237. Orbit.prototype._setSlideHeight = function(height){
  238. this.$slides.each(function(){
  239. $(this).css('max-height', height);
  240. });
  241. };
  242. /**
  243. * Adds event listeners to basically everything within the element.
  244. * @function
  245. * @private
  246. */
  247. Orbit.prototype._events = function(){
  248. var _this = this;
  249. //***************************************
  250. //**Now using custom event - thanks to:**
  251. //** Yohai Ararat of Toronto **
  252. //***************************************
  253. if(this.$slides.length > 1){
  254. if(this.options.swipe){
  255. this.$slides.off('swipeleft.zf.orbit swiperight.zf.orbit')
  256. .on('swipeleft.zf.orbit', function(e){
  257. e.preventDefault();
  258. _this.changeSlide(true);
  259. }).on('swiperight.zf.orbit', function(e){
  260. e.preventDefault();
  261. _this.changeSlide(false);
  262. });
  263. }
  264. //***************************************
  265. if(this.options.autoPlay){
  266. this.$slides.on('click.zf.orbit', function(){
  267. _this.$element.data('clickedOn', _this.$element.data('clickedOn') ? false : true);
  268. _this.timer[_this.$element.data('clickedOn') ? 'pause' : 'start']();
  269. });
  270. if(this.options.pauseOnHover){
  271. this.$element.on('mouseenter.zf.orbit', function(){
  272. _this.timer.pause();
  273. }).on('mouseleave.zf.orbit', function(){
  274. if(!_this.$element.data('clickedOn')){
  275. _this.timer.start();
  276. }
  277. });
  278. }
  279. }
  280. if(this.options.navButtons){
  281. var $controls = this.$element.find('.' + this.options.nextClass + ', .' + this.options.prevClass);
  282. $controls.attr('tabindex', 0)
  283. //also need to handle enter/return and spacebar key presses
  284. .on('click.zf.orbit touchend.zf.orbit', function(){
  285. _this.changeSlide($(this).hasClass(_this.options.nextClass));
  286. });
  287. }
  288. if(this.options.bullets){
  289. this.$bullets.on('click.zf.orbit touchend.zf.orbit', function(){
  290. if(/is-active/g.test(this.className)){ return false; }//if this is active, kick out of function.
  291. var idx = $(this).data('slide'),
  292. ltr = idx > _this.$slides.filter('.is-active').data('slide'),
  293. $slide = _this.$slides.eq(idx);
  294. _this.changeSlide(ltr, $slide, idx);
  295. });
  296. }
  297. this.$wrapper.add(this.$bullets).on('keydown.zf.orbit', function(e){
  298. // handle keyboard event with keyboard util
  299. Foundation.Keyboard.handleKey(e, 'Orbit', {
  300. next: function() {
  301. _this.changeSlide(true);
  302. },
  303. previous: function() {
  304. _this.changeSlide(false);
  305. },
  306. handled: function() { // if bullet is focused, make sure focus moves
  307. if ($(e.target).is(_this.$bullets)) {
  308. _this.$bullets.filter('.is-active').focus();
  309. }
  310. }
  311. });
  312. });
  313. }
  314. };
  315. /**
  316. * Changes the current slide to a new one.
  317. * @function
  318. * @param {Boolean} isLTR - flag if the slide should move left to right.
  319. * @param {jQuery} chosenSlide - the jQuery element of the slide to show next, if one is selected.
  320. * @param {Number} idx - the index of the new slide in its collection, if one chosen.
  321. * @fires Orbit#slidechange
  322. */
  323. Orbit.prototype.changeSlide = function(isLTR, chosenSlide, idx){
  324. var $curSlide = this.$slides.filter('.is-active').eq(0);
  325. if(/mui/g.test($curSlide[0].className)){ return false; }//if the slide is currently animating, kick out of the function
  326. var $firstSlide = this.$slides.first(),
  327. $lastSlide = this.$slides.last(),
  328. dirIn = isLTR ? 'Right' : 'Left',
  329. dirOut = isLTR ? 'Left' : 'Right',
  330. _this = this,
  331. $newSlide;
  332. if(!chosenSlide){//most of the time, this will be auto played or clicked from the navButtons.
  333. $newSlide = isLTR ? //if wrapping enabled, check to see if there is a `next` or `prev` sibling, if not, select the first or last slide to fill in. if wrapping not enabled, attempt to select `next` or `prev`, if there's nothing there, the function will kick out on next step. CRAZY NESTED TERNARIES!!!!!
  334. (this.options.infiniteWrap ? $curSlide.next('.' + this.options.slideClass).length ? $curSlide.next('.' + this.options.slideClass) : $firstSlide : $curSlide.next('.' + this.options.slideClass))//pick next slide if moving left to right
  335. :
  336. (this.options.infiniteWrap ? $curSlide.prev('.' + this.options.slideClass).length ? $curSlide.prev('.' + this.options.slideClass) : $lastSlide : $curSlide.prev('.' + this.options.slideClass));//pick prev slide if moving right to left
  337. }else{
  338. $newSlide = chosenSlide;
  339. }
  340. if($newSlide.length){
  341. if(this.options.bullets){
  342. idx = idx || this.$slides.index($newSlide);//grab index to update bullets
  343. this._updateBullets(idx);
  344. }
  345. if(this.options.useMUI){
  346. Foundation.Motion.animateIn(
  347. $newSlide.addClass('is-active').css({'position': 'absolute', 'top': 0}),
  348. this.options['animInFrom' + dirIn],
  349. function(){
  350. $newSlide.css({'position': 'relative', 'display': 'block'})
  351. .attr('aria-live', 'polite');
  352. });
  353. Foundation.Motion.animateOut(
  354. $curSlide.removeClass('is-active'),
  355. this.options['animOutTo' + dirOut],
  356. function(){
  357. $curSlide.removeAttr('aria-live');
  358. if(_this.options.autoPlay && !_this.timer.isPaused){
  359. _this.timer.restart();
  360. }
  361. //do stuff?
  362. });
  363. }else{
  364. $curSlide.removeClass('is-active is-in').removeAttr('aria-live').hide();
  365. $newSlide.addClass('is-active is-in').attr('aria-live', 'polite').show();
  366. if(this.options.autoPlay && !this.timer.isPaused){
  367. this.timer.restart();
  368. }
  369. }
  370. /**
  371. * Triggers when the slide has finished animating in.
  372. * @event Orbit#slidechange
  373. */
  374. this.$element.trigger('slidechange.zf.orbit', [$newSlide]);
  375. }
  376. };
  377. /**
  378. * Updates the active state of the bullets, if displayed.
  379. * @function
  380. * @private
  381. * @param {Number} idx - the index of the current slide.
  382. */
  383. Orbit.prototype._updateBullets = function(idx){
  384. var $oldBullet = this.$element.find('.' + this.options.boxOfBullets)
  385. .find('.is-active').removeClass('is-active').blur(),
  386. span = $oldBullet.find('span:last').detach(),
  387. $newBullet = this.$bullets.eq(idx).addClass('is-active').append(span);
  388. };
  389. /**
  390. * Destroys the carousel and hides the element.
  391. * @function
  392. */
  393. Orbit.prototype.destroy = function(){
  394. this.$element.off('.zf.orbit').find('*').off('.zf.orbit').end().hide();
  395. Foundation.unregisterPlugin(this);
  396. };
  397. Foundation.plugin(Orbit, 'Orbit');
  398. }(jQuery, window.Foundation);