kea-rails 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. checksums.yaml +4 -4
  2. data/lib/kea-rails/version.rb +1 -1
  3. data/vendor/assets/components/Keypress/LICENSE +191 -0
  4. data/vendor/assets/components/Keypress/README.md +78 -0
  5. data/vendor/assets/components/Keypress/bower.json +28 -0
  6. data/vendor/assets/components/Keypress/keypress-2.1.0.min.js +33 -0
  7. data/vendor/assets/components/Keypress/keypress.coffee +921 -0
  8. data/vendor/assets/components/Keypress/keypress.js +1132 -0
  9. data/vendor/assets/components/Keypress/package.json +15 -0
  10. data/vendor/assets/components/attachejs/attache-jquery.js +47 -0
  11. data/vendor/assets/components/attachejs/attache-knockout.js +56 -0
  12. data/vendor/assets/components/attachejs/attache.css +10 -0
  13. data/vendor/assets/components/attachejs/attache.js +589 -0
  14. data/vendor/assets/components/attachejs/bower.json +36 -0
  15. data/vendor/assets/components/humane-js/bower.json +8 -0
  16. data/vendor/assets/components/humane-js/changelog.md +165 -0
  17. data/vendor/assets/components/humane-js/humane.js +238 -0
  18. data/vendor/assets/components/humane-js/humane.min.js +11 -0
  19. data/vendor/assets/components/humane-js/index.html +190 -0
  20. data/vendor/assets/components/humane-js/package.json +25 -0
  21. data/vendor/assets/components/humane-js/readme.md +85 -0
  22. data/vendor/assets/components/humane-js/test/issue23.html +11 -0
  23. data/vendor/assets/components/humane-js/test/issue36.html +9 -0
  24. data/vendor/assets/components/humane-js/test/issue38.html +15 -0
  25. data/vendor/assets/components/humane-js/test/issue49.html +9 -0
  26. data/vendor/assets/components/humane-js/theme-src/bigbox.styl +65 -0
  27. data/vendor/assets/components/humane-js/theme-src/boldlight.styl +64 -0
  28. data/vendor/assets/components/humane-js/theme-src/jackedup.styl +69 -0
  29. data/vendor/assets/components/humane-js/theme-src/libnotify.styl +61 -0
  30. data/vendor/assets/components/humane-js/theme-src/original.styl +51 -0
  31. data/vendor/assets/components/humane-js/themes/bigbox.css +123 -0
  32. data/vendor/assets/components/humane-js/themes/boldlight.css +122 -0
  33. data/vendor/assets/components/humane-js/themes/flatty.css +94 -0
  34. data/vendor/assets/components/humane-js/themes/jackedup.css +123 -0
  35. data/vendor/assets/components/humane-js/themes/libnotify.css +115 -0
  36. data/vendor/assets/components/humane-js/themes/original.css +72 -0
  37. data/vendor/assets/components/knockout-sortable/README.md +129 -0
  38. data/vendor/assets/components/knockout-sortable/bower.json +15 -0
  39. data/vendor/assets/components/knockout-sortable/build/knockout-sortable.js +358 -0
  40. data/vendor/assets/components/knockout-sortable/build/knockout-sortable.min.js +2 -0
  41. data/vendor/assets/components/uri.js/README.md +437 -0
  42. data/vendor/assets/components/uri.js/URI.jquery.json +38 -0
  43. data/vendor/assets/components/uri.js/about-uris.html +156 -0
  44. data/vendor/assets/components/uri.js/build.html +66 -0
  45. data/vendor/assets/components/uri.js/build.js +78 -0
  46. data/vendor/assets/components/uri.js/component.json +15 -0
  47. data/vendor/assets/components/uri.js/contribute.md +11 -0
  48. data/vendor/assets/components/uri.js/docs.html +1280 -0
  49. data/vendor/assets/components/uri.js/index.html +173 -0
  50. data/vendor/assets/components/uri.js/jquery-1.10.2.min.js +6 -0
  51. data/vendor/assets/components/uri.js/jquery-1.7.2.min.js +4 -0
  52. data/vendor/assets/components/uri.js/jquery-1.8.2.min.js +2 -0
  53. data/vendor/assets/components/uri.js/jquery-1.9.1.min.js +5 -0
  54. data/vendor/assets/components/uri.js/jquery-uri-plugin.html +203 -0
  55. data/vendor/assets/components/uri.js/package.json +75 -0
  56. data/vendor/assets/components/uri.js/prettify/lang-apollo.js +2 -0
  57. data/vendor/assets/components/uri.js/prettify/lang-clj.js +18 -0
  58. data/vendor/assets/components/uri.js/prettify/lang-css.js +2 -0
  59. data/vendor/assets/components/uri.js/prettify/lang-go.js +1 -0
  60. data/vendor/assets/components/uri.js/prettify/lang-hs.js +2 -0
  61. data/vendor/assets/components/uri.js/prettify/lang-lisp.js +3 -0
  62. data/vendor/assets/components/uri.js/prettify/lang-lua.js +2 -0
  63. data/vendor/assets/components/uri.js/prettify/lang-ml.js +2 -0
  64. data/vendor/assets/components/uri.js/prettify/lang-n.js +4 -0
  65. data/vendor/assets/components/uri.js/prettify/lang-proto.js +1 -0
  66. data/vendor/assets/components/uri.js/prettify/lang-scala.js +2 -0
  67. data/vendor/assets/components/uri.js/prettify/lang-sql.js +2 -0
  68. data/vendor/assets/components/uri.js/prettify/lang-tex.js +1 -0
  69. data/vendor/assets/components/uri.js/prettify/lang-vb.js +2 -0
  70. data/vendor/assets/components/uri.js/prettify/lang-vhdl.js +3 -0
  71. data/vendor/assets/components/uri.js/prettify/lang-wiki.js +2 -0
  72. data/vendor/assets/components/uri.js/prettify/lang-xq.js +3 -0
  73. data/vendor/assets/components/uri.js/prettify/lang-yaml.js +2 -0
  74. data/vendor/assets/components/uri.js/prettify/prettify.css +1 -0
  75. data/vendor/assets/components/uri.js/prettify/prettify.js +28 -0
  76. data/vendor/assets/components/uri.js/prettify/prettify.sunburst.css +52 -0
  77. data/vendor/assets/components/uri.js/screen.css +167 -0
  78. data/vendor/assets/components/uri.js/screen.js +39 -0
  79. data/vendor/assets/components/uri.js/src/IPv6.js +185 -0
  80. data/vendor/assets/components/uri.js/src/SecondLevelDomains.js +220 -0
  81. data/vendor/assets/components/uri.js/src/URI.fragmentQuery.js +103 -0
  82. data/vendor/assets/components/uri.js/src/URI.fragmentURI.js +96 -0
  83. data/vendor/assets/components/uri.js/src/URI.js +1938 -0
  84. data/vendor/assets/components/uri.js/src/URI.min.js +81 -0
  85. data/vendor/assets/components/uri.js/src/URITemplate.js +494 -0
  86. data/vendor/assets/components/uri.js/src/jquery.URI.js +232 -0
  87. data/vendor/assets/components/uri.js/src/jquery.URI.min.js +7 -0
  88. data/vendor/assets/components/uri.js/src/punycode.js +508 -0
  89. data/vendor/assets/components/uri.js/test/index.html +26 -0
  90. data/vendor/assets/components/uri.js/test/pre_libs.js +4 -0
  91. data/vendor/assets/components/uri.js/test/qunit/qunit-composite.css +13 -0
  92. data/vendor/assets/components/uri.js/test/qunit/qunit-composite.js +167 -0
  93. data/vendor/assets/components/uri.js/test/qunit/qunit.css +244 -0
  94. data/vendor/assets/components/uri.js/test/qunit/qunit.js +2212 -0
  95. data/vendor/assets/components/uri.js/test/test.URI.html +26 -0
  96. data/vendor/assets/components/uri.js/test/test.fragmentQuery.html +31 -0
  97. data/vendor/assets/components/uri.js/test/test.fragmentURI.html +31 -0
  98. data/vendor/assets/components/uri.js/test/test.jQuery-1.10.html +31 -0
  99. data/vendor/assets/components/uri.js/test/test.jQuery-1.7.html +31 -0
  100. data/vendor/assets/components/uri.js/test/test.jQuery-1.8.html +31 -0
  101. data/vendor/assets/components/uri.js/test/test.jQuery-1.9.html +31 -0
  102. data/vendor/assets/components/uri.js/test/test.js +1409 -0
  103. data/vendor/assets/components/uri.js/test/test_fragmentQuery.js +57 -0
  104. data/vendor/assets/components/uri.js/test/test_fragmentURI.js +59 -0
  105. data/vendor/assets/components/uri.js/test/test_jim.js +143 -0
  106. data/vendor/assets/components/uri.js/test/test_jquery.js +138 -0
  107. data/vendor/assets/components/uri.js/test/test_template.js +385 -0
  108. data/vendor/assets/components/uri.js/test/urls.js +1236 -0
  109. data/vendor/assets/components/uri.js/uri-template.html +234 -0
  110. data/vendor/assets/components/uri.js/utils/SLDs.php +37 -0
  111. data/vendor/assets/components/uri.js/utils/sld.js +101 -0
  112. data/vendor/assets/components/veiljs/bower.json +36 -0
  113. data/vendor/assets/components/veiljs/veil-jquery.js +47 -0
  114. data/vendor/assets/components/veiljs/veil-knockout.js +55 -0
  115. data/vendor/assets/components/veiljs/veil.js +465 -0
  116. data/vendor/assets/javascripts/fuse.js +472 -0
  117. data/vendor/assets/javascripts/jquery-ui.js +3844 -0
  118. data/vendor/assets/javascripts/knockout-3.2.0-debug.js +5299 -0
  119. data/vendor/assets/javascripts/moment.js +1902 -0
  120. data/vendor/assets/javascripts/pikaday.js +896 -0
  121. data/vendor/assets/stylesheets/pikaday.css +171 -0
  122. metadata +120 -1
@@ -0,0 +1,921 @@
1
+ ###
2
+ Copyright 2014 David Mauro
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
+ Keypress is a robust keyboard input capturing Javascript utility
17
+ focused on input for games.
18
+
19
+ version 2.1.0
20
+ ###
21
+
22
+ ###
23
+ Combo options available and their defaults:
24
+ keys : [] - An array of the keys pressed together to activate combo.
25
+ count : 0 - The number of times a counting combo has been pressed. Reset on release.
26
+ is_unordered : false - Unless this is set to true, the keys can be pressed down in any order.
27
+ is_counting : false - Makes this a counting combo (see documentation).
28
+ is_exclusive : false - This combo will replace other exclusive combos when true.
29
+ is_solitary : false - This combo will only fire if ONLY it's keys are pressed down.
30
+ is_sequence : false - Rather than a key combo, this is an ordered key sequence.
31
+ prevent_default : false - Prevent default behavior for all component key keypresses.
32
+ prevent_repeat : false - Prevent the combo from repeating when keydown is held.
33
+ on_keydown : null - A function that is called when the combo is pressed.
34
+ on_keyup : null - A function that is called when the combo is released.
35
+ on_release : null - A function that is called when all keys in the combo are released.
36
+ this : undefined - Defines the scope for your callback functions.
37
+ ###
38
+
39
+ ###########
40
+ # Constants
41
+ ###########
42
+
43
+ _factory_defaults =
44
+ is_unordered : false
45
+ is_counting : false
46
+ is_exclusive : false
47
+ is_solitary : false
48
+ prevent_default : false
49
+ prevent_repeat : false
50
+
51
+ _modifier_keys = ["meta", "alt", "option", "ctrl", "shift", "cmd"]
52
+
53
+ _metakey = "ctrl"
54
+
55
+ ###########################
56
+ # Public object and Classes
57
+ ###########################
58
+
59
+ keypress = {}
60
+
61
+ keypress.debug = false
62
+
63
+ class Combo
64
+ constructor: (dictionary) ->
65
+ # Copy over any non-false values
66
+ for own property, value of dictionary
67
+ @[property] = value if value != false
68
+
69
+ # Standard Defaults
70
+ @keys = @keys or []
71
+ @count = @count or 0
72
+
73
+ allows_key_repeat: ->
74
+ # Combos with keydown functions should be able to rapid fire
75
+ # when holding down the key for an extended period
76
+ return not @prevent_repeat and typeof @on_keydown is "function"
77
+
78
+ reset: ->
79
+ @count = 0
80
+ @keyup_fired = null
81
+
82
+ class keypress.Listener
83
+ constructor:(element, defaults) ->
84
+ # Public properties
85
+ @should_suppress_event_defaults = false
86
+ @should_force_event_defaults = false
87
+ @sequence_delay = 800
88
+
89
+ # Private properties
90
+ @_registered_combos = []
91
+ @_keys_down = []
92
+ @_active_combos = []
93
+ @_sequence = []
94
+ @_sequence_timer = null
95
+ @_prevent_capture = false
96
+ @_defaults = defaults or {}
97
+ for own property, value of _factory_defaults
98
+ @_defaults[property] = @_defaults[property] or value
99
+
100
+ # Attach handlers to element
101
+ @element = element or document.body
102
+
103
+ attach_handler = (target, event, handler) ->
104
+ if target.addEventListener
105
+ target.addEventListener event, handler
106
+ else if target.attachEvent
107
+ target.attachEvent "on#{event}", handler
108
+
109
+ return handler
110
+
111
+ @keydown_event = attach_handler @element, "keydown", (e) =>
112
+ e = e or window.event
113
+ @_receive_input e, true
114
+ @_bug_catcher e
115
+
116
+ @keyup_event = attach_handler @element, "keyup", (e) =>
117
+ e = e or window.event
118
+ @_receive_input e, false
119
+
120
+ @blur_event = attach_handler window, "blur", =>
121
+ # Assume all keys are released when we can't catch key events
122
+ # This prevents alt+tab conflicts
123
+ for key in @_keys_down
124
+ @_key_up key, {}
125
+ @_keys_down = []
126
+
127
+ destroy: () ->
128
+ remove_handler = (target, event, handler) ->
129
+ if target.removeEventListener?
130
+ target.removeEventListener event, handler
131
+ else if target.removeEvent?
132
+ target.removeEvent "on#{event}", handler
133
+
134
+ remove_handler @element, "keydown", @keydown_event
135
+ remove_handler @element, "keyup", @keyup_event
136
+ remove_handler window, "blur", @blur_event
137
+
138
+ # Helper Methods
139
+
140
+ _bug_catcher: (e) ->
141
+ # This seems to be Mac specific weirdness, so we'll target "cmd" as metaKey
142
+ # Force a keyup for non-modifier keys when command is held because they don't fire
143
+ if _metakey is "cmd" and "cmd" in @_keys_down and _convert_key_to_readable(e.keyCode) not in ["cmd", "shift", "alt", "caps", "tab"]
144
+ @_receive_input e, false
145
+ # Note: we're currently ignoring the fact that this doesn't catch the bug that a keyup
146
+ # will not fire if you keydown a combo, then press and hold cmd, then keyup the combo.
147
+ # Perhaps we should fire keyup on all active combos when we press cmd?
148
+
149
+ _cmd_bug_check: (combo_keys) ->
150
+ # We don't want to allow combos to activate if the cmd key
151
+ # is pressed, but cmd isn't in them. This is so they don't
152
+ # accidentally rapid fire due to our hack-around for the cmd
153
+ # key bug and having to fake keyups.
154
+ if _metakey is "cmd" and "cmd" in @_keys_down and "cmd" not in combo_keys
155
+ return false
156
+ return true
157
+
158
+ _prevent_default: (e, should_prevent) ->
159
+ # If we've pressed a combo, or if we are working towards
160
+ # one, we should prevent the default keydown event.
161
+ if (should_prevent or @should_suppress_event_defaults) and not @should_force_event_defaults
162
+ if e.preventDefault then e.preventDefault() else e.returnValue = false
163
+ e.stopPropagation() if e.stopPropagation
164
+
165
+ # Tracking Combos
166
+
167
+ _get_active_combos: (key) ->
168
+ # Based on the keys_down and the key just pressed or released
169
+ # (which should not be in keys_down), we determine if any
170
+ # combo in registered_combos could be considered active.
171
+ # This will return an array of active combos
172
+
173
+ active_combos = []
174
+
175
+ # First check that every key in keys_down maps to a combo
176
+ keys_down = _filter_array @_keys_down, (down_key) ->
177
+ down_key isnt key
178
+ keys_down.push key
179
+
180
+ # Get perfect matches
181
+ @_match_combo_arrays keys_down, (match) =>
182
+ active_combos.push(match) if @_cmd_bug_check match.keys
183
+
184
+ # Get fuzzy matches
185
+ @_fuzzy_match_combo_arrays keys_down, (match) =>
186
+ return if match in active_combos
187
+ active_combos.push(match) unless match.is_solitary or not @_cmd_bug_check match.keys
188
+
189
+ return active_combos
190
+
191
+ _get_potential_combos: (key) ->
192
+ # Check if we are working towards pressing a combo.
193
+ # Used for preventing default on keys that might match
194
+ # to a combo in the future.
195
+ potentials = []
196
+ for combo in @_registered_combos
197
+ continue if combo.is_sequence
198
+ potentials.push(combo) if key in combo.keys and @_cmd_bug_check combo.keys
199
+ return potentials
200
+
201
+ _add_to_active_combos: (combo) ->
202
+ should_replace = false
203
+ should_prepend = true
204
+ already_replaced = false
205
+ # An active combo is any combo which the user has already entered.
206
+ # We use this to track when a user has released the last key of a
207
+ # combo for on_release, and to keep combos from 'overlapping'.
208
+ if combo in @_active_combos
209
+ return true
210
+ else if @_active_combos.length
211
+ # We have to check if we're replacing another active combo
212
+ # So compare the combo.keys to all active combos' keys.
213
+ for i in [0...@_active_combos.length]
214
+ active_combo = @_active_combos[i]
215
+ continue unless active_combo and active_combo.is_exclusive and combo.is_exclusive
216
+ active_keys = active_combo.keys
217
+ unless should_replace
218
+ for active_key in active_keys
219
+ should_replace = true
220
+ unless active_key in combo.keys
221
+ should_replace = false
222
+ break
223
+
224
+ if should_prepend and not should_replace
225
+ for combo_key in combo.keys
226
+ should_prepend = false
227
+ unless combo_key in active_keys
228
+ should_prepend = true
229
+ break
230
+
231
+ if should_replace
232
+ if already_replaced
233
+ active_combo = @_active_combos.splice(i, 1)[0]
234
+ active_combo.reset() if active_combo?
235
+ else
236
+ active_combo = @_active_combos.splice(i, 1, combo)[0]
237
+ active_combo.reset() if active_combo?
238
+ already_replaced = true
239
+ should_prepend = false
240
+ if should_prepend
241
+ @_active_combos.unshift combo
242
+
243
+ return should_replace or should_prepend
244
+
245
+ _remove_from_active_combos: (combo) ->
246
+ for i in [0...@_active_combos.length]
247
+ active_combo = @_active_combos[i]
248
+ if active_combo is combo
249
+ combo = @_active_combos.splice(i, 1)[0]
250
+ combo.reset()
251
+ break
252
+ return
253
+
254
+ # Sequence Methods
255
+
256
+ _get_possible_sequences: ->
257
+ # Determine what if any sequences we're working towards.
258
+ # We will consider any which any part of the end of the sequence
259
+ # matches and return all of them.
260
+ matches = []
261
+ for combo in @_registered_combos
262
+ for j in [1..@_sequence.length]
263
+ sequence = @_sequence.slice -j
264
+ continue unless combo.is_sequence
265
+ unless "shift" in combo.keys
266
+ sequence = _filter_array sequence, (key) ->
267
+ return key isnt "shift"
268
+ continue unless sequence.length
269
+ for i in [0...sequence.length]
270
+ if combo.keys[i] is sequence[i]
271
+ match = true
272
+ else
273
+ match = false
274
+ break
275
+ matches.push(combo) if match
276
+ return matches
277
+
278
+ _add_key_to_sequence: (key, e) ->
279
+ @_sequence.push key
280
+ # Now check if they're working towards a sequence
281
+ sequence_combos = @_get_possible_sequences()
282
+ if sequence_combos.length
283
+ for combo in sequence_combos
284
+ @_prevent_default e, combo.prevent_default
285
+ # If we're working towards one, give them more time to keep going
286
+ clearTimeout(@_sequence_timer) if @_sequence_timer
287
+ if @sequence_delay > -1
288
+ @_sequence_timer = setTimeout ->
289
+ @_sequence = []
290
+ , @sequence_delay
291
+ else
292
+ # If we're not working towards something, just clear it out
293
+ @_sequence = []
294
+ return
295
+
296
+ _get_sequence: (key) ->
297
+ # Compare _sequence to all combos
298
+ for combo in @_registered_combos
299
+ continue unless combo.is_sequence
300
+ for j in [1..@_sequence.length]
301
+ # As we are traversing backwards through the sequence keys,
302
+ # Take out any shift keys, unless shift is in the combo.
303
+ sequence = (_filter_array @_sequence, (seq_key) ->
304
+ return true if "shift" in combo.keys
305
+ return seq_key isnt "shift"
306
+ ).slice -j
307
+ continue unless combo.keys.length is sequence.length
308
+ for i in [0...sequence.length]
309
+ seq_key = sequence[i]
310
+ # Special case for shift. Ignore shift keys, unless the sequence explicitly uses them
311
+ continue if seq_key is "shift" unless "shift" in combo.keys
312
+ # Don't select this combo if we're pressing shift and shift isn't in it
313
+ continue if key is "shift" and "shift" not in combo.keys
314
+ if combo.keys[i] is seq_key
315
+ match = true
316
+ else
317
+ match = false
318
+ break
319
+ return combo if match
320
+ return false
321
+
322
+ # Catching Combos
323
+
324
+ _receive_input: (e, is_keydown) ->
325
+ # If we're not capturing input, we should
326
+ # clear out _keys_down for good measure
327
+ if @_prevent_capture
328
+ @_keys_down = [] if @_keys_down.length
329
+ return
330
+ key = _convert_key_to_readable e.keyCode
331
+ # Catch tabbing out of a non-capturing state
332
+ if !is_keydown and !@_keys_down.length and key in ["alt", _metakey]
333
+ return
334
+ return unless key
335
+ if is_keydown
336
+ @_key_down key, e
337
+ else
338
+ @_key_up key, e
339
+
340
+ _fire: (event, combo, key_event, is_autorepeat) ->
341
+ # Only fire this event if the function is defined
342
+ if typeof combo["on_" + event] is "function"
343
+ @_prevent_default key_event, (combo["on_" + event].call(combo.this, key_event, combo.count, is_autorepeat) isnt true)
344
+ # We need to mark that keyup has already happened
345
+ if event is "release"
346
+ combo.count = 0
347
+ if event is "keyup"
348
+ combo.keyup_fired = true
349
+
350
+ _match_combo_arrays: (potential_match, match_handler) ->
351
+ # This will return all combos that match
352
+ for source_combo in @_registered_combos
353
+ if (not source_combo.is_unordered and _compare_arrays_sorted(potential_match, source_combo.keys)) or (source_combo.is_unordered and _compare_arrays(potential_match, source_combo.keys))
354
+ match_handler source_combo
355
+ return
356
+
357
+ _fuzzy_match_combo_arrays: (potential_match, match_handler) ->
358
+ # This will return combos that match even if other keys are pressed
359
+ for source_combo in @_registered_combos
360
+ if (not source_combo.is_unordered and _is_array_in_array_sorted(source_combo.keys, potential_match)) or (source_combo.is_unordered and _is_array_in_array(source_combo.keys, potential_match))
361
+ match_handler source_combo
362
+ return
363
+
364
+ _keys_remain: (combo) ->
365
+ for key in combo.keys
366
+ if key in @_keys_down
367
+ keys_remain = true
368
+ break
369
+ return keys_remain
370
+
371
+ _key_down: (key, e) ->
372
+ # Check if we're holding shift
373
+ shifted_key = _convert_to_shifted_key key, e
374
+ key = shifted_key if shifted_key
375
+
376
+ # Add the key to sequences
377
+ @_add_key_to_sequence key, e
378
+ sequence_combo = @_get_sequence key
379
+ @_fire("keydown", sequence_combo, e) if sequence_combo
380
+
381
+ # We might have modifier keys down when coming back to
382
+ # this window and they might not be in _keys_down, so
383
+ # we're doing a check to make sure we put it back in.
384
+ # This only works for explicit modifier keys.
385
+ for mod, event_mod of _modifier_event_mapping
386
+ continue unless e[event_mod]
387
+ continue if mod is key or mod in @_keys_down
388
+ @_keys_down.push mod
389
+ # Alternatively, we might not have modifier keys down
390
+ # that we think are, so we should catch those too
391
+ for mod, event_mod of _modifier_event_mapping
392
+ continue if mod is key
393
+ if mod in @_keys_down and not e[event_mod]
394
+ # The Windows key will think it is the cmd key, but won't trigger the event mod
395
+ continue if mod is "cmd" and _metakey isnt "cmd"
396
+ for i in [0...@_keys_down.length]
397
+ @_keys_down.splice(i, 1) if @_keys_down[i] is mod
398
+
399
+ # Find which combos we have pressed or might be working towards, and prevent default
400
+ combos = @_get_active_combos key
401
+ potential_combos = @_get_potential_combos key
402
+ for combo in combos
403
+ @_handle_combo_down combo, potential_combos, key, e
404
+ if potential_combos.length
405
+ for potential in potential_combos
406
+ @_prevent_default e, potential.prevent_default
407
+
408
+ if key not in @_keys_down
409
+ @_keys_down.push key
410
+ return
411
+
412
+ _handle_combo_down: (combo, potential_combos, key, e) ->
413
+ # Make sure we're not trying to fire for a combo that already fired
414
+ return false unless key in combo.keys
415
+
416
+ @_prevent_default e, (combo and combo.prevent_default)
417
+
418
+ is_autorepeat = false
419
+ # If we've already pressed this key, check that we want to fire
420
+ # again, otherwise just add it to the keys_down list.
421
+ if key in @_keys_down
422
+ is_autorepeat = true
423
+ return false unless combo.allows_key_repeat()
424
+
425
+ # Now we add this combo or replace it in _active_combos
426
+ result = @_add_to_active_combos combo, key
427
+
428
+ # We reset the keyup_fired property because you should be
429
+ # able to fire that again, if you've pressed the key down again
430
+ combo.keyup_fired = false
431
+
432
+ # Now we fire the keydown event unless there is a larger exclusive potential combo
433
+ is_other_exclusive = false
434
+ if combo.is_exclusive
435
+ for potential_combo in potential_combos
436
+ if potential_combo.is_exclusive and potential_combo.keys.length > combo.keys.length
437
+ is_other_exclusive = true
438
+ break
439
+
440
+ unless is_other_exclusive
441
+ if combo.is_counting and typeof combo.on_keydown is "function"
442
+ combo.count += 1
443
+
444
+ # Only fire keydown if we added it
445
+ if result
446
+ @_fire "keydown", combo, e, is_autorepeat
447
+
448
+ _key_up: (key, e) ->
449
+ # Check if we're holding shift
450
+ unshifted_key = key
451
+ shifted_key = _convert_to_shifted_key key, e
452
+ key = shifted_key if shifted_key
453
+ shifted_key = _keycode_shifted_keys[unshifted_key]
454
+ # We have to make sure the key matches to what we had in _keys_down
455
+ if e.shiftKey
456
+ key = unshifted_key unless shifted_key and shifted_key in @_keys_down
457
+ else
458
+ key = shifted_key unless unshifted_key and unshifted_key in @_keys_down
459
+
460
+ # Check if we have a keyup firing
461
+ sequence_combo = @_get_sequence key
462
+ @_fire("keyup", sequence_combo, e) if sequence_combo
463
+
464
+ # Remove from the list
465
+ return false unless key in @_keys_down
466
+ for i in [0...@_keys_down.length]
467
+ if @_keys_down[i] in [key, shifted_key, unshifted_key]
468
+ @_keys_down.splice i, 1
469
+ break
470
+
471
+ # Store this for later cleanup
472
+ active_combos_length = @_active_combos.length
473
+
474
+ # When releasing we should only check if we
475
+ # match from _active_combos so that we don't
476
+ # accidentally fire for a combo that was a
477
+ # smaller part of the one we actually wanted.
478
+ combos = []
479
+ for active_combo in @_active_combos
480
+ if key in active_combo.keys
481
+ combos.push active_combo
482
+ for combo in combos
483
+ @_handle_combo_up combo, e, key
484
+
485
+ # We also need to check other combos that might still be in active_combos
486
+ # and needs to be removed from it.
487
+ if active_combos_length > 1
488
+ for active_combo in @_active_combos
489
+ continue if active_combo is undefined or active_combo in combos
490
+ unless @_keys_remain active_combo
491
+ @_remove_from_active_combos active_combo
492
+ return
493
+
494
+ _handle_combo_up: (combo, e, key) ->
495
+ @_prevent_default e, (combo and combo.prevent_default)
496
+
497
+ # Check if any keys from this combo are still being held.
498
+ keys_remaining = @_keys_remain combo
499
+
500
+ # Any unactivated combos will fire
501
+ if !combo.keyup_fired
502
+ # And we should not fire it if it is a solitary combo and something else is pressed
503
+ keys_down = @_keys_down.slice()
504
+ keys_down.push key
505
+ if not combo.is_solitary or _compare_arrays keys_down, combo.keys
506
+ @_fire "keyup", combo, e
507
+ # Dont' add to the count unless we only have a keyup callback
508
+ if combo.is_counting and typeof combo.on_keyup is "function" and typeof combo.on_keydown isnt "function"
509
+ combo.count += 1
510
+
511
+ # If this was the last key released of the combo, clean up.
512
+ unless keys_remaining
513
+ @_fire "release", combo, e
514
+ @_remove_from_active_combos combo
515
+ return
516
+
517
+ # Public Registration Methods
518
+
519
+ simple_combo: (keys, callback) ->
520
+ # Shortcut for simple combos.
521
+ @register_combo(
522
+ keys : keys
523
+ on_keydown : callback
524
+ )
525
+
526
+ counting_combo: (keys, count_callback) ->
527
+ # Shortcut for counting combos
528
+ @register_combo(
529
+ keys : keys
530
+ is_counting : true
531
+ is_unordered : false
532
+ on_keydown : count_callback
533
+ )
534
+
535
+ sequence_combo: (keys, callback) ->
536
+ @register_combo(
537
+ keys : keys
538
+ on_keydown : callback
539
+ is_sequence : true
540
+ )
541
+
542
+ register_combo: (combo_dictionary) ->
543
+ # Allow a space dilineated string instead of array
544
+ if typeof combo_dictionary["keys"] is "string"
545
+ combo_dictionary["keys"] = combo_dictionary["keys"].split " "
546
+ for own property, value of @_defaults
547
+ if combo_dictionary[property] is undefined
548
+ combo_dictionary[property] = value
549
+ combo = new Combo combo_dictionary
550
+
551
+ if _validate_combo combo
552
+ @_registered_combos.push combo
553
+ return combo
554
+
555
+ register_many: (combo_array) ->
556
+ # Will return an array of the combos actually registered
557
+ @register_combo(combo) for combo in combo_array
558
+
559
+ unregister_combo: (keys_or_combo) ->
560
+ return false unless keys_or_combo
561
+
562
+ unregister_combo = (combo) =>
563
+ for i in [0...@_registered_combos.length]
564
+ if combo is @_registered_combos[i]
565
+ @_registered_combos.splice i, 1
566
+ break
567
+
568
+ if keys_or_combo instanceof Combo
569
+ unregister_combo keys_or_combo
570
+ else
571
+ if typeof keys_or_combo is "string"
572
+ keys_or_combo = keys_or_combo.split " "
573
+ for combo in @_registered_combos
574
+ continue unless combo?
575
+ if (combo.is_unordered and _compare_arrays(keys_or_combo, combo.keys)) or (not combo.is_unordered and _compare_arrays_sorted(keys_or_combo, combo.keys))
576
+ unregister_combo combo
577
+
578
+ unregister_many: (combo_array) ->
579
+ for combo in combo_array
580
+ @unregister_combo combo
581
+
582
+ # Other public methods
583
+
584
+ get_registered_combos: ->
585
+ return @_registered_combos
586
+
587
+ reset: ->
588
+ @_registered_combos = []
589
+
590
+ listen: ->
591
+ @_prevent_capture = false
592
+
593
+ stop_listening: ->
594
+ @_prevent_capture = true
595
+
596
+ get_meta_key: ->
597
+ # Helpful for debugging purposes
598
+ return _metakey
599
+
600
+ ##################
601
+ # Helper Functions
602
+ ##################
603
+
604
+ _decide_meta_key = ->
605
+ # If the useragent reports Mac OS X, assume cmd is metakey
606
+ if navigator.userAgent.indexOf("Mac OS X") != -1
607
+ _metakey = "cmd"
608
+ return
609
+
610
+ _change_keycodes_by_browser = ->
611
+ if navigator.userAgent.indexOf("Opera") != -1
612
+ # Opera does weird stuff with command and control keys, let's fix that.
613
+ # Note: Opera cannot override meta + s browser default of save page.
614
+ # Note: Opera does some really strange stuff when cmd+alt+shift
615
+ # are held and a non-modifier key is pressed.
616
+ _keycode_dictionary["17"] = "cmd"
617
+ return
618
+
619
+ _convert_key_to_readable = (k) ->
620
+ return _keycode_dictionary[k]
621
+
622
+ _filter_array = (array, callback) ->
623
+ if array.filter
624
+ return array.filter(callback)
625
+ else
626
+ # For browsers without Array.prototype.filter like IE<9:
627
+ return (element for element in array when callback(element))
628
+
629
+ _compare_arrays = (a1, a2) ->
630
+ # This will ignore the ordering of the arrays
631
+ # and simply check if they have the same contents.
632
+ return false unless a1.length is a2.length
633
+ for item in a1
634
+ continue if item in a2
635
+ return false
636
+ return true
637
+
638
+ _compare_arrays_sorted = (a1, a2) ->
639
+ return false unless a1.length is a2.length
640
+ for i in [0...a1.length]
641
+ return false unless a1[i] is a2[i]
642
+ return true
643
+
644
+ _is_array_in_array = (a1, a2) ->
645
+ # Returns true only if all of the contents of
646
+ # a1 are included in a2
647
+ for item in a1
648
+ return false unless item in a2
649
+ return true
650
+
651
+ _index_of_in_array = Array.prototype.indexOf or (a, item) ->
652
+ for i in [0..a.length]
653
+ return i if a[i] is item
654
+ return -1
655
+
656
+ _is_array_in_array_sorted = (a1, a2) ->
657
+ # Return true only if all of the contents of
658
+ # a1 are include in a2 and they appear in the
659
+ # same order in both.
660
+ prev = 0
661
+ for item in a1
662
+ index = _index_of_in_array.call a2, item
663
+ if index >= prev
664
+ prev = index
665
+ else
666
+ return false
667
+ return true
668
+
669
+ _log_error = () ->
670
+ console.log arguments... if keypress.debug
671
+
672
+ _key_is_valid = (key) ->
673
+ valid = false
674
+ for _, valid_key of _keycode_dictionary
675
+ if key is valid_key
676
+ valid = true
677
+ break
678
+ unless valid
679
+ for _, valid_key of _keycode_shifted_keys
680
+ if key is valid_key
681
+ valid = true
682
+ break
683
+ return valid
684
+
685
+ _validate_combo = (combo) ->
686
+ validated = true
687
+
688
+ # Warn for lack of keys
689
+ unless combo.keys.length
690
+ _log_error "You're trying to bind a combo with no keys:", combo
691
+
692
+ # Convert "meta" to either "ctrl" or "cmd"
693
+ # Don't explicity use the command key, it breaks
694
+ # because it is the windows key in Windows, and
695
+ # cannot be hijacked.
696
+ for i in [0...combo.keys.length]
697
+ key = combo.keys[i]
698
+ # Check the name and replace if needed
699
+ alt_name = _keycode_alternate_names[key]
700
+ key = combo.keys[i] = alt_name if alt_name
701
+ if key is "meta"
702
+ combo.keys.splice i, 1, _metakey
703
+ if key is "cmd"
704
+ _log_error "Warning: use the \"meta\" key rather than \"cmd\" for Windows compatibility"
705
+
706
+ # Check that all keys in the combo are valid
707
+ for key in combo.keys
708
+ unless _key_is_valid key
709
+ _log_error "Do not recognize the key \"#{key}\""
710
+ validated = false
711
+
712
+ # We can only allow a single non-modifier key
713
+ # in combos that include the command key (this
714
+ # includes 'meta') because of the keyup bug.
715
+ if "meta" in combo.keys or "cmd" in combo.keys
716
+ non_modifier_keys = combo.keys.slice()
717
+ for mod_key in _modifier_keys
718
+ if (i = _index_of_in_array.call(non_modifier_keys, mod_key)) > -1
719
+ non_modifier_keys.splice(i, 1)
720
+ if non_modifier_keys.length > 1
721
+ _log_error "META and CMD key combos cannot have more than 1 non-modifier keys", combo, non_modifier_keys
722
+ validated = false
723
+
724
+ # Tell the user if they are trying to use any
725
+ # combo properties that don't actually exist,
726
+ # but allow the combo
727
+ for property, value of combo
728
+ if _factory_defaults[property] is "undefined"
729
+ _log_error "The property #{property} is not a valid combo property. Your combo has still been registered."
730
+
731
+ return validated
732
+
733
+ _convert_to_shifted_key = (key, e) ->
734
+ return false unless e.shiftKey
735
+ k = _keycode_shifted_keys[key]
736
+ return k if k?
737
+ return false
738
+
739
+ ##########################
740
+ # Key Mapping Dictionaries
741
+ ##########################
742
+
743
+ _modifier_event_mapping =
744
+ "cmd" : "metaKey"
745
+ "ctrl" : "ctrlKey"
746
+ "shift" : "shiftKey"
747
+ "alt" : "altKey"
748
+
749
+ _keycode_alternate_names =
750
+ "escape" : "esc"
751
+ "control" : "ctrl"
752
+ "command" : "cmd"
753
+ "break" : "pause"
754
+ "windows" : "cmd"
755
+ "option" : "alt"
756
+ "caps_lock" : "caps"
757
+ "apostrophe" : "\'"
758
+ "semicolon" : ";"
759
+ "tilde" : "~"
760
+ "accent" : "`"
761
+ "scroll_lock" : "scroll"
762
+ "num_lock" : "num"
763
+
764
+ _keycode_shifted_keys =
765
+ "/" : "?"
766
+ "." : ">"
767
+ "," : "<"
768
+ "\'" : "\""
769
+ ";" : ":"
770
+ "[" : "{"
771
+ "]" : "}"
772
+ "\\" : "|"
773
+ "`" : "~"
774
+ "=" : "+"
775
+ "-" : "_"
776
+ "1" : "!"
777
+ "2" : "@"
778
+ "3" : "#"
779
+ "4" : "$"
780
+ "5" : "%"
781
+ "6" : "^"
782
+ "7" : "&"
783
+ "8" : "*"
784
+ "9" : "("
785
+ "0" : ")"
786
+
787
+ _keycode_dictionary =
788
+ 0 : "\\" # Firefox reports this keyCode when shift is held
789
+ 8 : "backspace"
790
+ 9 : "tab"
791
+ 12 : "num"
792
+ 13 : "enter"
793
+ 16 : "shift"
794
+ 17 : "ctrl"
795
+ 18 : "alt"
796
+ 19 : "pause"
797
+ 20 : "caps"
798
+ 27 : "esc"
799
+ 32 : "space"
800
+ 33 : "pageup"
801
+ 34 : "pagedown"
802
+ 35 : "end"
803
+ 36 : "home"
804
+ 37 : "left"
805
+ 38 : "up"
806
+ 39 : "right"
807
+ 40 : "down"
808
+ 44 : "print"
809
+ 45 : "insert"
810
+ 46 : "delete"
811
+ 48 : "0"
812
+ 49 : "1"
813
+ 50 : "2"
814
+ 51 : "3"
815
+ 52 : "4"
816
+ 53 : "5"
817
+ 54 : "6"
818
+ 55 : "7"
819
+ 56 : "8"
820
+ 57 : "9"
821
+ 65 : "a"
822
+ 66 : "b"
823
+ 67 : "c"
824
+ 68 : "d"
825
+ 69 : "e"
826
+ 70 : "f"
827
+ 71 : "g"
828
+ 72 : "h"
829
+ 73 : "i"
830
+ 74 : "j"
831
+ 75 : "k"
832
+ 76 : "l"
833
+ 77 : "m"
834
+ 78 : "n"
835
+ 79 : "o"
836
+ 80 : "p"
837
+ 81 : "q"
838
+ 82 : "r"
839
+ 83 : "s"
840
+ 84 : "t"
841
+ 85 : "u"
842
+ 86 : "v"
843
+ 87 : "w"
844
+ 88 : "x"
845
+ 89 : "y"
846
+ 90 : "z"
847
+ 91 : "cmd"
848
+ 92 : "cmd"
849
+ 93 : "cmd"
850
+ 96 : "num_0"
851
+ 97 : "num_1"
852
+ 98 : "num_2"
853
+ 99 : "num_3"
854
+ 100 : "num_4"
855
+ 101 : "num_5"
856
+ 102 : "num_6"
857
+ 103 : "num_7"
858
+ 104 : "num_8"
859
+ 105 : "num_9"
860
+ 106 : "num_multiply"
861
+ 107 : "num_add"
862
+ 108 : "num_enter"
863
+ 109 : "num_subtract"
864
+ 110 : "num_decimal"
865
+ 111 : "num_divide"
866
+ 112 : "f1"
867
+ 113 : "f2"
868
+ 114 : "f3"
869
+ 115 : "f4"
870
+ 116 : "f5"
871
+ 117 : "f6"
872
+ 118 : "f7"
873
+ 119 : "f8"
874
+ 120 : "f9"
875
+ 121 : "f10"
876
+ 122 : "f11"
877
+ 123 : "f12"
878
+ 124 : "print"
879
+ 144 : "num"
880
+ 145 : "scroll"
881
+ 186 : ";"
882
+ 187 : "="
883
+ 188 : ","
884
+ 189 : "-"
885
+ 190 : "."
886
+ 191 : "/"
887
+ 192 : "`"
888
+ 219 : "["
889
+ 220 : "\\"
890
+ 221 : "]"
891
+ 222 : "\'"
892
+ 223 : "`"
893
+ 224 : "cmd"
894
+ 225 : "alt"
895
+ # Opera weirdness
896
+ 57392 : "ctrl"
897
+ 63289 : "num"
898
+ # Firefox weirdness
899
+ 59 : ";"
900
+ 61 : "-"
901
+ 173 : "="
902
+
903
+ # For testing only:
904
+ keypress._keycode_dictionary = _keycode_dictionary
905
+ keypress._is_array_in_array_sorted = _is_array_in_array_sorted
906
+
907
+ ############
908
+ # Initialize
909
+ ############
910
+
911
+ _decide_meta_key()
912
+ _change_keycodes_by_browser()
913
+
914
+ # Anonymous Module Definition
915
+ if typeof define is "function" and define.amd
916
+ define [], ->
917
+ return keypress
918
+ else if exports?
919
+ exports.keypress = keypress
920
+ else
921
+ window.keypress = keypress