2 (function ($, window
, document
, undefined) {
5 Foundation
.libs
.topbar
= {
12 sticky_class
: 'sticky',
13 custom_back_text
: true,
15 mobile_show_parent_link
: true,
17 scrolltop
: true, // jump to top when sticky nav menu toggle is clicked
21 init: function (section
, method
, options
) {
22 Foundation
.inherit(this, 'add_custom_rule register_media throttle');
25 self
.register_media('topbar', 'foundation-mq-topbar');
27 this.bindings(method
, options
);
29 self
.S('[' + this.attr_name() + ']', this.scope
).each(function () {
31 settings
= topbar
.data(self
.attr_name(true) + '-init'),
32 section
= self
.S('section, .top-bar-section', this);
33 topbar
.data('index', 0);
34 var topbarContainer
= topbar
.parent();
35 if (topbarContainer
.hasClass('fixed') || self
.is_sticky(topbar
, topbarContainer
, settings
)) {
36 self
.settings
.sticky_class
= settings
.sticky_class
;
37 self
.settings
.sticky_topbar
= topbar
;
38 topbar
.data('height', topbarContainer
.outerHeight());
39 topbar
.data('stickyoffset', topbarContainer
.offset().top
);
41 topbar
.data('height', topbar
.outerHeight());
44 if (!settings
.assembled
) {
45 self
.assemble(topbar
);
48 if (settings
.is_hover
) {
49 self
.S('.has-dropdown', topbar
).addClass('not-click');
51 self
.S('.has-dropdown', topbar
).removeClass('not-click');
54 // Pad body when sticky (scrolled) or fixed.
55 self
.add_custom_rule('.f-topbar-fixed { padding-top: ' + topbar
.data('height') + 'px }');
57 if (topbarContainer
.hasClass('fixed')) {
58 self
.S('body').addClass('f-topbar-fixed');
64 is_sticky: function (topbar
, topbarContainer
, settings
) {
65 var sticky
= topbarContainer
.hasClass(settings
.sticky_class
);
67 if (sticky
&& settings
.sticky_on
=== 'all') {
69 } else if (sticky
&& this.small() && settings
.sticky_on
=== 'small') {
70 return (matchMedia(Foundation
.media_queries
.small
).matches
&& !matchMedia(Foundation
.media_queries
.medium
).matches
&& !matchMedia(Foundation
.media_queries
.large
).matches
);
72 } else if (sticky
&& this.medium() && settings
.sticky_on
=== 'medium') {
73 return (matchMedia(Foundation
.media_queries
.small
).matches
&& matchMedia(Foundation
.media_queries
.medium
).matches
&& !matchMedia(Foundation
.media_queries
.large
).matches
);
75 } else if (sticky
&& this.large() && settings
.sticky_on
=== 'large') {
76 return (matchMedia(Foundation
.media_queries
.small
).matches
&& matchMedia(Foundation
.media_queries
.medium
).matches
&&
77 matchMedia(Foundation
.media_queries
.large
).matches
);
84 toggle: function (toggleEl
) {
89 topbar
= self
.S(toggleEl
).closest('[' + this.attr_name() + ']');
91 topbar
= self
.S('[' + this.attr_name() + ']');
94 var settings
= topbar
.data(this.attr_name(true) + '-init');
96 var section
= self
.S('section, .top-bar-section', topbar
);
98 if (self
.breakpoint()) {
100 section
.css({left
: '0%'});
101 $('>.name', section
).css({left
: '100%'});
103 section
.css({right
: '0%'});
104 $('>.name', section
).css({right
: '100%'});
107 self
.S('li.moved', section
).removeClass('moved');
108 topbar
.data('index', 0);
111 .toggleClass('expanded')
115 if (settings
.scrolltop
) {
116 if (!topbar
.hasClass('expanded')) {
117 if (topbar
.hasClass('fixed')) {
118 topbar
.parent().addClass('fixed');
119 topbar
.removeClass('fixed');
120 self
.S('body').addClass('f-topbar-fixed');
122 } else if (topbar
.parent().hasClass('fixed')) {
123 if (settings
.scrolltop
) {
124 topbar
.parent().removeClass('fixed');
125 topbar
.addClass('fixed');
126 self
.S('body').removeClass('f-topbar-fixed');
128 window
.scrollTo(0, 0);
130 topbar
.parent().removeClass('expanded');
134 if (self
.is_sticky(topbar
, topbar
.parent(), settings
)) {
135 topbar
.parent().addClass('fixed');
138 if (topbar
.parent().hasClass('fixed')) {
139 if (!topbar
.hasClass('expanded')) {
140 topbar
.removeClass('fixed');
141 topbar
.parent().removeClass('expanded');
142 self
.update_sticky_positioning();
144 topbar
.addClass('fixed');
145 topbar
.parent().addClass('expanded');
146 self
.S('body').addClass('f-topbar-fixed');
154 events: function (bar
) {
160 .on('click.fndtn.topbar', '[' + this.attr_name() + '] .toggle-topbar', function (e
) {
164 .on('click.fndtn.topbar', '.top-bar .top-bar-section li a[href^="#"],[' + this.attr_name() + '] .top-bar-section li a[href^="#"]', function (e
) {
165 var li
= $(this).closest('li');
166 if (self
.breakpoint() && !li
.hasClass('back') && !li
.hasClass('has-dropdown')) {
170 .on('click.fndtn.topbar', '[' + this.attr_name() + '] li.has-dropdown', function (e
) {
172 target
= S(e
.target
),
173 topbar
= li
.closest('[' + self
.attr_name() + ']'),
174 settings
= topbar
.data(self
.attr_name(true) + '-init');
176 if (target
.data('revealId')) {
181 if (self
.breakpoint()) return;
182 if (settings
.is_hover
&& !Modernizr
.touch
) return;
184 e
.stopImmediatePropagation();
186 if (li
.hasClass('hover')) {
188 .removeClass('hover')
190 .removeClass('hover');
192 li
.parents('li.hover')
193 .removeClass('hover');
195 li
.addClass('hover');
197 $(li
).siblings().removeClass('hover');
199 if (target
[0].nodeName
=== 'A' && target
.parent().hasClass('has-dropdown')) {
204 .on('click.fndtn.topbar', '[' + this.attr_name() + '] .has-dropdown>a', function (e
) {
205 if (self
.breakpoint()) {
210 topbar
= $this.closest('[' + self
.attr_name() + ']'),
211 section
= topbar
.find('section, .top-bar-section'),
212 dropdownHeight
= $this.next('.dropdown').outerHeight(),
213 $selectedLi
= $this.closest('li');
215 topbar
.data('index', topbar
.data('index') + 1);
216 $selectedLi
.addClass('moved');
219 section
.css({left
: -(100 * topbar
.data('index')) + '%'});
220 section
.find('>.name').css({left
: 100 * topbar
.data('index') + '%'});
222 section
.css({right
: -(100 * topbar
.data('index')) + '%'});
223 section
.find('>.name').css({right
: 100 * topbar
.data('index') + '%'});
226 topbar
.css('height', $this.siblings('ul').outerHeight(true) + topbar
.data('height'));
230 S(window
).off('.topbar').on('resize.fndtn.topbar', self
.throttle(function () {
231 self
.resize
.call(self
);
232 }, 50)).trigger('resize').trigger('resize.fndtn.topbar').load(function () {
233 // Ensure that the offset is calculated after all of the pages resources have loaded
234 S(this).trigger('resize.fndtn.topbar');
237 S('body').off('.topbar').on('click.fndtn.topbar', function (e
) {
238 var parent
= S(e
.target
).closest('li').closest('li.hover');
240 if (parent
.length
> 0) {
244 S('[' + self
.attr_name() + '] li.hover').removeClass('hover');
247 // Go up a level on Click
248 S(this.scope
).on('click.fndtn.topbar', '[' + this.attr_name() + '] .has-dropdown .back', function (e
) {
252 topbar
= $this.closest('[' + self
.attr_name() + ']'),
253 section
= topbar
.find('section, .top-bar-section'),
254 settings
= topbar
.data(self
.attr_name(true) + '-init'),
255 $movedLi
= $this.closest('li.moved'),
256 $previousLevelUl
= $movedLi
.parent();
258 topbar
.data('index', topbar
.data('index') - 1);
261 section
.css({left
: -(100 * topbar
.data('index')) + '%'});
262 section
.find('>.name').css({left
: 100 * topbar
.data('index') + '%'});
264 section
.css({right
: -(100 * topbar
.data('index')) + '%'});
265 section
.find('>.name').css({right
: 100 * topbar
.data('index') + '%'});
268 if (topbar
.data('index') === 0) {
269 topbar
.css('height', '');
271 topbar
.css('height', $previousLevelUl
.outerHeight(true) + topbar
.data('height'));
274 setTimeout(function () {
275 $movedLi
.removeClass('moved');
279 // Show dropdown menus when their items are focused
280 S(this.scope
).find('.dropdown a')
282 $(this).parents('.has-dropdown').addClass('hover');
285 $(this).parents('.has-dropdown').removeClass('hover');
289 resize: function () {
291 self
.S('[' + this.attr_name() + ']').each(function () {
292 var topbar
= self
.S(this),
293 settings
= topbar
.data(self
.attr_name(true) + '-init');
295 var stickyContainer
= topbar
.parent('.' + self
.settings
.sticky_class
);
298 if (!self
.breakpoint()) {
299 var doToggle
= topbar
.hasClass('expanded');
302 .removeClass('expanded')
304 .removeClass('hover');
311 if (self
.is_sticky(topbar
, stickyContainer
, settings
)) {
312 if (stickyContainer
.hasClass('fixed')) {
313 // Remove the fixed to allow for correct calculation of the offset.
314 stickyContainer
.removeClass('fixed');
316 stickyOffset
= stickyContainer
.offset().top
;
317 if (self
.S(document
.body
).hasClass('f-topbar-fixed')) {
318 stickyOffset
-= topbar
.data('height');
321 topbar
.data('stickyoffset', stickyOffset
);
322 stickyContainer
.addClass('fixed');
324 stickyOffset
= stickyContainer
.offset().top
;
325 topbar
.data('stickyoffset', stickyOffset
);
332 breakpoint: function () {
333 return !matchMedia(Foundation
.media_queries
['topbar']).matches
;
337 return matchMedia(Foundation
.media_queries
['small']).matches
;
340 medium: function () {
341 return matchMedia(Foundation
.media_queries
['medium']).matches
;
345 return matchMedia(Foundation
.media_queries
['large']).matches
;
348 assemble: function (topbar
) {
350 settings
= topbar
.data(this.attr_name(true) + '-init'),
351 section
= self
.S('section, .top-bar-section', topbar
);
353 // Pull element out of the DOM for manipulation
356 self
.S('.has-dropdown>a', section
).each(function () {
357 var $link
= self
.S(this),
358 $dropdown
= $link
.siblings('.dropdown'),
359 url
= $link
.attr('href'),
363 if (!$dropdown
.find('.title.back').length
) {
365 if (settings
.mobile_show_parent_link
== true && url
) {
366 $titleLi
= $('<li class="title back js-generated"><h5><a href="javascript:void(0)"></a></h5></li><li class="parent-link show-for-small-only"><a class="parent-link js-generated" href="' + url
+ '">' + $link
.html() + '</a></li>');
368 $titleLi
= $('<li class="title back js-generated"><h5><a href="javascript:void(0)"></a></h5>');
371 // Copy link to subnav
372 if (settings
.custom_back_text
== true) {
373 $('h5>a', $titleLi
).html(settings
.back_text
);
375 $('h5>a', $titleLi
).html('« ' + $link
.html());
377 $dropdown
.prepend($titleLi
);
381 // Put element back in the DOM
382 section
.appendTo(topbar
);
387 this.assembled(topbar
);
390 assembled: function (topbar
) {
391 topbar
.data(this.attr_name(true), $.extend({}, topbar
.data(this.attr_name(true)), {assembled
: true}));
394 height: function (ul
) {
398 $('> li', ul
).each(function () {
399 total
+= self
.S(this).outerHeight(true);
405 sticky: function () {
408 this.S(window
).on('scroll', function () {
409 self
.update_sticky_positioning();
413 update_sticky_positioning: function () {
414 var klass
= '.' + this.settings
.sticky_class
,
415 $window
= this.S(window
),
418 if (self
.settings
.sticky_topbar
&& self
.is_sticky(this.settings
.sticky_topbar
, this.settings
.sticky_topbar
.parent(), this.settings
)) {
419 var distance
= this.settings
.sticky_topbar
.data('stickyoffset');
420 if (!self
.S(klass
).hasClass('expanded')) {
421 if ($window
.scrollTop() > (distance
)) {
422 if (!self
.S(klass
).hasClass('fixed')) {
423 self
.S(klass
).addClass('fixed');
424 self
.S('body').addClass('f-topbar-fixed');
426 } else if ($window
.scrollTop() <= distance
) {
427 if (self
.S(klass
).hasClass('fixed')) {
428 self
.S(klass
).removeClass('fixed');
429 self
.S('body').removeClass('f-topbar-fixed');
437 this.S(this.scope
).off('.fndtn.topbar');
438 this.S(window
).off('.fndtn.topbar');
441 reflow: function () {
444 }(jQuery
, window
, window
.document
));