mousetrap-rails 0.0.1

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