2 (function ($, window
, document
, undefined) {
5 Foundation
.libs
.dropdown
= {
12 disabled_class
: 'disabled',
23 init: function (scope
, method
, options
) {
24 Foundation
.inherit(this, 'throttle');
26 $.extend(true, this.settings
, method
, options
);
27 this.bindings(method
, options
);
30 events: function (scope
) {
36 .on('click.fndtn.dropdown', '[' + this.attr_name() + ']', function (e
) {
37 var settings
= S(this).data(self
.attr_name(true) + '-init') || self
.settings
;
38 if (!settings
.is_hover
|| Modernizr
.touch
) {
40 if (S(this).parent('[data-reveal-id]')) {
46 .on('mouseenter.fndtn.dropdown', '[' + this.attr_name() + '], [' + this.attr_name() + '-content]', function (e
) {
51 clearTimeout(self
.timeout
);
53 if ($this.data(self
.data_attr())) {
54 dropdown
= S('#' + $this.data(self
.data_attr()));
58 target
= S('[' + self
.attr_name() + '="' + dropdown
.attr('id') + '"]');
61 var settings
= target
.data(self
.attr_name(true) + '-init') || self
.settings
;
63 if (S(e
.currentTarget
).data(self
.data_attr()) && settings
.is_hover
) {
64 self
.closeall
.call(self
);
67 if (settings
.is_hover
) self
.open
.apply(self
, [dropdown
, target
]);
69 .on('mouseleave.fndtn.dropdown', '[' + this.attr_name() + '], [' + this.attr_name() + '-content]', function (e
) {
73 if ($this.data(self
.data_attr())) {
74 settings
= $this.data(self
.data_attr(true) + '-init') || self
.settings
;
77 var target
= S('[' + self
.attr_name() + '="' + S(this).attr('id') + '"]'),
78 settings
= target
.data(self
.attr_name(true) + '-init') || self
.settings
;
81 self
.timeout
= setTimeout(function () {
82 if ($this.data(self
.data_attr())) {
83 if (settings
.is_hover
) self
.close
.call(self
, S('#' + $this.data(self
.data_attr())));
85 if (settings
.is_hover
) self
.close
.call(self
, $this);
87 }.bind(this), settings
.hover_timeout
);
89 .on('click.fndtn.dropdown', function (e
) {
90 var parent
= S(e
.target
).closest('[' + self
.attr_name() + '-content]');
91 var links
= parent
.find('a');
93 if (links
.length
> 0 && parent
.attr('aria-autoclose') !== "false") {
94 self
.close
.call(self
, S('[' + self
.attr_name() + '-content]'));
97 if (S(e
.target
).closest('[' + self
.attr_name() + ']').length
> 0) {
101 if (!(S(e
.target
).data('revealId')) &&
102 (parent
.length
> 0 && (S(e
.target
).is('[' + self
.attr_name() + '-content]') ||
103 $.contains(parent
.first()[0], e
.target
)))) {
108 self
.close
.call(self
, S('[' + self
.attr_name() + '-content]'));
110 .on('opened.fndtn.dropdown', '[' + self
.attr_name() + '-content]', function () {
111 self
.settings
.opened
.call(this);
113 .on('closed.fndtn.dropdown', '[' + self
.attr_name() + '-content]', function () {
114 self
.settings
.closed
.call(this);
119 .on('resize.fndtn.dropdown', self
.throttle(function () {
120 self
.resize
.call(self
);
126 close: function (dropdown
) {
128 dropdown
.each(function () {
129 var original_target
= $('[' + self
.attr_name() + '=' + dropdown
[0].id
+ ']') || $('aria-controls=' + dropdown
[0].id
+ ']');
130 original_target
.attr('aria-expanded', 'false');
131 if (self
.S(this).hasClass(self
.settings
.active_class
)) {
133 .css(Foundation
.rtl
? 'right' : 'left', '-99999px')
134 .attr('aria-hidden', 'true')
135 .removeClass(self
.settings
.active_class
)
136 .prev('[' + self
.attr_name() + ']')
137 .removeClass(self
.settings
.active_class
)
138 .removeData('target');
140 self
.S(this).trigger('closed').trigger('closed.fndtn.dropdown', [dropdown
]);
143 dropdown
.removeClass('f-open-' + this.attr_name(true));
146 closeall: function () {
148 $.each(self
.S('.f-open-' + this.attr_name(true)), function () {
149 self
.close
.call(self
, self
.S(this));
153 open: function (dropdown
, target
) {
156 .addClass(this.settings
.active_class
), target
);
157 dropdown
.prev('[' + this.attr_name() + ']').addClass(this.settings
.active_class
);
158 dropdown
.data('target', target
.get(0)).trigger('opened').trigger('opened.fndtn.dropdown', [dropdown
, target
]);
159 dropdown
.attr('aria-hidden', 'false');
160 target
.attr('aria-expanded', 'true');
162 dropdown
.addClass('f-open-' + this.attr_name(true));
165 data_attr: function () {
166 if (this.namespace.length
> 0) {
167 return this.namespace + '-' + this.name
;
173 toggle: function (target
) {
174 if (target
.hasClass(this.settings
.disabled_class
)) {
177 var dropdown
= this.S('#' + target
.data(this.data_attr()));
178 if (dropdown
.length
=== 0) {
179 // No dropdown found, not continuing
183 this.close
.call(this, this.S('[' + this.attr_name() + '-content]').not(dropdown
));
185 if (dropdown
.hasClass(this.settings
.active_class
)) {
186 this.close
.call(this, dropdown
);
187 if (dropdown
.data('target') !== target
.get(0))
188 this.open
.call(this, dropdown
, target
);
190 this.open
.call(this, dropdown
, target
);
194 resize: function () {
195 var dropdown
= this.S('[' + this.attr_name() + '-content].open'),
196 target
= this.S('[' + this.attr_name() + '="' + dropdown
.attr('id') + '"]');
198 if (dropdown
.length
&& target
.length
) {
199 this.css(dropdown
, target
);
203 css: function (dropdown
, target
) {
204 var left_offset
= Math
.max((target
.width() - dropdown
.width()) / 2, 8),
205 settings
= target
.data(this.attr_name(true) + '-init') || this.settings
;
210 var p
= this.dirs
.bottom
.call(dropdown
, target
, settings
);
212 dropdown
.attr('style', '').removeClass('drop-left drop-right drop-top').css({
213 position
: 'absolute',
219 dropdown
.css(Foundation
.rtl
? 'right' : 'left', left_offset
);
222 this.style(dropdown
, target
, settings
);
228 style: function (dropdown
, target
, settings
) {
229 var css
= $.extend({position
: 'absolute'},
230 this.dirs
[settings
.align
].call(dropdown
, target
, settings
));
232 dropdown
.attr('style', '').css(css
);
235 // return CSS property object
236 // `this` is the dropdown
238 // Calculate target offset
239 _base: function (t
) {
240 var o_p
= this.offsetParent(),
247 //set some flags on the p object to pass along
251 p
.leftRightFlag
= false;
253 //lets see if the panel will be off the screen
254 //get the actual width of the page and store it
256 if (document
.getElementsByClassName('row')[0]) {
257 actualBodyWidth
= document
.getElementsByClassName('row')[0].clientWidth
;
259 actualBodyWidth
= window
.outerWidth
;
262 var actualMarginWidth
= (window
.outerWidth
- actualBodyWidth
) / 2;
263 var actualBoundary
= actualBodyWidth
;
265 if (!this.hasClass('mega')) {
267 if (t
.offset().top
<= this.outerHeight()) {
269 actualBoundary
= window
.outerWidth
- actualMarginWidth
;
270 p
.leftRightFlag
= true;
274 if (t
.offset().left
+ this.outerWidth() > t
.offset().left
+ actualMarginWidth
&& t
.offset().left
- actualMarginWidth
> this.outerWidth()) {
280 if (t
.offset().left
- this.outerWidth() <= 0) {
289 top: function (t
, s
) {
290 var self
= Foundation
.libs
.dropdown
,
291 p
= self
.dirs
._base
.call(this, t
);
293 this.addClass('drop-top');
295 if (p
.missTop
== true) {
296 p
.top
= p
.top
+ t
.outerHeight() + this.outerHeight();
297 this.removeClass('drop-top');
300 if (p
.missRight
== true) {
301 p
.left
= p
.left
- this.outerWidth() + t
.outerWidth();
304 if (t
.outerWidth() < this.outerWidth() || self
.small() || this.hasClass(s
.mega_menu
)) {
305 self
.adjust_pip(this, t
, s
, p
);
308 if (Foundation
.rtl
) {
310 left
: p
.left
- this.outerWidth() + t
.outerWidth(),
311 top
: p
.top
- this.outerHeight()
315 return {left
: p
.left
, top
: p
.top
- this.outerHeight()};
318 bottom: function (t
, s
) {
319 var self
= Foundation
.libs
.dropdown
,
320 p
= self
.dirs
._base
.call(this, t
);
322 if (p
.missRight
== true) {
323 p
.left
= p
.left
- this.outerWidth() + t
.outerWidth();
326 if (t
.outerWidth() < this.outerWidth() || self
.small() || this.hasClass(s
.mega_menu
)) {
327 self
.adjust_pip(this, t
, s
, p
);
331 return {left
: p
.left
- this.outerWidth() + t
.outerWidth(), top
: p
.top
+ t
.outerHeight()};
334 return {left
: p
.left
, top
: p
.top
+ t
.outerHeight()};
337 left: function (t
, s
) {
338 var p
= Foundation
.libs
.dropdown
.dirs
._base
.call(this, t
);
340 this.addClass('drop-left');
342 if (p
.missLeft
== true) {
343 p
.left
= p
.left
+ this.outerWidth();
344 p
.top
= p
.top
+ t
.outerHeight();
345 this.removeClass('drop-left');
348 return {left
: p
.left
- this.outerWidth(), top
: p
.top
};
351 right: function (t
, s
) {
352 var p
= Foundation
.libs
.dropdown
.dirs
._base
.call(this, t
);
354 this.addClass('drop-right');
356 if (p
.missRight
== true) {
357 p
.left
= p
.left
- this.outerWidth();
358 p
.top
= p
.top
+ t
.outerHeight();
359 this.removeClass('drop-right');
361 p
.triggeredRight
= true;
364 var self
= Foundation
.libs
.dropdown
;
366 if (t
.outerWidth() < this.outerWidth() || self
.small() || this.hasClass(s
.mega_menu
)) {
367 self
.adjust_pip(this, t
, s
, p
);
370 return {left
: p
.left
+ t
.outerWidth(), top
: p
.top
};
374 // Insert rule to style psuedo elements
375 adjust_pip: function (dropdown
, target
, settings
, position
) {
376 var sheet
= Foundation
.stylesheet
,
379 if (dropdown
.hasClass(settings
.mega_class
)) {
380 pip_offset_base
= position
.left
+ (target
.outerWidth() / 2) - 8;
382 else if (this.small()) {
383 pip_offset_base
+= position
.left
- 8;
386 this.rule_idx
= sheet
.cssRules
.length
;
389 var sel_before
= '.f-dropdown.open:before',
390 sel_after
= '.f-dropdown.open:after',
391 css_before
= 'left: ' + pip_offset_base
+ 'px;',
392 css_after
= 'left: ' + (pip_offset_base
- 1) + 'px;';
394 if (position
.missRight
== true) {
395 pip_offset_base
= dropdown
.outerWidth() - 23;
396 sel_before
= '.f-dropdown.open:before',
397 sel_after
= '.f-dropdown.open:after',
398 css_before
= 'left: ' + pip_offset_base
+ 'px;',
399 css_after
= 'left: ' + (pip_offset_base
- 1) + 'px;';
402 //just a case where right is fired, but its not missing right
403 if (position
.triggeredRight
== true) {
404 sel_before
= '.f-dropdown.open:before',
405 sel_after
= '.f-dropdown.open:after',
406 css_before
= 'left:-12px;',
407 css_after
= 'left:-14px;';
410 if (sheet
.insertRule
) {
411 sheet
.insertRule([sel_before
, '{', css_before
, '}'].join(' '), this.rule_idx
);
412 sheet
.insertRule([sel_after
, '{', css_after
, '}'].join(' '), this.rule_idx
+ 1);
414 sheet
.addRule(sel_before
, css_before
, this.rule_idx
);
415 sheet
.addRule(sel_after
, css_after
, this.rule_idx
+ 1);
419 // Remove old dropdown rule index
420 clear_idx: function () {
421 var sheet
= Foundation
.stylesheet
;
423 if (typeof this.rule_idx
!== 'undefined') {
424 sheet
.deleteRule(this.rule_idx
);
425 sheet
.deleteRule(this.rule_idx
);
426 delete this.rule_idx
;
431 return matchMedia(Foundation
.media_queries
.small
).matches
&& !matchMedia(Foundation
.media_queries
.medium
).matches
;
435 this.S(this.scope
).off('.fndtn.dropdown');
436 this.S('html, body').off('.fndtn.dropdown');
437 this.S(window
).off('.fndtn.dropdown');
438 this.S('[data-dropdown-content]').off('.fndtn.dropdown');
441 reflow: function () {
444 }(jQuery
, window
, window
.document
));