foundation.orbit.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. ;(function ($, window, document, undefined) {
  2. 'use strict';
  3. Foundation.libs = Foundation.libs || {};
  4. Foundation.libs.orbit = {
  5. name: 'orbit',
  6. version: '4.2.0',
  7. settings: {
  8. timer_speed: 10000,
  9. pause_on_hover: true,
  10. resume_on_mouseout: false,
  11. animation_speed: 500,
  12. bullets: true,
  13. stack_on_small: true,
  14. navigation_arrows: true,
  15. slide_number: true,
  16. container_class: 'orbit-container',
  17. stack_on_small_class: 'orbit-stack-on-small',
  18. next_class: 'orbit-next',
  19. prev_class: 'orbit-prev',
  20. timer_container_class: 'orbit-timer',
  21. timer_paused_class: 'paused',
  22. timer_progress_class: 'orbit-progress',
  23. slides_container_class: 'orbit-slides-container',
  24. bullets_container_class: 'orbit-bullets',
  25. bullets_active_class: 'active',
  26. slide_number_class: 'orbit-slide-number',
  27. caption_class: 'orbit-caption',
  28. active_slide_class: 'active',
  29. orbit_transition_class: 'orbit-transitioning'
  30. },
  31. init: function (scope, method, options) {
  32. var self = this;
  33. Foundation.inherit(self, 'data_options');
  34. if (typeof method === 'object') {
  35. $.extend(true, self.settings, method);
  36. }
  37. if ($(scope).is('[data-orbit]')) {
  38. var scoped_self = $.extend(true, {}, self);
  39. scoped_self._init(idx, el);
  40. }
  41. $('[data-orbit]', scope).each(function(idx, el) {
  42. var scoped_self = $.extend(true, {}, self);
  43. scoped_self._init(idx, el);
  44. });
  45. },
  46. _container_html: function() {
  47. var self = this;
  48. return '<div class="' + self.settings.container_class + '"></div>';
  49. },
  50. _bullets_container_html: function($slides) {
  51. var self = this,
  52. $list = $('<ol class="' + self.settings.bullets_container_class + '"></ol>');
  53. $slides.each(function(idx, slide) {
  54. var $item = $('<li data-orbit-slide-number="' + (idx+1) + '" class=""></li>');
  55. if (idx === 0) {
  56. $item.addClass(self.settings.bullets_active_class);
  57. }
  58. $list.append($item);
  59. });
  60. return $list;
  61. },
  62. _slide_number_html: function(slide_number, total_slides) {
  63. var self = this,
  64. $container = $('<div class="' + self.settings.slide_number_class + '"></div>');
  65. $container.append('<span>' + slide_number + '</span> of <span>' + total_slides + '</span>');
  66. return $container;
  67. },
  68. _timer_html: function() {
  69. var self = this;
  70. if (typeof self.settings.timer_speed === 'number' && self.settings.timer_speed > 0) {
  71. return '<div class="' + self.settings.timer_container_class
  72. + '"><span></span><div class="' + self.settings.timer_progress_class
  73. + '"></div></div>';
  74. } else {
  75. return '';
  76. }
  77. },
  78. _next_html: function() {
  79. var self = this;
  80. return '<a href="#" class="' + self.settings.next_class + '">Next <span></span></a>';
  81. },
  82. _prev_html: function() {
  83. var self = this;
  84. return '<a href="#" class="' + self.settings.prev_class + '">Prev <span></span></a>';
  85. },
  86. _init: function (idx, slider) {
  87. var self = this,
  88. $slides_container = $(slider),
  89. $container = $slides_container.wrap(self._container_html()).parent(),
  90. $slides = $slides_container.children();
  91. $.extend(true, self.settings, self.data_options($slides_container));
  92. if (self.settings.navigation_arrows) {
  93. $container.append(self._prev_html());
  94. $container.append(self._next_html());
  95. }
  96. $slides_container.addClass(self.settings.slides_container_class);
  97. if (self.settings.stack_on_small) {
  98. $container.addClass(self.settings.stack_on_small_class);
  99. }
  100. if (self.settings.slide_number) {
  101. $container.append(self._slide_number_html(1, $slides.length));
  102. }
  103. $container.append(self._timer_html());
  104. if (self.settings.bullets) {
  105. $container.after(self._bullets_container_html($slides));
  106. }
  107. // To better support the "sliding" effect it's easier
  108. // if we just clone the first and last slides
  109. $slides_container.append($slides.first().clone().attr('data-orbit-slide',''));
  110. $slides_container.prepend($slides.last().clone().attr('data-orbit-slide',''));
  111. // Make the first "real" slide active
  112. $slides_container.css(Foundation.rtl ? 'marginRight' : 'marginLeft', '-100%');
  113. $slides.first().addClass(self.settings.active_slide_class);
  114. self._init_events($slides_container);
  115. self._init_dimensions($slides_container);
  116. self._start_timer($slides_container);
  117. },
  118. _init_events: function ($slides_container) {
  119. var self = this,
  120. $container = $slides_container.parent();
  121. $(window)
  122. .on('load.fndtn.orbit', function() {
  123. $slides_container.height('');
  124. $slides_container.height($slides_container.height($container.height()));
  125. $slides_container.trigger('orbit:ready');
  126. })
  127. .on('resize.fndtn.orbit', function() {
  128. $slides_container.height('');
  129. $slides_container.height($slides_container.height($container.height()));
  130. });
  131. $(document).on('click.fndtn.orbit', '[data-orbit-link]', function(e) {
  132. e.preventDefault();
  133. var id = $(e.currentTarget).attr('data-orbit-link'),
  134. $slide = $slides_container.find('[data-orbit-slide=' + id + ']').first();
  135. if ($slide.length === 1) {
  136. self._reset_timer($slides_container, true);
  137. self._goto($slides_container, $slide.index(), function() {});
  138. }
  139. });
  140. $container.siblings('.' + self.settings.bullets_container_class)
  141. .on('click.fndtn.orbit', '[data-orbit-slide-number]', function(e) {
  142. e.preventDefault();
  143. self._reset_timer($slides_container, true);
  144. self._goto($slides_container, $(e.currentTarget).data('orbit-slide-number'),function() {});
  145. });
  146. $container
  147. .on('mouseenter.fndtn.orbit', function(e) {
  148. if (self.settings.pause_on_hover) {
  149. self._stop_timer($slides_container);
  150. }
  151. })
  152. .on('mouseleave.fndtn.orbit', function(e) {
  153. if (self.settings.resume_on_mouseout) {
  154. self._start_timer($slides_container);
  155. }
  156. })
  157. .on('orbit:after-slide-change.fndtn.orbit', function(e, orbit) {
  158. var $slide_number = $container.find('.' + self.settings.slide_number_class);
  159. if ($slide_number.length === 1) {
  160. $slide_number.replaceWith(self._slide_number_html(orbit.slide_number, orbit.total_slides));
  161. }
  162. })
  163. .on('orbit:next-slide.fndtn.orbit click.fndtn.orbit', '.' + self.settings.next_class, function(e) {
  164. e.preventDefault();
  165. self._reset_timer($slides_container, true);
  166. self._goto($slides_container, 'next', function() {});
  167. })
  168. .on('orbit:prev-slide.fndtn.orbit click.fndtn.orbit', '.' + self.settings.prev_class, function(e) {
  169. e.preventDefault();
  170. self._reset_timer($slides_container, true);
  171. self._goto($slides_container, 'prev', function() {});
  172. })
  173. .on('orbit:toggle-play-pause.fndtn.orbit click.fndtn.orbit touchstart.fndtn.orbit', '.' + self.settings.timer_container_class, function(e) {
  174. e.preventDefault();
  175. var $timer = $(e.currentTarget).toggleClass(self.settings.timer_paused_class),
  176. $slides_container = $timer.closest('.' + self.settings.container_class)
  177. .find('.' + self.settings.slides_container_class);
  178. if ($timer.hasClass(self.settings.timer_paused_class)) {
  179. self._stop_timer($slides_container);
  180. } else {
  181. self._start_timer($slides_container);
  182. }
  183. })
  184. .on('touchstart.fndtn.orbit', function(e) {
  185. if (!e.touches) { e = e.originalEvent; }
  186. var data = {
  187. start_page_x: e.touches[0].pageX,
  188. start_page_y: e.touches[0].pageY,
  189. start_time: (new Date()).getTime(),
  190. delta_x: 0,
  191. is_scrolling: undefined
  192. };
  193. $container.data('swipe-transition', data);
  194. e.stopPropagation();
  195. })
  196. .on('touchmove.fndtn.orbit', function(e) {
  197. if (!e.touches) { e = e.originalEvent; }
  198. // Ignore pinch/zoom events
  199. if(e.touches.length > 1 || e.scale && e.scale !== 1) return;
  200. var data = $container.data('swipe-transition');
  201. if (typeof data === 'undefined') {
  202. data = {};
  203. }
  204. data.delta_x = e.touches[0].pageX - data.start_page_x;
  205. if ( typeof data.is_scrolling === 'undefined') {
  206. data.is_scrolling = !!( data.is_scrolling || Math.abs(data.delta_x) < Math.abs(e.touches[0].pageY - data.start_page_y) );
  207. }
  208. if (!data.is_scrolling && !data.active) {
  209. e.preventDefault();
  210. self._stop_timer($slides_container);
  211. var direction = (data.delta_x < 0) ? 'next' : 'prev';
  212. data.active = true;
  213. self._goto($slides_container, direction, function() {});
  214. }
  215. })
  216. .on('touchend.fndtn.orbit', function(e) {
  217. $container.data('swipe-transition', {});
  218. e.stopPropagation();
  219. });
  220. },
  221. _init_dimensions: function ($slides_container) {
  222. var $container = $slides_container.parent(),
  223. $slides = $slides_container.children();
  224. $slides_container.css('width', $slides.length * 100 + '%');
  225. $slides.css('width', 100 / $slides.length + '%');
  226. $slides_container.height($container.height());
  227. $slides_container.css('width', $slides.length * 100 + '%');
  228. },
  229. _start_timer: function ($slides_container) {
  230. var self = this,
  231. $container = $slides_container.parent();
  232. var callback = function() {
  233. self._reset_timer($slides_container, false);
  234. self._goto($slides_container, 'next', function() {
  235. self._start_timer($slides_container);
  236. });
  237. };
  238. var $timer = $container.find('.' + self.settings.timer_container_class),
  239. $progress = $timer.find('.' + self.settings.timer_progress_class),
  240. progress_pct = ($progress.width() / $timer.width()),
  241. delay = self.settings.timer_speed - (progress_pct * self.settings.timer_speed);
  242. $progress.animate({'width': '100%'}, delay, 'linear', callback);
  243. $slides_container.trigger('orbit:timer-started');
  244. },
  245. _stop_timer: function ($slides_container) {
  246. var self = this,
  247. $container = $slides_container.parent(),
  248. $timer = $container.find('.' + self.settings.timer_container_class),
  249. $progress = $timer.find('.' + self.settings.timer_progress_class),
  250. progress_pct = $progress.width() / $timer.width();
  251. self._rebuild_timer($container, progress_pct * 100 + '%');
  252. // $progress.stop();
  253. $slides_container.trigger('orbit:timer-stopped');
  254. $timer = $container.find('.' + self.settings.timer_container_class);
  255. $timer.addClass(self.settings.timer_paused_class);
  256. },
  257. _reset_timer: function($slides_container, is_paused) {
  258. var self = this,
  259. $container = $slides_container.parent();
  260. self._rebuild_timer($container, '0%');
  261. if (typeof is_paused === 'boolean' && is_paused) {
  262. var $timer = $container.find('.' + self.settings.timer_container_class);
  263. $timer.addClass(self.settings.timer_paused_class);
  264. }
  265. },
  266. _rebuild_timer: function ($container, width_pct) {
  267. // Zepto is unable to stop animations since they
  268. // are css-based. This is a workaround for that
  269. // limitation, which rebuilds the dom element
  270. // thus stopping the animation
  271. var self = this,
  272. $timer = $container.find('.' + self.settings.timer_container_class),
  273. $new_timer = $(self._timer_html()),
  274. $new_timer_progress = $new_timer.find('.' + self.settings.timer_progress_class);
  275. if (typeof Zepto === 'function') {
  276. $timer.remove();
  277. $container.append($new_timer);
  278. $new_timer_progress.css('width', width_pct);
  279. } else if (typeof jQuery === 'function') {
  280. var $progress = $timer.find('.' + self.settings.timer_progress_class);
  281. $progress.css('width', width_pct);
  282. $progress.stop();
  283. }
  284. },
  285. _goto: function($slides_container, index_or_direction, callback) {
  286. var self = this,
  287. $container = $slides_container.parent(),
  288. $slides = $slides_container.children(),
  289. $active_slide = $slides_container.find('.' + self.settings.active_slide_class),
  290. active_index = $active_slide.index(),
  291. margin_position = Foundation.rtl ? 'marginRight' : 'marginLeft';
  292. if ($container.hasClass(self.settings.orbit_transition_class)) {
  293. return false;
  294. }
  295. if (index_or_direction === 'prev') {
  296. if (active_index === 0) {
  297. active_index = $slides.length - 1;
  298. }
  299. else {
  300. active_index--;
  301. }
  302. }
  303. else if (index_or_direction === 'next') {
  304. active_index = (active_index+1) % $slides.length;
  305. }
  306. else if (typeof index_or_direction === 'number') {
  307. active_index = (index_or_direction % $slides.length);
  308. }
  309. if (active_index === ($slides.length - 1) && index_or_direction === 'next') {
  310. $slides_container.css(margin_position, '0%');
  311. active_index = 1;
  312. }
  313. else if (active_index === 0 && index_or_direction === 'prev') {
  314. $slides_container.css(margin_position, '-' + ($slides.length - 1) * 100 + '%');
  315. active_index = $slides.length - 2;
  316. }
  317. // Start transition, make next slide active
  318. $container.addClass(self.settings.orbit_transition_class);
  319. $active_slide.removeClass(self.settings.active_slide_class);
  320. $($slides[active_index]).addClass(self.settings.active_slide_class);
  321. // Make next bullet active
  322. var $bullets = $container.siblings('.' + self.settings.bullets_container_class);
  323. if ($bullets.length === 1) {
  324. $bullets.children().removeClass(self.settings.bullets_active_class);
  325. $($bullets.children()[active_index-1]).addClass(self.settings.bullets_active_class);
  326. }
  327. var new_margin_left = '-' + (active_index * 100) + '%';
  328. // Check to see if animation will occur, otherwise perform
  329. // callbacks manually
  330. $slides_container.trigger('orbit:before-slide-change');
  331. if ($slides_container.css(margin_position) === new_margin_left) {
  332. $container.removeClass(self.settings.orbit_transition_class);
  333. $slides_container.trigger('orbit:after-slide-change', [{slide_number: active_index, total_slides: $slides_container.children().length - 2}]);
  334. callback();
  335. } else {
  336. var properties = {};
  337. properties[margin_position] = new_margin_left;
  338. $slides_container.animate(properties, self.settings.animation_speed, 'linear', function() {
  339. $container.removeClass(self.settings.orbit_transition_class);
  340. $slides_container.trigger('orbit:after-slide-change', [{slide_number: active_index, total_slides: $slides_container.children().length - 2}]);
  341. callback();
  342. });
  343. }
  344. }
  345. };
  346. }(Foundation.zj, this, this.document));