Skip to content

Commit 32d650e

Browse files
committed
Added click outside to close & keyboard navigation
Added keyboard navigation, esc to close and clicking outside strip will now also close it. Thx reddit!
1 parent 4fbbc59 commit 32d650e

9 files changed

Lines changed: 173 additions & 19 deletions

File tree

Gruntfile.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ module.exports = function(grunt) {
3333
'src/js/pages.js',
3434
'src/js/page.js',
3535
'src/js/window.js',
36+
'src/js/keyboard.js',
3637

3738
'src/js/api.js',
3839

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "strip",
33
"title": "Strip",
4-
"version": "1.0.0",
4+
"version": "1.0.1",
55
"description": "A Less Intrusive Responsive Lightbox",
66
"author": {
77
"name": "Nick Stakenburg",

src/js/api.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,14 @@ var _Strip = {
104104
}
105105
if (position > views.length) position = views.length;
106106

107+
// Allow API events to pass through by disabling hideOnClickOutside.
108+
// It is re-enabled when bringing a page into view using a slight delay
109+
// allowing a possible click event that triggers this show() function to
110+
// fully bubble up. This is needed when Strip is visible and Strip.show()
111+
// is called, the click would otherwise bubble down and instantly hide,
112+
// cancelling the show()
113+
Window.unbindHideOnClickOutside();
114+
107115
// if we've clicked an element, search for it in the currently open pagegroup
108116
var positionInAPG;
109117
if (isElement && (positionInAPG = Pages.getPositionInActivePageGroup(object))) {

src/js/helpers/timers.js

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,20 @@ $.extend(Timers.prototype, {
99
},
1010

1111
get: function(name) {
12-
return this._timers[name];
12+
return this._timers[name];
1313
},
14-
14+
1515
clear: function(name) {
16-
if (this._timers[name]) {
17-
clearTimeout(this._timers[name]);
18-
delete this._timers[name];
16+
if (name) {
17+
if (this._timers[name]) {
18+
clearTimeout(this._timers[name]);
19+
delete this._timers[name];
20+
}
1921
} else {
2022
this.clearAll();
2123
}
2224
},
23-
25+
2426
clearAll: function() {
2527
$.each(this._timers, function(i, timer) {
2628
clearTimeout(timer);

src/js/keyboard.js

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Keyboard
2+
// keeps track of keyboard events when enabled
3+
var Keyboard = {
4+
enabled: false,
5+
6+
keyCode: {
7+
'left': 37,
8+
'right': 39,
9+
'esc': 27
10+
},
11+
12+
// enable is passed the keyboard option of a page, which can be false
13+
// or contains multiple buttons to toggle
14+
enable: function(enabled) {
15+
this.disable();
16+
17+
if (!enabled) return;
18+
19+
$(document).bind('keydown', this._onKeyDownHandler = $.proxy(this.onKeyDown, this))
20+
.bind('keyup', this._onKeyUpHandler = $.proxy(this.onKeyUp, this));
21+
22+
this.enabled = enabled;
23+
},
24+
25+
disable: function() {
26+
this.enabled = false;
27+
28+
if (this._onKeyUpHandler) {
29+
$(document).unbind('keyup', this._onKeyUpHandler)
30+
.unbind('keydown', this._onKeyDownHandler);
31+
this._onKeyUpHandler = this._onKeyDownHandler = null;
32+
}
33+
},
34+
35+
onKeyDown: function(event) {
36+
if (!this.enabled || !Window.visible) return;
37+
38+
var key = this.getKeyByKeyCode(event.keyCode);
39+
40+
if (!key || (key && this.enabled && !this.enabled[key])) return;
41+
42+
event.preventDefault();
43+
event.stopPropagation();
44+
45+
switch (key) {
46+
case 'left':
47+
Window.previous();
48+
break;
49+
case 'right':
50+
Window.next();
51+
break;
52+
}
53+
},
54+
55+
onKeyUp: function(event) {
56+
if (!this.enabled || !Window.visible) return;
57+
58+
var key = this.getKeyByKeyCode(event.keyCode);
59+
60+
if (!key || (key && this.enabled && !this.enabled[key])) return;
61+
62+
switch (key) {
63+
case 'esc':
64+
Window.hide();
65+
break;
66+
}
67+
},
68+
69+
getKeyByKeyCode: function(keyCode) {
70+
for(var key in this.keyCode) {
71+
if (this.keyCode[key] == keyCode) return key;
72+
}
73+
return null;
74+
}
75+
};

src/js/options.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,24 @@ var Options = (function() {
3232
});
3333
});
3434
}
35-
35+
36+
// disable keyboard left/right when not an image
37+
if (MERGED.keyboard) {
38+
// when keyboard is true, enable all keys
39+
if ($.type(MERGED.keyboard) == 'boolean') {
40+
MERGED.keyboard = {};
41+
$.each(BASE.keyboard, function(key, bool) {
42+
MERGED.keyboard[key] = true;
43+
});
44+
}
45+
46+
// disable left and right keys for video, because players like
47+
// youtube use these keys
48+
if (type == 'vimeo' || type == 'youtube') {
49+
$.extend(MERGED.keyboard, { left: false, right: false });
50+
}
51+
}
52+
3653
// vimeo & youtube always have no overlap
3754
if (type == 'vimeo' || type == 'youtube') {
3855
MERGED.overlap = false;

src/js/page.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,11 +326,22 @@ $.extend(Page.prototype, {
326326
Window.setSkin(this.view.options.skin);
327327
Window.bindUI(); // enable ui controls
328328

329+
// keyboard
330+
Keyboard.enable(this.view.options.keyboard);
331+
329332
this.fitToWindow();
330333

331334
next_utility();
332335
}, this));
333336

337+
// we bind hide on click outside with a delay so API calls can pass through.
338+
// more in this in api.js
339+
shq.queue($.proxy(function(next_bind_hide_on_click_outside) {
340+
Window.timers.set('bind-hide-on-click-outside', $.proxy(function() {
341+
Window.bindHideOnClickOutside();
342+
next_bind_hide_on_click_outside();
343+
}, this), 1);
344+
}, this));
334345

335346
// vimeo and youtube use this for insertion
336347
if (this.view.type == 'vimeo' || this.view.type == 'youtube') {

src/js/skins.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,17 @@ Strip.Skins = {
66
ui: { show: 0, hide: 200 },
77
window: { show: 300, hide: 300 }
88
},
9+
hideOnClickOutside: true,
10+
keyboard: {
11+
left: true,
12+
right: true,
13+
esc: true
14+
},
915
loop: false,
16+
overlap: true,
1017
preload: [1,2],
1118
position: true,
1219
side: 'right',
13-
overlap: true,
1420
uiDelay: 3000,
1521
vimeo: {
1622
autoplay: 1,

src/js/window.js

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -402,12 +402,20 @@ var Window = {
402402
next_stop();
403403
}, this));
404404

405-
shq.queue($.proxy(function(next_unbind_hide_ui) {
405+
shq.queue($.proxy(function(next_unbinds) {
406+
// ui
406407
var duration = this.view ? this.view.options.effects.window.hide : 0;
407408
this.unbindUI();
408409
this.hideUI(null, duration);
409-
next_unbind_hide_ui();
410-
}, this));
410+
411+
// close on click outside
412+
this.unbindHideOnClickOutside();
413+
414+
// keyboard
415+
Keyboard.disable();
416+
417+
next_unbinds();
418+
}, this));
411419

412420
shq.queue($.proxy(function(next_zero) {
413421
this.resize(0, $.proxy(function() {
@@ -419,6 +427,8 @@ var Window = {
419427

420428
Pages.removeAll();
421429

430+
this.timers.clear();
431+
422432
this._position = -1;
423433

424434
// afterHide callback
@@ -451,8 +461,8 @@ var Window = {
451461

452462
//Pages.removeExpired();
453463
this.visible = false;
454-
this.timers.clear();
455464
this.hideUI(null, 0);
465+
this.timers.clear('ui');
456466
this.resetPrevNext();
457467
this._cachedMouseMoveEvent = null;
458468
},
@@ -500,6 +510,32 @@ var Window = {
500510
};
501511
},
502512

513+
514+
// close when clicking outside of strip or an element opening strip
515+
bindHideOnClickOutside: function() {
516+
this.unbindHideOnClickOutside();
517+
$(document.documentElement).bind('click', this._delegateHideOutsideHandler = $.proxy(this._delegateHideOutside, this));
518+
},
519+
520+
unbindHideOnClickOutside: function() {
521+
if (this._delegateHideOutsideHandler) {
522+
$(document.documentElement).unbind('click', this._delegateHideOutsideHandler);
523+
this._delegateHideOutsideHandler = null;
524+
}
525+
},
526+
527+
_delegateHideOutside: function(event) {
528+
var page = Pages.page;
529+
if (!this.visible || !(page && page.view.options.hideOnClickOutside)) return;
530+
531+
var element = event.target;
532+
533+
if (!$(element).closest('.strip, .strp-window')[0]) {
534+
this.hide();
535+
}
536+
},
537+
538+
503539
// UI
504540
bindUI: function() {
505541
this.unbindUI();
@@ -518,8 +554,6 @@ var Window = {
518554
.delegate('.strp-container', 'mouseout', this._onMouseOutHandler = $.proxy(this._onMouseOut, this))
519555
.delegate('.strp-container', 'mouseenter', this._onMouseEnterHandler = $.proxy(this._onMouseEnter, this));
520556

521-
522-
523557
$(window).bind('scroll', this._onScrollHandler = $.proxy(this._onScroll, this));
524558
}
525559

@@ -589,9 +623,9 @@ var Window = {
589623
},
590624

591625
_getEventSide: function(event) {
592-
var offsetLeft = this._offsetLeft || this.element.offset().left;
593-
var left = event.pageX - offsetLeft;
594-
var width = this._outerWidth || this.element.outerWidth();
626+
var offsetLeft = this._offsetLeft || this.element.offset().left,
627+
left = event.pageX - offsetLeft,
628+
width = this._outerWidth || this.element.outerWidth();
595629

596630
return left < .5 * width ? 'Previous' : 'Next';
597631
},
@@ -613,7 +647,7 @@ var Window = {
613647
if ($.type(callback) == 'function') callback();
614648
}, this));
615649
},
616-
650+
617651
hideUI: function(callback, alternateDuration) {
618652
var elements = this.element.find('.strp-nav-button');
619653

0 commit comments

Comments
 (0)