mousetrapjs 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,3 @@
1
1
  module Mousetrapjs
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
@@ -1,7 +1,760 @@
1
- /* mousetrap v1.1 craig.is/killing/mice */
2
- window.Mousetrap=function(){function m(a,e,b){if(a.addEventListener)return a.addEventListener(e,b,!1);a.attachEvent("on"+e,b)}function s(a){return"keypress"!=a.type&&h[a.which]?h[a.which]:"keypress"==a.type?String.fromCharCode(a.which):String.fromCharCode(a.which).toLowerCase()}function n(a){var a=a||{},e=!1,b;for(b in i)a[b]?e=!0:i[b]=0;e||(k=!1)}function t(a,e,b,c,z){var g,d,f=[];if(!j[a])return[];"keyup"==b&&o(a)&&(e=[a]);for(g=0;g<j[a].length;++g)if(d=j[a][g],!(d.seq&&i[d.seq]!=d.level)&&b==d.action&&
3
- ("keypress"===b||e.sort().join(",")===d.modifiers.sort().join(",")))c&&d.combo==z&&j[a].splice(g,1),f.push(d);return f}function p(a){a.which="number"==typeof a.which?a.which:a.keyCode;var e=s(a);if(e)if("keyup"===a.type&&q===e)q=!1;else{var b=a.target||a.srcElement,c=b.tagName;if(!(-1<(" "+b.className+" ").indexOf(" mousetrap ")?0:"INPUT"==c||"SELECT"==c||"TEXTAREA"==c)){b=[];a.shiftKey&&b.push("shift");a.altKey&&b.push("alt");a.ctrlKey&&b.push("ctrl");a.metaKey&&b.push("meta");for(var b=t(e,b,a.type),
4
- f={},g=!1,c=0;c<b.length;++c)b[c].seq?(g=!0,f[b[c].seq]=1,b[c].callback(a)):!g&&!k&&b[c].callback(a);a.type==k&&!o(e)&&n(f)}}}function o(a){return"shift"==a||"ctrl"==a||"alt"==a||"meta"==a}function u(a,e,b){if(!b){if(!l){l={};for(var c in h)95<c&&112>c||h.hasOwnProperty(c)&&(l[h[c]]=c)}b=l[a]?"keydown":"keypress"}"keypress"===b&&e.length&&(b="keydown");return b}function A(a,e,b,c){i[a]=0;c||(c=u(e[0],[]));var f=function(){k=c;++i[a];clearTimeout(v);v=setTimeout(n,1E3)},g=function(a){b(a);"keyup"!==
5
- c&&(q=s(a));setTimeout(n,10)},d;for(d=0;d<e.length;++d)w(e[d],d<e.length-1?f:g,c,a,d)}function w(a,e,b,c,f){var a=a.replace(/\s+/g," "),g=a.split(" "),d,h,i=[];if(1<g.length)return A(a,g,e,b);h="+"===a?["+"]:a.split("+");for(g=0;g<h.length;++g)d=h[g],x[d]&&(d=x[d]),b&&("keypress"!=b&&y[d])&&(d=y[d],i.push("shift")),o(d)&&i.push(d);b=u(d,i,b);j[d]||(j[d]=[]);t(d,i,b,!c,a);j[d][c?"unshift":"push"]({callback:e,modifiers:i,action:b,seq:c,level:f,combo:a})}for(var h={8:"backspace",9:"tab",13:"enter",16:"shift",
6
- 17:"ctrl",18:"alt",20:"capslock",27:"esc",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",46:"del",91:"meta",93:"meta",224:"meta",106:"*",107:"+",109:"-",110:".",111:"/",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'"},y={"~":"`","!":"1","@":"2","#":"3",$:"4","%":"5","^":"6","&":"7","*":"8","(":"9",")":"0",_:"-","+":"=",":":";",'"':"'","<":",",">":".","?":"/","|":"\\"},x={option:"alt",command:"meta","return":"enter",
7
- escape:"esc"},l,j={},r={},i={},v,q=!1,k=!1,f=1;20>f;++f)h[111+f]="f"+f;for(f=96;106>f;++f)h[f]=f-96;m(document,"keydown",p);m(document,"keyup",p);m(document,"keypress",p);return{bind:function(a,e,b){for(var c=a instanceof Array?a:[a],f=0;f<c.length;++f)w(c[f],e,b);r[a+":"+b]=e},trigger:function(a,e){r[a+":"+e]()},reset:function(){j={};r={}}}}();
1
+ /**
2
+ * Copyright 2012 Craig Campbell
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *
16
+ * Mousetrap is a simple keyboard shortcut library for Javascript with
17
+ * no external dependencies
18
+ *
19
+ * @preserve @version 1.1
20
+ * @url craig.is/killing/mice
21
+ */
22
+ window.Mousetrap = (function() {
23
+
24
+ /**
25
+ * mapping of special keycodes to their corresponding keys
26
+ *
27
+ * @type {Object}
28
+ */
29
+ var _MAP = {
30
+ 8: 'backspace',
31
+ 9: 'tab',
32
+ 13: 'enter',
33
+ 16: 'shift',
34
+ 17: 'ctrl',
35
+ 18: 'alt',
36
+ 20: 'capslock',
37
+ 27: 'esc',
38
+ 32: 'space',
39
+ 33: 'pageup',
40
+ 34: 'pagedown',
41
+ 35: 'end',
42
+ 36: 'home',
43
+ 37: 'left',
44
+ 38: 'up',
45
+ 39: 'right',
46
+ 40: 'down',
47
+ 45: 'ins',
48
+ 46: 'del',
49
+ 91: 'meta',
50
+ 93: 'meta',
51
+ 224: 'meta'
52
+ },
53
+
54
+ /**
55
+ * mapping for special characters so they can support
56
+ * keydown and keyup events
57
+ *
58
+ * @type {Object}
59
+ */
60
+ _KEYCODE_MAP = {
61
+ 106: '*',
62
+ 107: '+',
63
+ 109: '-',
64
+ 110: '.',
65
+ 111 : '/',
66
+ 186: ';',
67
+ 187: '=',
68
+ 188: ',',
69
+ 189: '-',
70
+ 190: '.',
71
+ 191: '/',
72
+ 192: '`',
73
+ 219: '[',
74
+ 220: '\\',
75
+ 221: ']',
76
+ 222: '\''
77
+ },
78
+
79
+ /**
80
+ * this is a mapping of keys that require shift on a US keypad
81
+ * back to the non shift equivelents
82
+ *
83
+ * this is so you can use keyup events with these keys
84
+ *
85
+ * @type {Object}
86
+ */
87
+ _SHIFT_MAP = {
88
+ '~': '`',
89
+ '!': '1',
90
+ '@': '2',
91
+ '#': '3',
92
+ '$': '4',
93
+ '%': '5',
94
+ '^': '6',
95
+ '&': '7',
96
+ '*': '8',
97
+ '(': '9',
98
+ ')': '0',
99
+ '_': '-',
100
+ '+': '=',
101
+ ':': ';',
102
+ '\"': '\'',
103
+ '<': ',',
104
+ '>': '.',
105
+ '?': '/',
106
+ '|': '\\'
107
+ },
108
+
109
+ /**
110
+ * this is a list of special strings you can use to map
111
+ * to modifier keys when you specify your keyboard shortcuts
112
+ *
113
+ * @type {Object}
114
+ */
115
+ _SPECIAL_ALIASES = {
116
+ 'option': 'alt',
117
+ 'command': 'meta',
118
+ 'return': 'enter',
119
+ 'escape': 'esc'
120
+ },
121
+
122
+ /**
123
+ * variable to store the flipped version of _MAP from above
124
+ * needed to check if we should use keypress or not when no action
125
+ * is specified
126
+ *
127
+ * @type {Object|undefined}
128
+ */
129
+ _REVERSE_MAP,
130
+
131
+ /**
132
+ * a list of all the callbacks setup via Mousetrap.bind()
133
+ *
134
+ * @type {Object}
135
+ */
136
+ _callbacks = {},
137
+
138
+ /**
139
+ * direct map of string combinations to callbacks used for trigger()
140
+ *
141
+ * @type {Object}
142
+ */
143
+ _direct_map = {},
144
+
145
+ /**
146
+ * keeps track of what level each sequence is at since multiple
147
+ * sequences can start out with the same sequence
148
+ *
149
+ * @type {Object}
150
+ */
151
+ _sequence_levels = {},
152
+
153
+ /**
154
+ * variable to store the setTimeout call
155
+ *
156
+ * @type {null|number}
157
+ */
158
+ _reset_timer,
159
+
160
+ /**
161
+ * temporary state where we will ignore the next keyup
162
+ *
163
+ * @type {boolean|string}
164
+ */
165
+ _ignore_next_keyup = false,
166
+
167
+ /**
168
+ * are we currently inside of a sequence?
169
+ * type of action ("keyup" or "keydown" or "keypress") or false
170
+ *
171
+ * @type {boolean|string}
172
+ */
173
+ _inside_sequence = false;
174
+
175
+ /**
176
+ * loop through the f keys, f1 to f19 and add them to the map
177
+ * programatically
178
+ */
179
+ for (var i = 1; i < 20; ++i) {
180
+ _MAP[111 + i] = 'f' + i;
181
+ }
182
+
183
+ /**
184
+ * loop through to map numbers on the numeric keypad
185
+ */
186
+ for (i = 96; i < 106; ++i) {
187
+ _MAP[i] = i - 96;
188
+ }
189
+
190
+ /**
191
+ * cross browser add event method
192
+ *
193
+ * @param {Element|HTMLDocument} object
194
+ * @param {string} type
195
+ * @param {Function} callback
196
+ * @returns void
197
+ */
198
+ function _addEvent(object, type, callback) {
199
+ if (object.addEventListener) {
200
+ return object.addEventListener(type, callback, false);
201
+ }
202
+
203
+ object.attachEvent('on' + type, callback);
204
+ }
205
+
206
+ /**
207
+ * takes the event and returns the keycode
208
+ *
209
+ * @param {Event} e
210
+ * @return {string}
211
+ */
212
+ function _characterFromEvent(e) {
213
+
214
+ // for keypress events we should return the character as is
215
+ if (e.type == 'keypress') {
216
+ return String.fromCharCode(e.which);
217
+ }
218
+
219
+ // for non keypress events the special maps are needed
220
+ if (_MAP[e.which]) {
221
+ return _MAP[e.which];
222
+ }
223
+
224
+ if (_KEYCODE_MAP[e.which]) {
225
+ return _KEYCODE_MAP[e.which];
226
+ }
227
+
228
+ // if it is not in the special map
229
+ return String.fromCharCode(e.which).toLowerCase();
230
+ }
231
+
232
+ /**
233
+ * should we stop this event before firing off callbacks
234
+ *
235
+ * @param {Event} e
236
+ * @return {boolean}
237
+ */
238
+ function _stop(e) {
239
+ var element = e.target || e.srcElement,
240
+ tag_name = element.tagName;
241
+
242
+ // if the element has the class "mousetrap" then no need to stop
243
+ if ((' ' + element.className + ' ').indexOf(' mousetrap ') > -1) {
244
+ return false;
245
+ }
246
+
247
+ // stop for input, select, and textarea
248
+ return tag_name == 'INPUT' || tag_name == 'SELECT' || tag_name == 'TEXTAREA'/* || element.getAttribute('contenteditable')*/;
249
+ }
250
+
251
+ /**
252
+ * checks if two arrays are equal
253
+ *
254
+ * @param {Array} modifiers1
255
+ * @param {Array} modifiers2
256
+ * @returns {boolean}
257
+ */
258
+ function _modifiersMatch(modifiers1, modifiers2) {
259
+ return modifiers1.sort().join(',') === modifiers2.sort().join(',');
260
+ }
261
+
262
+ /**
263
+ * resets all sequence counters except for the ones passed in
264
+ *
265
+ * @param {Object} do_not_reset
266
+ * @returns void
267
+ */
268
+ function _resetSequences(do_not_reset) {
269
+ do_not_reset = do_not_reset || {};
270
+
271
+ var active_sequences = false;
272
+
273
+ for (var key in _sequence_levels) {
274
+ if (!do_not_reset[key]) {
275
+ _sequence_levels[key] = 0;
276
+ continue;
277
+ }
278
+ active_sequences = true;
279
+ }
280
+
281
+ if (!active_sequences) {
282
+ _inside_sequence = false;
283
+ }
284
+ }
285
+
286
+ /**
287
+ * finds all callbacks that match based on the keycode, modifiers,
288
+ * and action
289
+ *
290
+ * @param {string} character
291
+ * @param {Array} modifiers
292
+ * @param {string} action
293
+ * @param {boolean=} remove - should we remove any matches
294
+ * @param {string=} combination
295
+ * @returns {Array}
296
+ */
297
+ function _getMatches(character, modifiers, action, remove, combination) {
298
+ var i,
299
+ callback,
300
+ matches = [];
301
+
302
+ // if there are no events related to this keycode
303
+ if (!_callbacks[character]) {
304
+ return [];
305
+ }
306
+
307
+ // if a modifier key is coming up on its own we should allow it
308
+ if (action == 'keyup' && _isModifier(character)) {
309
+ modifiers = [character];
310
+ }
311
+
312
+ // loop through all callbacks for the key that was pressed
313
+ // and see if any of them match
314
+ for (i = 0; i < _callbacks[character].length; ++i) {
315
+ callback = _callbacks[character][i];
316
+
317
+ // if this is a sequence but it is not at the right level
318
+ // then move onto the next match
319
+ if (callback['seq'] && _sequence_levels[callback['seq']] != callback['level']) {
320
+ continue;
321
+ }
322
+
323
+ // if the action we are looking for doesn't match the action we got
324
+ // then we should keep going
325
+ if (action != callback.action) {
326
+ continue;
327
+ }
328
+
329
+ // if this is a keypress event that means that we need to only
330
+ // look at the character, otherwise check the modifiers as
331
+ // well
332
+ if (action === 'keypress' || _modifiersMatch(modifiers, callback.modifiers)) {
333
+
334
+ // remove is used so if you change your mind and call bind a
335
+ // second time with a new function the first one is overwritten
336
+ if (remove && callback['combo'] == combination) {
337
+ _callbacks[character].splice(i, 1);
338
+ }
339
+
340
+ matches.push(callback);
341
+ }
342
+ }
343
+
344
+ return matches;
345
+ }
346
+
347
+ /**
348
+ * takes a key event and figures out what the modifiers are
349
+ *
350
+ * @param {Event} e
351
+ * @returns {Array}
352
+ */
353
+ function _eventModifiers(e) {
354
+ var modifiers = [];
355
+
356
+ if (e.shiftKey) {
357
+ modifiers.push('shift');
358
+ }
359
+
360
+ if (e.altKey) {
361
+ modifiers.push('alt');
362
+ }
363
+
364
+ if (e.ctrlKey) {
365
+ modifiers.push('ctrl');
366
+ }
367
+
368
+ if (e.metaKey) {
369
+ modifiers.push('meta');
370
+ }
371
+
372
+ return modifiers;
373
+ }
374
+
375
+ /**
376
+ * fires a callback for a matching keycode
377
+ *
378
+ * @param {string} character
379
+ * @param {Event} e
380
+ * @returns void
381
+ */
382
+ function _fireCallback(character, e) {
383
+
384
+ // if this event should not happen stop here
385
+ if (_stop(e)) {
386
+ return;
387
+ }
388
+
389
+ var callbacks = _getMatches(character, _eventModifiers(e), e.type),
390
+ i,
391
+ do_not_reset = {},
392
+ processed_sequence_callback = false;
393
+
394
+ // loop through matching callbacks for this key event
395
+ for (i = 0; i < callbacks.length; ++i) {
396
+
397
+ // fire for all sequence callbacks
398
+ // this is because if for example you have multiple sequences
399
+ // bound such as "g i" and "g t" they both need to fire the
400
+ // callback for matching g cause otherwise you can only ever
401
+ // match the first one
402
+ if (callbacks[i]['seq']) {
403
+ processed_sequence_callback = true;
404
+
405
+ // keep a list of which sequences were matches for later
406
+ do_not_reset[callbacks[i]['seq']] = 1;
407
+ callbacks[i].callback(e);
408
+ continue;
409
+ }
410
+
411
+ // if there were no sequence matches but we are still here
412
+ // that means this is a regular match so we should fire then break
413
+ if (!processed_sequence_callback && !_inside_sequence) {
414
+ callbacks[i].callback(e);
415
+ }
416
+ }
417
+
418
+ // if you are inside of a sequence and the key you are pressing
419
+ // is not a modifier key then we should reset all sequences
420
+ // that were not matched by this key event
421
+ if (e.type == _inside_sequence && !_isModifier(character)) {
422
+ _resetSequences(do_not_reset);
423
+ }
424
+ }
425
+
426
+ /**
427
+ * handles a keydown event
428
+ *
429
+ * @param {Event} e
430
+ * @returns void
431
+ */
432
+ function _handleKey(e) {
433
+
434
+ // add which for key events
435
+ // @see http://stackoverflow.com/questions/4285627/javascript-keycode-vs-charcode-utter-confusion
436
+ e.which = typeof e.which == "number" ? e.which : e.keyCode;
437
+
438
+ var character = _characterFromEvent(e);
439
+
440
+ // no character found then stop
441
+ if (!character) {
442
+ return;
443
+ }
444
+
445
+ if (e.type === 'keyup' && _ignore_next_keyup === character) {
446
+ _ignore_next_keyup = false;
447
+ return;
448
+ }
449
+
450
+ _fireCallback(character, e);
451
+ }
452
+
453
+ /**
454
+ * determines if the keycode specified is a modifier key or not
455
+ *
456
+ * @param {string} key
457
+ * @returns {boolean}
458
+ */
459
+ function _isModifier(key) {
460
+ return key == 'shift' || key == 'ctrl' || key == 'alt' || key == 'meta';
461
+ }
462
+
463
+ /**
464
+ * called to set a 1 second timeout on the specified sequence
465
+ *
466
+ * this is so after each key press in the sequence you have 1 second
467
+ * to press the next key before you have to start over
468
+ *
469
+ * @returns void
470
+ */
471
+ function _resetSequence() {
472
+ clearTimeout(_reset_timer);
473
+ _reset_timer = setTimeout(_resetSequences, 1000);
474
+ }
475
+
476
+ /**
477
+ * reverses the map lookup so that we can look for specific keys
478
+ * to see what can and can't use keypress
479
+ *
480
+ * @return {Object}
481
+ */
482
+ function _getReverseMap() {
483
+ if (!_REVERSE_MAP) {
484
+ _REVERSE_MAP = {};
485
+ for (var key in _MAP) {
486
+
487
+ // pull out the numberic keypad from here cause keypress should
488
+ // be able to detect the keys from the character
489
+ if (key > 95 && key < 112) {
490
+ continue;
491
+ }
492
+
493
+ if (_MAP.hasOwnProperty(key)) {
494
+ _REVERSE_MAP[_MAP[key]] = key;
495
+ }
496
+ }
497
+ }
498
+ return _REVERSE_MAP;
499
+ }
500
+
501
+ /**
502
+ * picks the best action based on the key combination
503
+ *
504
+ * @param {string} key - character for key
505
+ * @param {Array} modifiers
506
+ * @param {string=} action passed in
507
+ */
508
+ function _pickBestAction(key, modifiers, action) {
509
+
510
+ // if no action was picked in we should try to pick the one
511
+ // that we think would work best for this key
512
+ if (!action) {
513
+ action = _getReverseMap()[key] ? 'keydown' : 'keypress';
514
+ }
515
+
516
+ // modifier keys don't work as expected with keypress,
517
+ // switch to keydown
518
+ if (action === 'keypress' && modifiers.length) {
519
+ action = 'keydown';
520
+ }
521
+
522
+ return action;
523
+ }
524
+
525
+ /**
526
+ * binds a key sequence to an event
527
+ *
528
+ * @param {string} combo - combo specified in bind call
529
+ * @param {Array} keys
530
+ * @param {Function} callback
531
+ * @param {string=} action
532
+ * @returns void
533
+ */
534
+ function _bindSequence(combo, keys, callback, action) {
535
+
536
+ // start off by adding a sequence level record for this combination
537
+ // and setting the level to 0
538
+ _sequence_levels[combo] = 0;
539
+
540
+ // if there is no action pick the best one for the first key
541
+ // in the sequence
542
+ if (!action) {
543
+ action = _pickBestAction(keys[0], []);
544
+ }
545
+
546
+ /**
547
+ * callback to increase the sequence level for this sequence and reset
548
+ * all other sequences that were active
549
+ *
550
+ * @param {Event} e
551
+ * @returns void
552
+ */
553
+ var _increaseSequence = function(e) {
554
+ _inside_sequence = action;
555
+ ++_sequence_levels[combo];
556
+ _resetSequence();
557
+ },
558
+
559
+ /**
560
+ * wraps the specified callback inside of another function in order
561
+ * to reset all sequence counters as soon as this sequence is done
562
+ *
563
+ * @param {Event} e
564
+ * @returns void
565
+ */
566
+ _callbackAndReset = function(e) {
567
+ callback(e);
568
+
569
+ // we should ignore the next key up if the action is key down
570
+ // or keypress. this is so if you finish a sequence and
571
+ // release the key the final key will not trigger a keyup
572
+ if (action !== 'keyup') {
573
+ _ignore_next_keyup = _characterFromEvent(e);
574
+ }
575
+
576
+ // weird race condition if a sequence ends with the key
577
+ // another sequence begins with
578
+ setTimeout(_resetSequences, 10);
579
+ },
580
+ i;
581
+
582
+ // loop through keys one at a time and bind the appropriate callback
583
+ // function. for any key leading up to the final one it should
584
+ // increase the sequence. after the final, it should reset all sequences
585
+ for (i = 0; i < keys.length; ++i) {
586
+ _bindSingle(keys[i], i < keys.length - 1 ? _increaseSequence : _callbackAndReset, action, combo, i);
587
+ }
588
+ }
589
+
590
+ /**
591
+ * binds a single keyboard combination
592
+ *
593
+ * @param {string} combination
594
+ * @param {Function} callback
595
+ * @param {string=} action
596
+ * @param {string=} sequence_name - name of sequence if part of sequence
597
+ * @param {number=} level - what part of the sequence the command is
598
+ * @returns void
599
+ */
600
+ function _bindSingle(combination, callback, action, sequence_name, level) {
601
+
602
+ // make sure multiple spaces in a row become a single space
603
+ combination = combination.replace(/\s+/g, ' ');
604
+
605
+ var sequence = combination.split(' '),
606
+ i,
607
+ key,
608
+ keys,
609
+ modifiers = [];
610
+
611
+ // if this pattern is a sequence of keys then run through this method
612
+ // to reprocess each pattern one key at a time
613
+ if (sequence.length > 1) {
614
+ return _bindSequence(combination, sequence, callback, action);
615
+ }
616
+
617
+ // take the keys from this pattern and figure out what the actual
618
+ // pattern is all about
619
+ keys = combination === '+' ? ['+'] : combination.split('+');
620
+
621
+ for (i = 0; i < keys.length; ++i) {
622
+ key = keys[i];
623
+
624
+ // normalize key names
625
+ if (_SPECIAL_ALIASES[key]) {
626
+ key = _SPECIAL_ALIASES[key];
627
+ }
628
+
629
+ // if this is not a keypress event then we should
630
+ // be smart about using shift keys
631
+ // this will only work for US keyboards however
632
+ if (action && action != 'keypress' && _SHIFT_MAP[key]) {
633
+ key = _SHIFT_MAP[key];
634
+ modifiers.push('shift');
635
+ }
636
+
637
+ // if this key is a modifier then add it to the list of modifiers
638
+ if (_isModifier(key)) {
639
+ modifiers.push(key);
640
+ }
641
+ }
642
+
643
+ // depending on what the key combination is
644
+ // we will try to pick the best event for it
645
+ action = _pickBestAction(key, modifiers, action);
646
+
647
+ // make sure to initialize array if this is the first time
648
+ // a callback is added for this key
649
+ if (!_callbacks[key]) {
650
+ _callbacks[key] = [];
651
+ }
652
+
653
+ // remove an existing match if there is one
654
+ _getMatches(key, modifiers, action, !sequence_name, combination);
655
+
656
+ // add this call back to the array
657
+ // if it is a sequence put it at the beginning
658
+ // if not put it at the end
659
+ //
660
+ // this is important because the way these are processed expects
661
+ // the sequence ones to come first
662
+ _callbacks[key][sequence_name ? 'unshift' : 'push']({
663
+ callback: callback,
664
+ modifiers: modifiers,
665
+ action: action,
666
+ seq: sequence_name,
667
+ level: level,
668
+ combo: combination
669
+ });
670
+ }
671
+
672
+ /**
673
+ * binds multiple combinations to the same callback
674
+ *
675
+ * @param {Array} combinations
676
+ * @param {Function} callback
677
+ * @param {string|undefined} action
678
+ * @returns void
679
+ */
680
+ function _bindMultiple(combinations, callback, action) {
681
+ for (var i = 0; i < combinations.length; ++i) {
682
+ _bindSingle(combinations[i], callback, action);
683
+ }
684
+ }
685
+
686
+ // start!
687
+ _addEvent(document, 'keydown', _handleKey);
688
+ _addEvent(document, 'keyup', _handleKey);
689
+ _addEvent(document, 'keypress', _handleKey);
690
+
691
+ return {
692
+
693
+ /**
694
+ * binds an event to mousetrap
695
+ *
696
+ * can be a single key, a combination of keys separated with +,
697
+ * a comma separated list of keys, an array of keys, or
698
+ * a sequence of keys separated by spaces
699
+ *
700
+ * be sure to list the modifier keys first to make sure that the
701
+ * correct key ends up getting bound (the last key in the pattern)
702
+ *
703
+ * @param {string|Array} keys
704
+ * @param {Function} callback
705
+ * @param {string=} action - 'keypress', 'keydown', or 'keyup'
706
+ * @returns void
707
+ */
708
+ bind: function(keys, callback, action) {
709
+ _bindMultiple(keys instanceof Array ? keys : [keys], callback, action);
710
+ _direct_map[keys + ':' + action] = callback;
711
+ },
712
+
713
+ /**
714
+ * unbinds an event to mousetrap
715
+ *
716
+ * the unbinding sets the callback function of the specified key combo
717
+ * to an empty function and deletes the corresponding key in the
718
+ * _direct_map dict.
719
+ *
720
+ * the keycombo+action has to be exactly the same as
721
+ * it was defined in the bind method
722
+ *
723
+ * @todo actually remove this from the _callbacks dictionary instead
724
+ * of binding an empty function
725
+ *
726
+ * @param {string|Array} keys
727
+ * @param {string} action
728
+ * @returns void
729
+ */
730
+ unbind: function(keys, action) {
731
+ if (_direct_map[keys + ':' + action]) {
732
+ delete _direct_map[keys + ':' + action];
733
+ this.bind(keys, function() {}, action);
734
+ }
735
+ },
736
+
737
+ /**
738
+ * triggers an event that has already been bound
739
+ *
740
+ * @param {string} keys
741
+ * @param {string=} action
742
+ * @returns void
743
+ */
744
+ trigger: function(keys, action) {
745
+ _direct_map[keys + ':' + action]();
746
+ },
747
+
748
+ /**
749
+ * resets the library back to its initial state. this is useful
750
+ * if you want to clear out the current keyboard shortcuts and bind
751
+ * new ones - for example if you switch to another page
752
+ *
753
+ * @returns void
754
+ */
755
+ reset: function() {
756
+ _callbacks = {};
757
+ _direct_map = {};
758
+ }
759
+ };
760
+ }) ();
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mousetrapjs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-09 00:00:00.000000000 Z
12
+ date: 2012-07-11 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: Gem for Mousetrap js lib
15
15
  email:
@@ -47,7 +47,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
47
47
  version: '0'
48
48
  requirements: []
49
49
  rubyforge_project:
50
- rubygems_version: 1.8.23
50
+ rubygems_version: 1.8.22
51
51
  signing_key:
52
52
  specification_version: 3
53
53
  summary: Gem for Mousetrap js lib