Mercurial > web-octave
comparison js/foundation/foundation.dropdown.js @ 0:7abe02bf29ec
initial commit
author | Alex Krolick <whokilledtheelectricmonk@gmail.com> |
---|---|
date | Sat, 07 Nov 2015 18:04:42 -0800 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:7abe02bf29ec |
---|---|
1 ;(function ($, window, document, undefined) { | |
2 'use strict'; | |
3 | |
4 Foundation.libs.dropdown = { | |
5 name : 'dropdown', | |
6 | |
7 version : '5.5.3', | |
8 | |
9 settings : { | |
10 active_class : 'open', | |
11 disabled_class : 'disabled', | |
12 mega_class : 'mega', | |
13 align : 'bottom', | |
14 is_hover : false, | |
15 hover_timeout : 150, | |
16 opened : function () {}, | |
17 closed : function () {} | |
18 }, | |
19 | |
20 init : function (scope, method, options) { | |
21 Foundation.inherit(this, 'throttle'); | |
22 | |
23 $.extend(true, this.settings, method, options); | |
24 this.bindings(method, options); | |
25 }, | |
26 | |
27 events : function (scope) { | |
28 var self = this, | |
29 S = self.S; | |
30 | |
31 S(this.scope) | |
32 .off('.dropdown') | |
33 .on('click.fndtn.dropdown', '[' + this.attr_name() + ']', function (e) { | |
34 var settings = S(this).data(self.attr_name(true) + '-init') || self.settings; | |
35 if (!settings.is_hover || Modernizr.touch) { | |
36 e.preventDefault(); | |
37 if (S(this).parent('[data-reveal-id]').length) { | |
38 e.stopPropagation(); | |
39 } | |
40 self.toggle($(this)); | |
41 } | |
42 }) | |
43 .on('mouseenter.fndtn.dropdown', '[' + this.attr_name() + '], [' + this.attr_name() + '-content]', function (e) { | |
44 var $this = S(this), | |
45 dropdown, | |
46 target; | |
47 | |
48 clearTimeout(self.timeout); | |
49 | |
50 if ($this.data(self.data_attr())) { | |
51 dropdown = S('#' + $this.data(self.data_attr())); | |
52 target = $this; | |
53 } else { | |
54 dropdown = $this; | |
55 target = S('[' + self.attr_name() + '="' + dropdown.attr('id') + '"]'); | |
56 } | |
57 | |
58 var settings = target.data(self.attr_name(true) + '-init') || self.settings; | |
59 | |
60 if (S(e.currentTarget).data(self.data_attr()) && settings.is_hover) { | |
61 self.closeall.call(self); | |
62 } | |
63 | |
64 if (settings.is_hover) { | |
65 self.open.apply(self, [dropdown, target]); | |
66 } | |
67 }) | |
68 .on('mouseleave.fndtn.dropdown', '[' + this.attr_name() + '], [' + this.attr_name() + '-content]', function (e) { | |
69 var $this = S(this); | |
70 var settings; | |
71 | |
72 if ($this.data(self.data_attr())) { | |
73 settings = $this.data(self.data_attr(true) + '-init') || self.settings; | |
74 } else { | |
75 var target = S('[' + self.attr_name() + '="' + S(this).attr('id') + '"]'), | |
76 settings = target.data(self.attr_name(true) + '-init') || self.settings; | |
77 } | |
78 | |
79 self.timeout = setTimeout(function () { | |
80 if ($this.data(self.data_attr())) { | |
81 if (settings.is_hover) { | |
82 self.close.call(self, S('#' + $this.data(self.data_attr()))); | |
83 } | |
84 } else { | |
85 if (settings.is_hover) { | |
86 self.close.call(self, $this); | |
87 } | |
88 } | |
89 }.bind(this), settings.hover_timeout); | |
90 }) | |
91 .on('click.fndtn.dropdown', function (e) { | |
92 var parent = S(e.target).closest('[' + self.attr_name() + '-content]'); | |
93 var links = parent.find('a'); | |
94 | |
95 if (links.length > 0 && parent.attr('aria-autoclose') !== 'false') { | |
96 self.close.call(self, S('[' + self.attr_name() + '-content]')); | |
97 } | |
98 | |
99 if (e.target !== document && !$.contains(document.documentElement, e.target)) { | |
100 return; | |
101 } | |
102 | |
103 if (S(e.target).closest('[' + self.attr_name() + ']').length > 0) { | |
104 return; | |
105 } | |
106 | |
107 if (!(S(e.target).data('revealId')) && | |
108 (parent.length > 0 && (S(e.target).is('[' + self.attr_name() + '-content]') || | |
109 $.contains(parent.first()[0], e.target)))) { | |
110 e.stopPropagation(); | |
111 return; | |
112 } | |
113 | |
114 self.close.call(self, S('[' + self.attr_name() + '-content]')); | |
115 }) | |
116 .on('opened.fndtn.dropdown', '[' + self.attr_name() + '-content]', function () { | |
117 self.settings.opened.call(this); | |
118 }) | |
119 .on('closed.fndtn.dropdown', '[' + self.attr_name() + '-content]', function () { | |
120 self.settings.closed.call(this); | |
121 }); | |
122 | |
123 S(window) | |
124 .off('.dropdown') | |
125 .on('resize.fndtn.dropdown', self.throttle(function () { | |
126 self.resize.call(self); | |
127 }, 50)); | |
128 | |
129 this.resize(); | |
130 }, | |
131 | |
132 close : function (dropdown) { | |
133 var self = this; | |
134 dropdown.each(function (idx) { | |
135 var original_target = $('[' + self.attr_name() + '=' + dropdown[idx].id + ']') || $('aria-controls=' + dropdown[idx].id + ']'); | |
136 original_target.attr('aria-expanded', 'false'); | |
137 if (self.S(this).hasClass(self.settings.active_class)) { | |
138 self.S(this) | |
139 .css(Foundation.rtl ? 'right' : 'left', '-99999px') | |
140 .attr('aria-hidden', 'true') | |
141 .removeClass(self.settings.active_class) | |
142 .prev('[' + self.attr_name() + ']') | |
143 .removeClass(self.settings.active_class) | |
144 .removeData('target'); | |
145 | |
146 self.S(this).trigger('closed.fndtn.dropdown', [dropdown]); | |
147 } | |
148 }); | |
149 dropdown.removeClass('f-open-' + this.attr_name(true)); | |
150 }, | |
151 | |
152 closeall : function () { | |
153 var self = this; | |
154 $.each(self.S('.f-open-' + this.attr_name(true)), function () { | |
155 self.close.call(self, self.S(this)); | |
156 }); | |
157 }, | |
158 | |
159 open : function (dropdown, target) { | |
160 this | |
161 .css(dropdown | |
162 .addClass(this.settings.active_class), target); | |
163 dropdown.prev('[' + this.attr_name() + ']').addClass(this.settings.active_class); | |
164 dropdown.data('target', target.get(0)).trigger('opened.fndtn.dropdown', [dropdown, target]); | |
165 dropdown.attr('aria-hidden', 'false'); | |
166 target.attr('aria-expanded', 'true'); | |
167 dropdown.focus(); | |
168 dropdown.addClass('f-open-' + this.attr_name(true)); | |
169 }, | |
170 | |
171 data_attr : function () { | |
172 if (this.namespace.length > 0) { | |
173 return this.namespace + '-' + this.name; | |
174 } | |
175 | |
176 return this.name; | |
177 }, | |
178 | |
179 toggle : function (target) { | |
180 if (target.hasClass(this.settings.disabled_class)) { | |
181 return; | |
182 } | |
183 var dropdown = this.S('#' + target.data(this.data_attr())); | |
184 if (dropdown.length === 0) { | |
185 // No dropdown found, not continuing | |
186 return; | |
187 } | |
188 | |
189 this.close.call(this, this.S('[' + this.attr_name() + '-content]').not(dropdown)); | |
190 | |
191 if (dropdown.hasClass(this.settings.active_class)) { | |
192 this.close.call(this, dropdown); | |
193 if (dropdown.data('target') !== target.get(0)) { | |
194 this.open.call(this, dropdown, target); | |
195 } | |
196 } else { | |
197 this.open.call(this, dropdown, target); | |
198 } | |
199 }, | |
200 | |
201 resize : function () { | |
202 var dropdown = this.S('[' + this.attr_name() + '-content].open'); | |
203 var target = $(dropdown.data("target")); | |
204 | |
205 if (dropdown.length && target.length) { | |
206 this.css(dropdown, target); | |
207 } | |
208 }, | |
209 | |
210 css : function (dropdown, target) { | |
211 var left_offset = Math.max((target.width() - dropdown.width()) / 2, 8), | |
212 settings = target.data(this.attr_name(true) + '-init') || this.settings, | |
213 parentOverflow = dropdown.parent().css('overflow-y') || dropdown.parent().css('overflow'); | |
214 | |
215 this.clear_idx(); | |
216 | |
217 | |
218 | |
219 if (this.small()) { | |
220 var p = this.dirs.bottom.call(dropdown, target, settings); | |
221 | |
222 dropdown.attr('style', '').removeClass('drop-left drop-right drop-top').css({ | |
223 position : 'absolute', | |
224 width : '95%', | |
225 'max-width' : 'none', | |
226 top : p.top | |
227 }); | |
228 | |
229 dropdown.css(Foundation.rtl ? 'right' : 'left', left_offset); | |
230 } | |
231 // detect if dropdown is in an overflow container | |
232 else if (parentOverflow !== 'visible') { | |
233 var offset = target[0].offsetTop + target[0].offsetHeight; | |
234 | |
235 dropdown.attr('style', '').css({ | |
236 position : 'absolute', | |
237 top : offset | |
238 }); | |
239 | |
240 dropdown.css(Foundation.rtl ? 'right' : 'left', left_offset); | |
241 } | |
242 else { | |
243 | |
244 this.style(dropdown, target, settings); | |
245 } | |
246 | |
247 return dropdown; | |
248 }, | |
249 | |
250 style : function (dropdown, target, settings) { | |
251 var css = $.extend({position : 'absolute'}, | |
252 this.dirs[settings.align].call(dropdown, target, settings)); | |
253 | |
254 dropdown.attr('style', '').css(css); | |
255 }, | |
256 | |
257 // return CSS property object | |
258 // `this` is the dropdown | |
259 dirs : { | |
260 // Calculate target offset | |
261 _base : function (t, s) { | |
262 var o_p = this.offsetParent(), | |
263 o = o_p.offset(), | |
264 p = t.offset(); | |
265 | |
266 p.top -= o.top; | |
267 p.left -= o.left; | |
268 | |
269 //set some flags on the p object to pass along | |
270 p.missRight = false; | |
271 p.missTop = false; | |
272 p.missLeft = false; | |
273 p.leftRightFlag = false; | |
274 | |
275 //lets see if the panel will be off the screen | |
276 //get the actual width of the page and store it | |
277 var actualBodyWidth; | |
278 var windowWidth = window.innerWidth; | |
279 | |
280 if (document.getElementsByClassName('row')[0]) { | |
281 actualBodyWidth = document.getElementsByClassName('row')[0].clientWidth; | |
282 } else { | |
283 actualBodyWidth = windowWidth; | |
284 } | |
285 | |
286 var actualMarginWidth = (windowWidth - actualBodyWidth) / 2; | |
287 var actualBoundary = actualBodyWidth; | |
288 | |
289 if (!this.hasClass('mega') && !s.ignore_repositioning) { | |
290 var outerWidth = this.outerWidth(); | |
291 var o_left = t.offset().left; | |
292 | |
293 //miss top | |
294 if (t.offset().top <= this.outerHeight()) { | |
295 p.missTop = true; | |
296 actualBoundary = windowWidth - actualMarginWidth; | |
297 p.leftRightFlag = true; | |
298 } | |
299 | |
300 //miss right | |
301 if (o_left + outerWidth > o_left + actualMarginWidth && o_left - actualMarginWidth > outerWidth) { | |
302 p.missRight = true; | |
303 p.missLeft = false; | |
304 } | |
305 | |
306 //miss left | |
307 if (o_left - outerWidth <= 0) { | |
308 p.missLeft = true; | |
309 p.missRight = false; | |
310 } | |
311 } | |
312 | |
313 return p; | |
314 }, | |
315 | |
316 top : function (t, s) { | |
317 var self = Foundation.libs.dropdown, | |
318 p = self.dirs._base.call(this, t, s); | |
319 | |
320 this.addClass('drop-top'); | |
321 | |
322 if (p.missTop == true) { | |
323 p.top = p.top + t.outerHeight() + this.outerHeight(); | |
324 this.removeClass('drop-top'); | |
325 } | |
326 | |
327 if (p.missRight == true) { | |
328 p.left = p.left - this.outerWidth() + t.outerWidth(); | |
329 } | |
330 | |
331 if (t.outerWidth() < this.outerWidth() || self.small() || this.hasClass(s.mega_menu)) { | |
332 self.adjust_pip(this, t, s, p); | |
333 } | |
334 | |
335 if (Foundation.rtl) { | |
336 return {left : p.left - this.outerWidth() + t.outerWidth(), | |
337 top : p.top - this.outerHeight()}; | |
338 } | |
339 | |
340 return {left : p.left, top : p.top - this.outerHeight()}; | |
341 }, | |
342 | |
343 bottom : function (t, s) { | |
344 var self = Foundation.libs.dropdown, | |
345 p = self.dirs._base.call(this, t, s); | |
346 | |
347 if (p.missRight == true) { | |
348 p.left = p.left - this.outerWidth() + t.outerWidth(); | |
349 } | |
350 | |
351 if (t.outerWidth() < this.outerWidth() || self.small() || this.hasClass(s.mega_menu)) { | |
352 self.adjust_pip(this, t, s, p); | |
353 } | |
354 | |
355 if (self.rtl) { | |
356 return {left : p.left - this.outerWidth() + t.outerWidth(), top : p.top + t.outerHeight()}; | |
357 } | |
358 | |
359 return {left : p.left, top : p.top + t.outerHeight()}; | |
360 }, | |
361 | |
362 left : function (t, s) { | |
363 var p = Foundation.libs.dropdown.dirs._base.call(this, t, s); | |
364 | |
365 this.addClass('drop-left'); | |
366 | |
367 if (p.missLeft == true) { | |
368 p.left = p.left + this.outerWidth(); | |
369 p.top = p.top + t.outerHeight(); | |
370 this.removeClass('drop-left'); | |
371 } | |
372 | |
373 return {left : p.left - this.outerWidth(), top : p.top}; | |
374 }, | |
375 | |
376 right : function (t, s) { | |
377 var p = Foundation.libs.dropdown.dirs._base.call(this, t, s); | |
378 | |
379 this.addClass('drop-right'); | |
380 | |
381 if (p.missRight == true) { | |
382 p.left = p.left - this.outerWidth(); | |
383 p.top = p.top + t.outerHeight(); | |
384 this.removeClass('drop-right'); | |
385 } else { | |
386 p.triggeredRight = true; | |
387 } | |
388 | |
389 var self = Foundation.libs.dropdown; | |
390 | |
391 if (t.outerWidth() < this.outerWidth() || self.small() || this.hasClass(s.mega_menu)) { | |
392 self.adjust_pip(this, t, s, p); | |
393 } | |
394 | |
395 return {left : p.left + t.outerWidth(), top : p.top}; | |
396 } | |
397 }, | |
398 | |
399 // Insert rule to style psuedo elements | |
400 adjust_pip : function (dropdown, target, settings, position) { | |
401 var sheet = Foundation.stylesheet, | |
402 pip_offset_base = 8; | |
403 | |
404 if (dropdown.hasClass(settings.mega_class)) { | |
405 pip_offset_base = position.left + (target.outerWidth() / 2) - 8; | |
406 } else if (this.small()) { | |
407 pip_offset_base += position.left - 8; | |
408 } | |
409 | |
410 this.rule_idx = sheet.cssRules.length; | |
411 | |
412 //default | |
413 var sel_before = '.f-dropdown.open:before', | |
414 sel_after = '.f-dropdown.open:after', | |
415 css_before = 'left: ' + pip_offset_base + 'px;', | |
416 css_after = 'left: ' + (pip_offset_base - 1) + 'px;'; | |
417 | |
418 if (position.missRight == true) { | |
419 pip_offset_base = dropdown.outerWidth() - 23; | |
420 sel_before = '.f-dropdown.open:before', | |
421 sel_after = '.f-dropdown.open:after', | |
422 css_before = 'left: ' + pip_offset_base + 'px;', | |
423 css_after = 'left: ' + (pip_offset_base - 1) + 'px;'; | |
424 } | |
425 | |
426 //just a case where right is fired, but its not missing right | |
427 if (position.triggeredRight == true) { | |
428 sel_before = '.f-dropdown.open:before', | |
429 sel_after = '.f-dropdown.open:after', | |
430 css_before = 'left:-12px;', | |
431 css_after = 'left:-14px;'; | |
432 } | |
433 | |
434 if (sheet.insertRule) { | |
435 sheet.insertRule([sel_before, '{', css_before, '}'].join(' '), this.rule_idx); | |
436 sheet.insertRule([sel_after, '{', css_after, '}'].join(' '), this.rule_idx + 1); | |
437 } else { | |
438 sheet.addRule(sel_before, css_before, this.rule_idx); | |
439 sheet.addRule(sel_after, css_after, this.rule_idx + 1); | |
440 } | |
441 }, | |
442 | |
443 // Remove old dropdown rule index | |
444 clear_idx : function () { | |
445 var sheet = Foundation.stylesheet; | |
446 | |
447 if (typeof this.rule_idx !== 'undefined') { | |
448 sheet.deleteRule(this.rule_idx); | |
449 sheet.deleteRule(this.rule_idx); | |
450 delete this.rule_idx; | |
451 } | |
452 }, | |
453 | |
454 small : function () { | |
455 return matchMedia(Foundation.media_queries.small).matches && | |
456 !matchMedia(Foundation.media_queries.medium).matches; | |
457 }, | |
458 | |
459 off : function () { | |
460 this.S(this.scope).off('.fndtn.dropdown'); | |
461 this.S('html, body').off('.fndtn.dropdown'); | |
462 this.S(window).off('.fndtn.dropdown'); | |
463 this.S('[data-dropdown-content]').off('.fndtn.dropdown'); | |
464 }, | |
465 | |
466 reflow : function () {} | |
467 }; | |
468 }(jQuery, window, window.document)); |