1
+ ; ( function ( global ) {
2
+ var k ,
3
+ _handlers = { } ,
4
+ _mods = { 16 : false , 18 : false , 17 : false , 91 : false } ,
5
+ _scope = 'all' ,
6
+ // modifier keys
7
+ _MODIFIERS = {
8
+ '⇧' : 16 , shift : 16 ,
9
+ '⌥' : 18 , alt : 18 , option : 18 ,
10
+ '⌃' : 17 , ctrl : 17 , control : 17 ,
11
+ '⌘' : 91 , command : 91
12
+ } ,
13
+ // special keys
14
+ _MAP = {
15
+ backspace : 8 , tab : 9 , clear : 12 ,
16
+ enter : 13 , 'return' : 13 ,
17
+ esc : 27 , escape : 27 , space : 32 ,
18
+ left : 37 , up : 38 ,
19
+ right : 39 , down : 40 ,
20
+ del : 46 , 'delete' : 46 ,
21
+ home : 36 , end : 35 ,
22
+ pageup : 33 , pagedown : 34 ,
23
+ ',' : 188 , '.' : 190 , '/' : 191 ,
24
+ '`' : 192 , '-' : 189 , '=' : 187 ,
25
+ ';' : 186 , '\'' : 222 ,
26
+ '[' : 219 , ']' : 221 , '\\' : 220
27
+ } ,
28
+ code = function ( x ) {
29
+ return _MAP [ x ] || x . toUpperCase ( ) . charCodeAt ( 0 ) ;
30
+ } ,
31
+ _downKeys = [ ] ;
32
+
33
+ for ( k = 1 ; k < 20 ; k ++ ) _MAP [ 'f' + k ] = 111 + k ;
34
+
35
+ // IE doesn't support Array#indexOf, so have a simple replacement
36
+ function index ( array , item ) {
37
+ var i = array . length ;
38
+ while ( i -- ) if ( array [ i ] === item ) return i ;
39
+ return - 1 ;
40
+ }
41
+
42
+ // for comparing mods before unassignment
43
+ function compareArray ( a1 , a2 ) {
44
+ if ( a1 . length != a2 . length ) return false ;
45
+ for ( var i = 0 ; i < a1 . length ; i ++ ) {
46
+ if ( a1 [ i ] !== a2 [ i ] ) return false ;
47
+ }
48
+ return true ;
49
+ }
50
+
51
+ var modifierMap = {
52
+ 16 :'shiftKey' ,
53
+ 18 :'altKey' ,
54
+ 17 :'ctrlKey' ,
55
+ 91 :'metaKey'
56
+ } ;
57
+ function updateModifierKey ( event ) {
58
+ for ( k in _mods ) _mods [ k ] = event [ modifierMap [ k ] ] ;
59
+ } ;
60
+
61
+ // handle keydown event
62
+ function dispatch ( event ) {
63
+ var key , handler , k , i , modifiersMatch , scope ;
64
+ key = event . keyCode ;
65
+
66
+ if ( index ( _downKeys , key ) == - 1 ) {
67
+ _downKeys . push ( key ) ;
68
+ }
69
+
70
+ // if a modifier key, set the key.<modifierkeyname> property to true and return
71
+ if ( key == 93 || key == 224 ) key = 91 ; // right command on webkit, command on Gecko
72
+ if ( key in _mods ) {
73
+ _mods [ key ] = true ;
74
+ // 'assignKey' from inside this closure is exported to window.key
75
+ for ( k in _MODIFIERS ) if ( _MODIFIERS [ k ] == key ) assignKey [ k ] = true ;
76
+ return ;
77
+ }
78
+ updateModifierKey ( event ) ;
79
+
80
+ // see if we need to ignore the keypress (filter() can can be overridden)
81
+ // by default ignore key presses if a select, textarea, or input is focused
82
+ if ( ! assignKey . filter . call ( this , event ) ) return ;
83
+
84
+ // abort if no potentially matching shortcuts found
85
+ if ( ! ( key in _handlers ) ) return ;
86
+
87
+ scope = getScope ( ) ;
88
+
89
+ // for each potential shortcut
90
+ for ( i = 0 ; i < _handlers [ key ] . length ; i ++ ) {
91
+ handler = _handlers [ key ] [ i ] ;
92
+
93
+ // see if it's in the current scope
94
+ if ( handler . scope == scope || handler . scope == 'all' ) {
95
+ // check if modifiers match if any
96
+ modifiersMatch = handler . mods . length > 0 ;
97
+ for ( k in _mods )
98
+ if ( ( ! _mods [ k ] && index ( handler . mods , + k ) > - 1 ) ||
99
+ ( _mods [ k ] && index ( handler . mods , + k ) == - 1 ) ) modifiersMatch = false ;
100
+ // call the handler and stop the event if neccessary
101
+ if ( ( handler . mods . length == 0 && ! _mods [ 16 ] && ! _mods [ 18 ] && ! _mods [ 17 ] && ! _mods [ 91 ] ) || modifiersMatch ) {
102
+ if ( handler . method ( event , handler ) === false ) {
103
+ if ( event . preventDefault ) event . preventDefault ( ) ;
104
+ else event . returnValue = false ;
105
+ if ( event . stopPropagation ) event . stopPropagation ( ) ;
106
+ if ( event . cancelBubble ) event . cancelBubble = true ;
107
+ }
108
+ }
109
+ }
110
+ }
111
+ } ;
112
+
113
+ // unset modifier keys on keyup
114
+ function clearModifier ( event ) {
115
+ var key = event . keyCode , k ,
116
+ i = index ( _downKeys , key ) ;
117
+
118
+ // remove key from _downKeys
119
+ if ( i >= 0 ) {
120
+ _downKeys . splice ( i , 1 ) ;
121
+ }
122
+
123
+ if ( key == 93 || key == 224 ) key = 91 ;
124
+ if ( key in _mods ) {
125
+ _mods [ key ] = false ;
126
+ for ( k in _MODIFIERS ) if ( _MODIFIERS [ k ] == key ) assignKey [ k ] = false ;
127
+ }
128
+ } ;
129
+
130
+ function resetModifiers ( ) {
131
+ for ( k in _mods ) _mods [ k ] = false ;
132
+ for ( k in _MODIFIERS ) assignKey [ k ] = false ;
133
+ } ;
134
+
135
+ // parse and assign shortcut
136
+ function assignKey ( key , scope , method ) {
137
+ var keys , mods ;
138
+ keys = getKeys ( key ) ;
139
+ if ( method === undefined ) {
140
+ method = scope ;
141
+ scope = 'all' ;
142
+ }
143
+
144
+ // for each shortcut
145
+ for ( var i = 0 ; i < keys . length ; i ++ ) {
146
+ // set modifier keys if any
147
+ mods = [ ] ;
148
+ key = keys [ i ] . split ( '+' ) ;
149
+ if ( key . length > 1 ) {
150
+ mods = getMods ( key ) ;
151
+ key = [ key [ key . length - 1 ] ] ;
152
+ }
153
+ // convert to keycode and...
154
+ key = key [ 0 ]
155
+ key = code ( key ) ;
156
+ // ...store handler
157
+ if ( ! ( key in _handlers ) ) _handlers [ key ] = [ ] ;
158
+ _handlers [ key ] . push ( { shortcut : keys [ i ] , scope : scope , method : method , key : keys [ i ] , mods : mods } ) ;
159
+ }
160
+ } ;
161
+
162
+ // unbind all handlers for given key in current scope
163
+ function unbindKey ( key , scope ) {
164
+ var multipleKeys , keys ,
165
+ mods = [ ] ,
166
+ i , j , obj ;
167
+
168
+ multipleKeys = getKeys ( key ) ;
169
+
170
+ for ( j = 0 ; j < multipleKeys . length ; j ++ ) {
171
+ keys = multipleKeys [ j ] . split ( '+' ) ;
172
+
173
+ if ( keys . length > 1 ) {
174
+ mods = getMods ( keys ) ;
175
+ }
176
+
177
+ key = keys [ keys . length - 1 ] ;
178
+ key = code ( key ) ;
179
+
180
+ if ( scope === undefined ) {
181
+ scope = getScope ( ) ;
182
+ }
183
+ if ( ! _handlers [ key ] ) {
184
+ return ;
185
+ }
186
+ for ( i = 0 ; i < _handlers [ key ] . length ; i ++ ) {
187
+ obj = _handlers [ key ] [ i ] ;
188
+ // only clear handlers if correct scope and mods match
189
+ if ( obj . scope === scope && compareArray ( obj . mods , mods ) ) {
190
+ _handlers [ key ] [ i ] = { } ;
191
+ }
192
+ }
193
+ }
194
+ } ;
195
+
196
+ // Returns true if the key with code 'keyCode' is currently down
197
+ // Converts strings into key codes.
198
+ function isPressed ( keyCode ) {
199
+ if ( typeof ( keyCode ) == 'string' ) {
200
+ keyCode = code ( keyCode ) ;
201
+ }
202
+ return index ( _downKeys , keyCode ) != - 1 ;
203
+ }
204
+
205
+ function getPressedKeyCodes ( ) {
206
+ return _downKeys . slice ( 0 ) ;
207
+ }
208
+
209
+ function filter ( event ) {
210
+ if ( ( event . target || event . srcElement ) . classList ?. contains ( 'search-bar' ) ) {
211
+ return true ;
212
+ }
213
+ var tagName = ( event . target || event . srcElement ) . tagName ;
214
+ // ignore keypressed in any elements that support keyboard data input
215
+ return ! ( tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA' ) ;
216
+ }
217
+
218
+ // initialize key.<modifier> to false
219
+ for ( k in _MODIFIERS ) assignKey [ k ] = false ;
220
+
221
+ // set current scope (default 'all')
222
+ function setScope ( scope ) { _scope = scope || 'all' } ;
223
+ function getScope ( ) { return _scope || 'all' } ;
224
+
225
+ // delete all handlers for a given scope
226
+ function deleteScope ( scope ) {
227
+ var key , handlers , i ;
228
+
229
+ for ( key in _handlers ) {
230
+ handlers = _handlers [ key ] ;
231
+ for ( i = 0 ; i < handlers . length ; ) {
232
+ if ( handlers [ i ] . scope === scope ) handlers . splice ( i , 1 ) ;
233
+ else i ++ ;
234
+ }
235
+ }
236
+ } ;
237
+
238
+ // abstract key logic for assign and unassign
239
+ function getKeys ( key ) {
240
+ var keys ;
241
+ key = key . replace ( / \s / g, '' ) ;
242
+ keys = key . split ( ',' ) ;
243
+ if ( ( keys [ keys . length - 1 ] ) == '' ) {
244
+ keys [ keys . length - 2 ] += ',' ;
245
+ }
246
+ return keys ;
247
+ }
248
+
249
+ // abstract mods logic for assign and unassign
250
+ function getMods ( key ) {
251
+ var mods = key . slice ( 0 , key . length - 1 ) ;
252
+ for ( var mi = 0 ; mi < mods . length ; mi ++ )
253
+ mods [ mi ] = _MODIFIERS [ mods [ mi ] ] ;
254
+ return mods ;
255
+ }
256
+
257
+ // cross-browser events
258
+ function addEvent ( object , event , method ) {
259
+ if ( object . addEventListener )
260
+ object . addEventListener ( event , method , false ) ;
261
+ else if ( object . attachEvent )
262
+ object . attachEvent ( 'on' + event , function ( ) { method ( window . event ) } ) ;
263
+ } ;
264
+
265
+ // set the handlers globally on document
266
+ addEvent ( document , 'keydown' , function ( event ) { dispatch ( event ) } ) ; // Passing _scope to a callback to ensure it remains the same by execution. Fixes #48
267
+ addEvent ( document , 'keyup' , clearModifier ) ;
268
+
269
+ // reset modifiers to false whenever the window is (re)focused.
270
+ addEvent ( window , 'focus' , resetModifiers ) ;
271
+
272
+ // store previously defined key
273
+ var previousKey = global . key ;
274
+
275
+ // restore previously defined key and return reference to our key object
276
+ function noConflict ( ) {
277
+ var k = global . key ;
278
+ global . key = previousKey ;
279
+ return k ;
280
+ }
281
+
282
+ // set window.key and window.key.set/get/deleteScope, and the default filter
283
+ global . key = assignKey ;
284
+ global . key . setScope = setScope ;
285
+ global . key . getScope = getScope ;
286
+ global . key . deleteScope = deleteScope ;
287
+ global . key . filter = filter ;
288
+ global . key . isPressed = isPressed ;
289
+ global . key . getPressedKeyCodes = getPressedKeyCodes ;
290
+ global . key . noConflict = noConflict ;
291
+ global . key . unbind = unbindKey ;
292
+
293
+ if ( typeof module !== 'undefined' ) module . exports = assignKey ;
294
+
295
+ } ) ( this ) ;
0 commit comments