foundation.magellan.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. ;(function ($, window, document, undefined) {
  2. 'use strict';
  3. Foundation.libs['magellan-expedition'] = {
  4. name : 'magellan-expedition',
  5. version : '5.3.3',
  6. settings : {
  7. active_class: 'active',
  8. threshold: 0, // pixels from the top of the expedition for it to become fixes
  9. destination_threshold: 20, // pixels from the top of destination for it to be considered active
  10. throttle_delay: 30, // calculation throttling to increase framerate
  11. fixed_top: 0 // top distance in pixels assigend to the fixed element on scroll
  12. },
  13. init : function (scope, method, options) {
  14. Foundation.inherit(this, 'throttle');
  15. this.bindings(method, options);
  16. },
  17. events : function () {
  18. var self = this,
  19. S = self.S,
  20. settings = self.settings;
  21. // initialize expedition offset
  22. self.set_expedition_position();
  23. S(self.scope)
  24. .off('.magellan')
  25. .on('click.fndtn.magellan', '[' + self.add_namespace('data-magellan-arrival') + '] a[href^="#"]', function (e) {
  26. e.preventDefault();
  27. var expedition = $(this).closest('[' + self.attr_name() + ']'),
  28. settings = expedition.data('magellan-expedition-init'),
  29. hash = this.hash.split('#').join(''),
  30. target = $("a[name='"+hash+"']");
  31. if (target.length === 0) {
  32. target = $('#'+hash);
  33. }
  34. // Account for expedition height if fixed position
  35. var scroll_top = target.offset().top - settings.destination_threshold + 1;
  36. scroll_top = scroll_top - expedition.outerHeight();
  37. $('html, body').stop().animate({
  38. 'scrollTop': scroll_top
  39. }, 700, 'swing', function () {
  40. if(history.pushState) {
  41. history.pushState(null, null, '#'+hash);
  42. }
  43. else {
  44. location.hash = '#'+hash;
  45. }
  46. });
  47. })
  48. .on('scroll.fndtn.magellan', self.throttle(this.check_for_arrivals.bind(this), settings.throttle_delay));
  49. $(window)
  50. .on('resize.fndtn.magellan', self.throttle(this.set_expedition_position.bind(this), settings.throttle_delay));
  51. },
  52. check_for_arrivals : function() {
  53. var self = this;
  54. self.update_arrivals();
  55. self.update_expedition_positions();
  56. },
  57. set_expedition_position : function() {
  58. var self = this;
  59. $('[' + this.attr_name() + '=fixed]', self.scope).each(function(idx, el) {
  60. var expedition = $(this),
  61. settings = expedition.data('magellan-expedition-init'),
  62. styles = expedition.attr('styles'), // save styles
  63. top_offset, fixed_top;
  64. expedition.attr('style', '');
  65. top_offset = expedition.offset().top + settings.threshold;
  66. //set fixed-top by attribute
  67. fixed_top = parseInt(expedition.data('magellan-fixed-top'));
  68. if(!isNaN(fixed_top))
  69. self.settings.fixed_top = fixed_top;
  70. expedition.data(self.data_attr('magellan-top-offset'), top_offset);
  71. expedition.attr('style', styles);
  72. });
  73. },
  74. update_expedition_positions : function() {
  75. var self = this,
  76. window_top_offset = $(window).scrollTop();
  77. $('[' + this.attr_name() + '=fixed]', self.scope).each(function() {
  78. var expedition = $(this),
  79. settings = expedition.data('magellan-expedition-init'),
  80. styles = expedition.attr('style'), // save styles
  81. top_offset = expedition.data('magellan-top-offset');
  82. //scroll to the top distance
  83. if (window_top_offset+self.settings.fixed_top >= top_offset) {
  84. // Placeholder allows height calculations to be consistent even when
  85. // appearing to switch between fixed/non-fixed placement
  86. var placeholder = expedition.prev('[' + self.add_namespace('data-magellan-expedition-clone') + ']');
  87. if (placeholder.length === 0) {
  88. placeholder = expedition.clone();
  89. placeholder.removeAttr(self.attr_name());
  90. placeholder.attr(self.add_namespace('data-magellan-expedition-clone'),'');
  91. expedition.before(placeholder);
  92. }
  93. expedition.css({position:'fixed', top: settings.fixed_top}).addClass('fixed');
  94. } else {
  95. expedition.prev('[' + self.add_namespace('data-magellan-expedition-clone') + ']').remove();
  96. expedition.attr('style',styles).css('position','').css('top','').removeClass('fixed');
  97. }
  98. });
  99. },
  100. update_arrivals : function() {
  101. var self = this,
  102. window_top_offset = $(window).scrollTop();
  103. $('[' + this.attr_name() + ']', self.scope).each(function() {
  104. var expedition = $(this),
  105. settings = expedition.data(self.attr_name(true) + '-init'),
  106. offsets = self.offsets(expedition, window_top_offset),
  107. arrivals = expedition.find('[' + self.add_namespace('data-magellan-arrival') + ']'),
  108. active_item = false;
  109. offsets.each(function(idx, item) {
  110. if (item.viewport_offset >= item.top_offset) {
  111. var arrivals = expedition.find('[' + self.add_namespace('data-magellan-arrival') + ']');
  112. arrivals.not(item.arrival).removeClass(settings.active_class);
  113. item.arrival.addClass(settings.active_class);
  114. active_item = true;
  115. return true;
  116. }
  117. });
  118. if (!active_item) arrivals.removeClass(settings.active_class);
  119. });
  120. },
  121. offsets : function(expedition, window_offset) {
  122. var self = this,
  123. settings = expedition.data(self.attr_name(true) + '-init'),
  124. viewport_offset = window_offset;
  125. return expedition.find('[' + self.add_namespace('data-magellan-arrival') + ']').map(function(idx, el) {
  126. var name = $(this).data(self.data_attr('magellan-arrival')),
  127. dest = $('[' + self.add_namespace('data-magellan-destination') + '=' + name + ']');
  128. if (dest.length > 0) {
  129. var top_offset = Math.floor(dest.offset().top - settings.destination_threshold - expedition.outerHeight());
  130. return {
  131. destination : dest,
  132. arrival : $(this),
  133. top_offset : top_offset,
  134. viewport_offset : viewport_offset
  135. }
  136. }
  137. }).sort(function(a, b) {
  138. if (a.top_offset < b.top_offset) return -1;
  139. if (a.top_offset > b.top_offset) return 1;
  140. return 0;
  141. });
  142. },
  143. data_attr: function (str) {
  144. if (this.namespace.length > 0) {
  145. return this.namespace + '-' + str;
  146. }
  147. return str;
  148. },
  149. off : function () {
  150. this.S(this.scope).off('.magellan');
  151. this.S(window).off('.magellan');
  152. },
  153. reflow : function () {
  154. var self = this;
  155. // remove placeholder expeditions used for height calculation purposes
  156. $('[' + self.add_namespace('data-magellan-expedition-clone') + ']', self.scope).remove();
  157. }
  158. };
  159. }(jQuery, window, window.document));