summaryrefslogtreecommitdiff
path: root/js/core/dropdown.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/core/dropdown.js')
-rwxr-xr-xjs/core/dropdown.js525
1 files changed, 525 insertions, 0 deletions
diff --git a/js/core/dropdown.js b/js/core/dropdown.js
new file mode 100755
index 0000000..1fa7035
--- /dev/null
+++ b/js/core/dropdown.js
@@ -0,0 +1,525 @@
1/*! UIkit 2.26.4 | http://www.getuikit.com | (c) 2014 YOOtheme | MIT License */
2(function(UI) {
3
4 "use strict";
5
6 var active = false, hoverIdle, flips = {
7 'x': {
8 "bottom-left" : 'bottom-right',
9 "bottom-right" : 'bottom-left',
10 "bottom-center" : 'bottom-center',
11 "top-left" : 'top-right',
12 "top-right" : 'top-left',
13 "top-center" : 'top-center',
14 "left-top" : 'right-top',
15 "left-bottom" : 'right-bottom',
16 "left-center" : 'right-center',
17 "right-top" : 'left-top',
18 "right-bottom" : 'left-bottom',
19 "right-center" : 'left-center'
20 },
21 'y': {
22 "bottom-left" : 'top-left',
23 "bottom-right" : 'top-right',
24 "bottom-center" : 'top-center',
25 "top-left" : 'bottom-left',
26 "top-right" : 'bottom-right',
27 "top-center" : 'bottom-center',
28 "left-top" : 'left-bottom',
29 "left-bottom" : 'left-top',
30 "left-center" : 'left-center',
31 "right-top" : 'right-bottom',
32 "right-bottom" : 'right-top',
33 "right-center" : 'right-center'
34 },
35 'xy': {
36 "bottom-left" : 'top-right',
37 "bottom-right" : 'top-left',
38 "bottom-center" : 'top-center',
39 "top-left" : 'bottom-right',
40 "top-right" : 'bottom-left',
41 "top-center" : 'bottom-center',
42 "left-top" : 'right-bottom',
43 "left-bottom" : 'right-top',
44 "left-center" : 'right-center',
45 "right-top" : 'left-bottom',
46 "right-bottom" : 'left-top',
47 "right-center" : 'left-center'
48 }
49 };
50
51 UI.component('dropdown', {
52
53 defaults: {
54 'mode' : 'hover',
55 'pos' : 'bottom-left',
56 'offset' : 0,
57 'remaintime' : 800,
58 'justify' : false,
59 'boundary' : UI.$win,
60 'delay' : 0,
61 'dropdownSelector': '.uk-dropdown,.uk-dropdown-blank',
62 'hoverDelayIdle' : 250,
63 'preventflip' : false
64 },
65
66 remainIdle: false,
67
68 boot: function() {
69
70 var triggerevent = UI.support.touch ? "click" : "mouseenter";
71
72 // init code
73 UI.$html.on(triggerevent+".dropdown.uikit", "[data-uk-dropdown]", function(e) {
74
75 var ele = UI.$(this);
76
77 if (!ele.data("dropdown")) {
78
79 var dropdown = UI.dropdown(ele, UI.Utils.options(ele.attr("data-uk-dropdown")));
80
81 if (triggerevent=="click" || (triggerevent=="mouseenter" && dropdown.options.mode=="hover")) {
82 dropdown.element.trigger(triggerevent);
83 }
84
85 if (dropdown.element.find(dropdown.options.dropdownSelector).length) {
86 e.preventDefault();
87 }
88 }
89 });
90 },
91
92 init: function() {
93
94 var $this = this;
95
96 this.dropdown = this.find(this.options.dropdownSelector);
97 this.offsetParent = this.dropdown.parents().filter(function() {
98 return UI.$.inArray(UI.$(this).css('position'), ['relative', 'fixed', 'absolute']) !== -1;
99 }).slice(0,1);
100
101 this.centered = this.dropdown.hasClass('uk-dropdown-center');
102 this.justified = this.options.justify ? UI.$(this.options.justify) : false;
103
104 this.boundary = UI.$(this.options.boundary);
105
106 if (!this.boundary.length) {
107 this.boundary = UI.$win;
108 }
109
110 // legacy DEPRECATED!
111 if (this.dropdown.hasClass('uk-dropdown-up')) {
112 this.options.pos = 'top-left';
113 }
114 if (this.dropdown.hasClass('uk-dropdown-flip')) {
115 this.options.pos = this.options.pos.replace('left','right');
116 }
117 if (this.dropdown.hasClass('uk-dropdown-center')) {
118 this.options.pos = this.options.pos.replace(/(left|right)/,'center');
119 }
120 //-- end legacy
121
122 // Init ARIA
123 this.element.attr('aria-haspopup', 'true');
124 this.element.attr('aria-expanded', this.element.hasClass("uk-open"));
125
126 if (this.options.mode == "click" || UI.support.touch) {
127
128 this.on("click.uk.dropdown", function(e) {
129
130 var $target = UI.$(e.target);
131
132 if (!$target.parents($this.options.dropdownSelector).length) {
133
134 if ($target.is("a[href='#']") || $target.parent().is("a[href='#']") || ($this.dropdown.length && !$this.dropdown.is(":visible")) ){
135 e.preventDefault();
136 }
137
138 $target.blur();
139 }
140
141 if (!$this.element.hasClass('uk-open')) {
142
143 $this.show();
144
145 } else {
146
147 if (!$this.dropdown.find(e.target).length || $target.is(".uk-dropdown-close") || $target.parents(".uk-dropdown-close").length) {
148 $this.hide();
149 }
150 }
151 });
152
153 } else {
154
155 this.on("mouseenter", function(e) {
156
157 $this.trigger('pointerenter.uk.dropdown', [$this]);
158
159 if ($this.remainIdle) {
160 clearTimeout($this.remainIdle);
161 }
162
163 if (hoverIdle) {
164 clearTimeout(hoverIdle);
165 }
166
167 if (active && active == $this) {
168 return;
169 }
170
171 // pseudo manuAim
172 if (active && active != $this) {
173
174 hoverIdle = setTimeout(function() {
175 hoverIdle = setTimeout($this.show.bind($this), $this.options.delay);
176 }, $this.options.hoverDelayIdle);
177
178 } else {
179
180 hoverIdle = setTimeout($this.show.bind($this), $this.options.delay);
181 }
182
183 }).on("mouseleave", function() {
184
185 if (hoverIdle) {
186 clearTimeout(hoverIdle);
187 }
188
189 $this.remainIdle = setTimeout(function() {
190 if (active && active == $this) $this.hide();
191 }, $this.options.remaintime);
192
193 $this.trigger('pointerleave.uk.dropdown', [$this]);
194
195 }).on("click", function(e){
196
197 var $target = UI.$(e.target);
198
199 if ($this.remainIdle) {
200 clearTimeout($this.remainIdle);
201 }
202
203 if (active && active == $this) {
204 if (!$this.dropdown.find(e.target).length || $target.is(".uk-dropdown-close") || $target.parents(".uk-dropdown-close").length) {
205 $this.hide();
206 }
207 return;
208 }
209
210 if ($target.is("a[href='#']") || $target.parent().is("a[href='#']")){
211 e.preventDefault();
212 }
213
214 $this.show();
215 });
216 }
217 },
218
219 show: function(){
220
221 UI.$html.off("click.outer.dropdown");
222
223 if (active && active != this) {
224 active.hide(true);
225 }
226
227 if (hoverIdle) {
228 clearTimeout(hoverIdle);
229 }
230
231 this.trigger('beforeshow.uk.dropdown', [this]);
232
233 this.checkDimensions();
234 this.element.addClass('uk-open');
235
236 // Update ARIA
237 this.element.attr('aria-expanded', 'true');
238
239 this.trigger('show.uk.dropdown', [this]);
240
241 UI.Utils.checkDisplay(this.dropdown, true);
242 active = this;
243
244 this.registerOuterClick();
245 },
246
247 hide: function(force) {
248
249 this.trigger('beforehide.uk.dropdown', [this, force]);
250
251 this.element.removeClass('uk-open');
252
253 if (this.remainIdle) {
254 clearTimeout(this.remainIdle);
255 }
256
257 this.remainIdle = false;
258
259 // Update ARIA
260 this.element.attr('aria-expanded', 'false');
261
262 this.trigger('hide.uk.dropdown', [this, force]);
263
264 if (active == this) active = false;
265 },
266
267 registerOuterClick: function(){
268
269 var $this = this;
270
271 UI.$html.off("click.outer.dropdown");
272
273 setTimeout(function() {
274
275 UI.$html.on("click.outer.dropdown", function(e) {
276
277 if (hoverIdle) {
278 clearTimeout(hoverIdle);
279 }
280
281 var $target = UI.$(e.target);
282
283 if (active == $this && !$this.element.find(e.target).length) {
284 $this.hide(true);
285 UI.$html.off("click.outer.dropdown");
286 }
287 });
288 }, 10);
289 },
290
291 checkDimensions: function() {
292
293 if (!this.dropdown.length) return;
294
295 // reset
296 this.dropdown.removeClass('uk-dropdown-top uk-dropdown-bottom uk-dropdown-left uk-dropdown-right uk-dropdown-stack').css({
297 'top-left':'',
298 'left':'',
299 'margin-left' :'',
300 'margin-right':''
301 });
302
303 if (this.justified && this.justified.length) {
304 this.dropdown.css("min-width", "");
305 }
306
307 var $this = this,
308 pos = UI.$.extend({}, this.offsetParent.offset(), {width: this.offsetParent[0].offsetWidth, height: this.offsetParent[0].offsetHeight}),
309 posoffset = this.options.offset,
310 dropdown = this.dropdown,
311 offset = dropdown.show().offset() || {left: 0, top: 0},
312 width = dropdown.outerWidth(),
313 height = dropdown.outerHeight(),
314 boundarywidth = this.boundary.width(),
315 boundaryoffset = this.boundary[0] !== window && this.boundary.offset() ? this.boundary.offset(): {top:0, left:0},
316 dpos = this.options.pos;
317
318 var variants = {
319 "bottom-left" : {top: 0 + pos.height + posoffset, left: 0},
320 "bottom-right" : {top: 0 + pos.height + posoffset, left: 0 + pos.width - width},
321 "bottom-center" : {top: 0 + pos.height + posoffset, left: 0 + pos.width / 2 - width / 2},
322 "top-left" : {top: 0 - height - posoffset, left: 0},
323 "top-right" : {top: 0 - height - posoffset, left: 0 + pos.width - width},
324 "top-center" : {top: 0 - height - posoffset, left: 0 + pos.width / 2 - width / 2},
325 "left-top" : {top: 0, left: 0 - width - posoffset},
326 "left-bottom" : {top: 0 + pos.height - height, left: 0 - width - posoffset},
327 "left-center" : {top: 0 + pos.height / 2 - height / 2, left: 0 - width - posoffset},
328 "right-top" : {top: 0, left: 0 + pos.width + posoffset},
329 "right-bottom" : {top: 0 + pos.height - height, left: 0 + pos.width + posoffset},
330 "right-center" : {top: 0 + pos.height / 2 - height / 2, left: 0 + pos.width + posoffset}
331 },
332 css = {},
333 pp;
334
335 pp = dpos.split('-');
336 css = variants[dpos] ? variants[dpos] : variants['bottom-left'];
337
338 // justify dropdown
339 if (this.justified && this.justified.length) {
340 justify(dropdown.css({left:0}), this.justified, boundarywidth);
341 } else {
342
343 if (this.options.preventflip !== true) {
344
345 var fdpos;
346
347 switch(this.checkBoundary(pos.left + css.left, pos.top + css.top, width, height, boundarywidth)) {
348 case "x":
349 if(this.options.preventflip !=='x') fdpos = flips['x'][dpos] || 'right-top';
350 break;
351 case "y":
352 if(this.options.preventflip !=='y') fdpos = flips['y'][dpos] || 'top-left';
353 break;
354 case "xy":
355 if(!this.options.preventflip) fdpos = flips['xy'][dpos] || 'right-bottom';
356 break;
357 }
358
359 if (fdpos) {
360
361 pp = fdpos.split('-');
362 css = variants[fdpos] ? variants[fdpos] : variants['bottom-left'];
363
364 // check flipped
365 if (this.checkBoundary(pos.left + css.left, pos.top + css.top, width, height, boundarywidth)) {
366 pp = dpos.split('-');
367 css = variants[dpos] ? variants[dpos] : variants['bottom-left'];
368 }
369 }
370 }
371 }
372
373 if (width > boundarywidth) {
374 dropdown.addClass("uk-dropdown-stack");
375 this.trigger('stack.uk.dropdown', [this]);
376 }
377
378 dropdown.css(css).css("display", "").addClass('uk-dropdown-'+pp[0]);
379 },
380
381 checkBoundary: function(left, top, width, height, boundarywidth) {
382
383 var axis = "";
384
385 if (left < 0 || ((left - UI.$win.scrollLeft())+width) > boundarywidth) {
386 axis += "x";
387 }
388
389 if ((top - UI.$win.scrollTop()) < 0 || ((top - UI.$win.scrollTop())+height) > window.innerHeight) {
390 axis += "y";
391 }
392
393 return axis;
394 }
395 });
396
397
398 UI.component('dropdownOverlay', {
399
400 defaults: {
401 'justify' : false,
402 'cls' : '',
403 'duration': 200
404 },
405
406 boot: function() {
407
408 // init code
409 UI.ready(function(context) {
410
411 UI.$("[data-uk-dropdown-overlay]", context).each(function() {
412 var ele = UI.$(this);
413
414 if (!ele.data("dropdownOverlay")) {
415 UI.dropdownOverlay(ele, UI.Utils.options(ele.attr("data-uk-dropdown-overlay")));
416 }
417 });
418 });
419 },
420
421 init: function() {
422
423 var $this = this;
424
425 this.justified = this.options.justify ? UI.$(this.options.justify) : false;
426 this.overlay = this.element.find('uk-dropdown-overlay');
427
428 if (!this.overlay.length) {
429 this.overlay = UI.$('<div class="uk-dropdown-overlay"></div>').appendTo(this.element);
430 }
431
432 this.overlay.addClass(this.options.cls);
433
434 this.on({
435
436 'beforeshow.uk.dropdown': function(e, dropdown) {
437 $this.dropdown = dropdown;
438
439 if ($this.justified && $this.justified.length) {
440 justify($this.overlay.css({'display':'block', 'margin-left':'','margin-right':''}), $this.justified, $this.justified.outerWidth());
441 }
442 },
443
444 'show.uk.dropdown': function(e, dropdown) {
445
446 var h = $this.dropdown.dropdown.outerHeight(true);
447
448 $this.dropdown.element.removeClass('uk-open');
449
450 $this.overlay.stop().css('display', 'block').animate({height: h}, $this.options.duration, function() {
451
452 $this.dropdown.dropdown.css('visibility', '');
453 $this.dropdown.element.addClass('uk-open');
454
455 UI.Utils.checkDisplay($this.dropdown.dropdown, true);
456 });
457
458 $this.pointerleave = false;
459 },
460
461 'hide.uk.dropdown': function() {
462 $this.overlay.stop().animate({height: 0}, $this.options.duration);
463 },
464
465 'pointerenter.uk.dropdown': function(e, dropdown) {
466 clearTimeout($this.remainIdle);
467 },
468
469 'pointerleave.uk.dropdown': function(e, dropdown) {
470 $this.pointerleave = true;
471 }
472 });
473
474
475 this.overlay.on({
476
477 'mouseenter': function() {
478 if ($this.remainIdle) {
479 clearTimeout($this.dropdown.remainIdle);
480 clearTimeout($this.remainIdle);
481 }
482 },
483
484 'mouseleave': function(){
485
486 if ($this.pointerleave && active) {
487
488 $this.remainIdle = setTimeout(function() {
489 if(active) active.hide();
490 }, active.options.remaintime);
491 }
492 }
493 })
494 }
495
496 });
497
498
499 function justify(ele, justifyTo, boundarywidth, offset) {
500
501 ele = UI.$(ele);
502 justifyTo = UI.$(justifyTo);
503 boundarywidth = boundarywidth || window.innerWidth;
504 offset = offset || ele.offset();
505
506 if (justifyTo.length) {
507
508 var jwidth = justifyTo.outerWidth();
509
510 ele.css("min-width", jwidth);
511
512 if (UI.langdirection == 'right') {
513
514 var right1 = boundarywidth - (justifyTo.offset().left + jwidth),
515 right2 = boundarywidth - (ele.offset().left + ele.outerWidth());
516
517 ele.css("margin-right", right1 - right2);
518
519 } else {
520 ele.css("margin-left", justifyTo.offset().left - offset.left);
521 }
522 }
523 }
524
525})(UIkit);