foundation.magellan.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. ;(function ($, window, document, undefined) {
  2. 'use strict';
  3. Foundation.libs['magellan-expedition'] = {
  4. name : 'magellan-expedition',
  5. version : '5.5.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. offset_by_height : true, // whether to offset the destination by the expedition height. Usually you want this to be true, unless your expedition is on the side.
  13. duration : 700, // animation duration time
  14. easing : 'swing' // animation easing
  15. },
  16. init : function (scope, method, options) {
  17. Foundation.inherit(this, 'throttle');
  18. this.bindings(method, options);
  19. },
  20. events : function () {
  21. var self = this,
  22. S = self.S,
  23. settings = self.settings;
  24. // initialize expedition offset
  25. self.set_expedition_position();
  26. S(self.scope)
  27. .off('.magellan')
  28. .on('click.fndtn.magellan', '[' + self.add_namespace('data-magellan-arrival') + '] a[href*=#]', function (e) {
  29. var sameHost = ((this.hostname === location.hostname) || !this.hostname),
  30. samePath = self.filterPathname(location.pathname) === self.filterPathname(this.pathname),
  31. testHash = this.hash.replace(/(:|\.|\/)/g, '\\$1'),
  32. anchor = this;
  33. if (sameHost && samePath && testHash) {
  34. e.preventDefault();
  35. var expedition = $(this).closest('[' + self.attr_name() + ']'),
  36. settings = expedition.data('magellan-expedition-init'),
  37. hash = this.hash.split('#').join(''),
  38. target = $('a[name="' + hash + '"]');
  39. if (target.length === 0) {
  40. target = $('#' + hash);
  41. }
  42. // Account for expedition height if fixed position
  43. var scroll_top = target.offset().top - settings.destination_threshold + 1;
  44. if (settings.offset_by_height) {
  45. scroll_top = scroll_top - expedition.outerHeight();
  46. }
  47. $('html, body').stop().animate({
  48. 'scrollTop' : scroll_top
  49. }, settings.duration, settings.easing, function () {
  50. if (history.pushState) {
  51. history.pushState(null, null, anchor.pathname + anchor.search + '#' + hash);
  52. } else {
  53. location.hash = anchor.pathname + anchor.search + '#' + hash;
  54. }
  55. });
  56. }
  57. })
  58. .on('scroll.fndtn.magellan', self.throttle(this.check_for_arrivals.bind(this), settings.throttle_delay));
  59. },
  60. check_for_arrivals : function () {
  61. var self = this;
  62. self.update_arrivals();
  63. self.update_expedition_positions();
  64. },
  65. set_expedition_position : function () {
  66. var self = this;
  67. $('[' + this.attr_name() + '=fixed]', self.scope).each(function (idx, el) {
  68. var expedition = $(this),
  69. settings = expedition.data('magellan-expedition-init'),
  70. styles = expedition.attr('styles'), // save styles
  71. top_offset, fixed_top;
  72. expedition.attr('style', '');
  73. top_offset = expedition.offset().top + settings.threshold;
  74. //set fixed-top by attribute
  75. fixed_top = parseInt(expedition.data('magellan-fixed-top'));
  76. if (!isNaN(fixed_top)) {
  77. self.settings.fixed_top = fixed_top;
  78. }
  79. expedition.data(self.data_attr('magellan-top-offset'), top_offset);
  80. expedition.attr('style', styles);
  81. });
  82. },
  83. update_expedition_positions : function () {
  84. var self = this,
  85. window_top_offset = $(window).scrollTop();
  86. $('[' + this.attr_name() + '=fixed]', self.scope).each(function () {
  87. var expedition = $(this),
  88. settings = expedition.data('magellan-expedition-init'),
  89. styles = expedition.attr('style'), // save styles
  90. top_offset = expedition.data('magellan-top-offset');
  91. //scroll to the top distance
  92. if (window_top_offset + self.settings.fixed_top >= top_offset) {
  93. // Placeholder allows height calculations to be consistent even when
  94. // appearing to switch between fixed/non-fixed placement
  95. var placeholder = expedition.prev('[' + self.add_namespace('data-magellan-expedition-clone') + ']');
  96. if (placeholder.length === 0) {
  97. placeholder = expedition.clone();
  98. placeholder.removeAttr(self.attr_name());
  99. placeholder.attr(self.add_namespace('data-magellan-expedition-clone'), '');
  100. expedition.before(placeholder);
  101. }
  102. expedition.css({position :'fixed', top : settings.fixed_top}).addClass('fixed');
  103. } else {
  104. expedition.prev('[' + self.add_namespace('data-magellan-expedition-clone') + ']').remove();
  105. expedition.attr('style', styles).css('position', '').css('top', '').removeClass('fixed');
  106. }
  107. });
  108. },
  109. update_arrivals : function () {
  110. var self = this,
  111. window_top_offset = $(window).scrollTop();
  112. $('[' + this.attr_name() + ']', self.scope).each(function () {
  113. var expedition = $(this),
  114. settings = expedition.data(self.attr_name(true) + '-init'),
  115. offsets = self.offsets(expedition, window_top_offset),
  116. arrivals = expedition.find('[' + self.add_namespace('data-magellan-arrival') + ']'),
  117. active_item = false;
  118. offsets.each(function (idx, item) {
  119. if (item.viewport_offset >= item.top_offset) {
  120. var arrivals = expedition.find('[' + self.add_namespace('data-magellan-arrival') + ']');
  121. arrivals.not(item.arrival).removeClass(settings.active_class);
  122. item.arrival.addClass(settings.active_class);
  123. active_item = true;
  124. return true;
  125. }
  126. });
  127. if (!active_item) {
  128. arrivals.removeClass(settings.active_class);
  129. }
  130. });
  131. },
  132. offsets : function (expedition, window_offset) {
  133. var self = this,
  134. settings = expedition.data(self.attr_name(true) + '-init'),
  135. viewport_offset = window_offset;
  136. return expedition.find('[' + self.add_namespace('data-magellan-arrival') + ']').map(function (idx, el) {
  137. var name = $(this).data(self.data_attr('magellan-arrival')),
  138. dest = $('[' + self.add_namespace('data-magellan-destination') + '=' + name + ']');
  139. if (dest.length > 0) {
  140. var top_offset = dest.offset().top - settings.destination_threshold;
  141. if (settings.offset_by_height) {
  142. top_offset = top_offset - expedition.outerHeight();
  143. }
  144. top_offset = Math.floor(top_offset);
  145. return {
  146. destination : dest,
  147. arrival : $(this),
  148. top_offset : top_offset,
  149. viewport_offset : viewport_offset
  150. }
  151. }
  152. }).sort(function (a, b) {
  153. if (a.top_offset < b.top_offset) {
  154. return -1;
  155. }
  156. if (a.top_offset > b.top_offset) {
  157. return 1;
  158. }
  159. return 0;
  160. });
  161. },
  162. data_attr : function (str) {
  163. if (this.namespace.length > 0) {
  164. return this.namespace + '-' + str;
  165. }
  166. return str;
  167. },
  168. off : function () {
  169. this.S(this.scope).off('.magellan');
  170. this.S(window).off('.magellan');
  171. },
  172. filterPathname : function (pathname) {
  173. pathname = pathname || '';
  174. return pathname
  175. .replace(/^\//,'')
  176. .replace(/(?:index|default).[a-zA-Z]{3,4}$/,'')
  177. .replace(/\/$/,'');
  178. },
  179. reflow : function () {
  180. var self = this;
  181. // remove placeholder expeditions used for height calculation purposes
  182. $('[' + self.add_namespace('data-magellan-expedition-clone') + ']', self.scope).remove();
  183. }
  184. };
  185. }(jQuery, window, window.document));