kea-rails 2.0.0.pre.alpha9 → 2.0.0.pre.alpha10
Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eb3b31ed74e896c174cb54a09cdc2a93ac5ee4e3
|
4
|
+
data.tar.gz: 5b692d4a6acc81b6e0393e24a4b2cf101e2196fc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 181e188768920beff23e2d02ff259632e2068f0433647a119f31e339f74a124f56740dafd161d8348a2aecace564d6b66892480d49b06ca02b0d797cf26b06f6
|
7
|
+
data.tar.gz: ffec861f77ad0135edbdaef2ee258da73bd676025d32db1e91b69e24ca640842b522d8b5ef2185893370f5a83b649ed235a9aeb5c95913c2c6f930b6325ee153
|
@@ -17,6 +17,8 @@ class Kea::InstallGenerator < Rails::Generators::Base
|
|
17
17
|
empty_directory namespaced_path("app/assets/javascripts", "services")
|
18
18
|
create_file namespaced_path("app/assets/javascripts", "services/.keep")
|
19
19
|
empty_directory namespaced_path("app/assets/javascripts", "viewmodels")
|
20
|
+
empty_directory namespaced_path("app/assets/javascripts", "components")
|
21
|
+
create_file namespaced_path("app/assets/javascripts", "components/.keep")
|
20
22
|
empty_directory namespaced_path("app/assets/javascripts", "sherlock")
|
21
23
|
create_file namespaced_path("app/assets/javascripts", "sherlock/.keep")
|
22
24
|
end
|
@@ -42,6 +44,7 @@ class Kea::InstallGenerator < Rails::Generators::Base
|
|
42
44
|
//= require_directory ./services
|
43
45
|
//= require ./viewmodels/main
|
44
46
|
//= require_directory ./viewmodels
|
47
|
+
//= require_directory ./components
|
45
48
|
|
46
49
|
//= require_directory ./sherlock
|
47
50
|
|
@@ -51,7 +51,7 @@ class Kea::ModelGenerator < Rails::Generators::NamedBase
|
|
51
51
|
unserializable_attributes << attribute if @klass.attribute_names.include?(attribute)
|
52
52
|
end
|
53
53
|
|
54
|
-
attribute_initializers << "this.
|
54
|
+
attribute_initializers << "this.unserializableAttributes([#{unserializable_attributes.collect { |a| "'#{a}'"}.join(", ")}]);"
|
55
55
|
|
56
56
|
@model_attributes.in_groups_of(5, false) do |group|
|
57
57
|
serializable_attribute_strings << ' ' + group.collect { |attribute| "'#{attribute}'" }.join(', ') + ",\n"
|
@@ -59,7 +59,7 @@ class Kea::ModelGenerator < Rails::Generators::NamedBase
|
|
59
59
|
|
60
60
|
serializable_attribute_strings.gsub!(/,\n\z/, "\n")
|
61
61
|
|
62
|
-
attribute_initializers << "this.
|
62
|
+
attribute_initializers << "this.serializableAttributes([\n#{serializable_attribute_strings} ]);"
|
63
63
|
|
64
64
|
@model_associations.each do |assoc|
|
65
65
|
attribute_initializers << case assoc.macro
|
data/lib/kea-rails/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kea-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.0.pre.
|
4
|
+
version: 2.0.0.pre.alpha10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jan-Christian Foeh
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-04
|
11
|
+
date: 2015-05-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -98,7 +98,6 @@ files:
|
|
98
98
|
- vendor/assets/components/Keypress/README.md
|
99
99
|
- vendor/assets/components/Keypress/bower.json
|
100
100
|
- vendor/assets/components/Keypress/keypress-2.1.0.min.js
|
101
|
-
- vendor/assets/components/Keypress/keypress.coffee
|
102
101
|
- vendor/assets/components/Keypress/keypress.js
|
103
102
|
- vendor/assets/components/Keypress/package.json
|
104
103
|
- vendor/assets/components/attachejs/attache-jquery.js
|
@@ -1,921 +0,0 @@
|
|
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
|