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