1 /* Copyright 2014 Yukkuri Games
2 Licensed under the terms of the GNU GPL v2 or later
3 @license http://www.gnu.org/licenses/gpl-2.0.html
4 @source: http://yukkurigames.com/yuu/
10 var yT
= this.yT
|| require("./yT");
11 var yf
= this.yf
|| require("./yf");
31 // Fill in the key name tables.
32 for (var i
= "A".charCodeAt(0); i
<= "Z".charCodeAt(0); ++i
)
33 yuu
.KEY_NAMES
[i
] = String
.fromCharCode(i
).toLowerCase();
34 for (i
= "0".charCodeAt(0); i
<= "9".charCodeAt(0); ++i
)
35 yuu
.KEY_NAMES
[i
] = String
.fromCharCode(i
);
36 for (i
= 1; i
<= 12; ++i
)
37 yuu
.KEY_NAMES
[111 + i
] = "f" + i
;
39 function splitKeys (keystring
) {
40 return keystring
.toLowerCase().split("+").sort();
44 constructor: function (keystring
, command
) {
45 /** An individual key to command binding.
47 The key string is e.g. "a", "control+f", "left+alt+z".
48 "f+control" is equivalent to "control+f", and binding one
49 in a set will override the other. */
50 this.keystring
= keystring
;
51 this.command
= command
;
52 this.keys
= splitKeys(keystring
);
55 uses: function (name
) {
56 /** True if the given key name is relevant to this binding. */
57 return yf
.contains(this.keys
, name
.toLowerCase());
60 on: function (pressed
) {
61 /** True if all keys in this binding are pressed. */
62 return yf
.every
.call(pressed
, yf
.getter
, this.keys
);
66 function longerBind (a
, b
) {
67 return a
.keys
.length
> b
.keys
.length
? a
: b
;
70 function longestBind (binds
) {
71 return yf
.foldl(longerBind
, binds
);
74 function isActivate (bind
) {
75 return bind
.command
[0] === '+' && bind
.command
[1] !== '+';
78 function anticommand (bind
) {
79 return "-" + bind
.command
.substring(1);
83 constructor: function (binds
) {
84 /** A group of key bindings.
86 A set may only have one bind per key combination
87 (regardless of order) at a time. Binding already-bound
88 keys to a different command will overwrite, not duplicate,
92 yf
.ipairs
.call(this, this.bind
, binds
|| {});
95 bind: function (keystring
, command
) {
96 /** Bind keys to a command in this set. */
97 var bind
= new KeyBind(keystring
, command
);
98 this.binds
= this.binds
.filter(function (b
) {
99 return !yf
.seqEqual(b
.keys
, bind
.keys
);
103 unbind: function (keystring
) {
104 /** Unbind keys from this set. */
105 var keys
= splitKeys(keystring
);
106 this.binds
= this.binds
.filter(function (b
) {
107 return !yf
.seqEqual(b
.keys
, keys
);
112 yuu
.keyEventName = function (event
) {
113 return yuu
.keyCodeName(event
.keyCode
, event
.key
|| event
.keyIdentifier
);
116 yuu
.keyCodeName = function (code
, defaultName
) {
118 defaultName
= defaultName
.toLowerCase();
119 if (defaultName
=== "unidentified")
121 var name
= yuu
.KEY_NAMES
[code
] || defaultName
;
123 name
= "key:" + code
;
127 yuu
.InputState
= yT({
128 constructor: function (bindsets
) {
129 this._bindsets
= yf
.slice(bindsets
|| [yuu
.defaultKeybinds
]);
131 /** The current state of each key; 0 if not pressed,
132 the time it was pressed if it is pressed. */
135 push: function (bindset
) {
136 /** Add a key bind set to the handler stack. */
137 this._bindsets
= this._bindsets
.concat(bindset
);
140 remove: function (bindset
) {
141 /** Remove a key bind set from the handler stack. */
142 this._bindsets
= yf
.without(this._bindsets
, bindset
);
145 insertBefore: function (bindset
, before
) {
146 this._bindsets
= yf
.insertBefore(
147 this._bindsets
.slice(), bindset
, before
);
150 _triggeredBinds: function (name
) {
151 var pressed
= this.pressed
;
152 function triggered (bind
) {
153 return bind
.uses(name
) && bind
.on(pressed
);
156 yf
.each(function (bindset
) {
157 binds
.push
.apply(binds
, yf
.filter(triggered
, bindset
.binds
));
162 _down: function (name
) {
163 /** Mark the input as down, return an array of commands.
165 This array is always of length 1 for down/change events.
167 Returns null if no binds were triggered, which is slightly
168 different than binds being triggered but no commands are
171 var pressed
= this.pressed
[name
];
172 this.pressed
[name
] = Date
.now();
173 var bind
= longestBind(this._triggeredBinds(name
));
174 return bind
? pressed
? [] : [bind
.command
] : null;
177 _up: function (name
) {
178 /** Mark the input as down, return an array of commands.
180 Only binds for commands with the special + form are
181 returned on release, and the + is converted to a -.
183 Returns null if no binds were triggered, which is slightly
184 different than binds being triggered but no commands are
187 if (!this.pressed
[name
])
189 var cmds
= yf
.map(anticommand
, yf
.filter(
190 isActivate
, this._triggeredBinds(name
)));
191 this.pressed
[name
] = 0;
192 return cmds
.length
? cmds
: null;
195 change: function (name
) {
196 /** Mark the input as changed, return an array of commands.
198 `change` is for inputs that do not have meaningful
199 "down" or "up" states, like moving a mouse. Instead,
200 it fires when the input's state changes - e.g. when
201 the x and y position change.
203 The `pressed` table remains unmodified as a result of
204 `change` inputs. Like `down`, `change` returns only the
205 first match it finds.
207 this.pressed
[name
] = Date
.now();
208 var bind
= longestBind(this._triggeredBinds(name
));
209 this.pressed
[name
] = 0;
210 return bind
? [bind
.command
] : null;
213 keydown
: { proxy
: "_down" },
214 keyup
: { proxy
: "_up" },
216 gamepadbuttondown: function (gamepad
, button
) {
217 return this._down("gamepad" + gamepad
.index
+ "button" + button
)
218 || this._down("gamepadbutton" + button
);
221 gamepadbuttonup: function (gamepad
, button
) {
222 return this._up("gamepad" + gamepad
.index
+ "button" + button
)
223 || this._up("gamepadbutton" + button
);
226 mousemove: function () {
227 return this.change("mousemove");
230 mousedown: function (button
) {
231 return this._down("mouse" + button
);
234 mouseup: function (button
) {
235 return this._up("mouse" + button
);
243 yuu
.stopPropagation
= function stopPropagation (event
, preventDefault
) {
244 event
.stopPropagation();
246 event
.preventDefault();
247 if (event
.stopImmediatePropagation
)
248 event
.stopImmediatePropagation();
249 if (event
.gesture
&& event
.gesture
!== event
)
250 stopPropagation(event
.gesture
, preventDefault
);
253 yuu
.registerInitHook(function () {
254 yuu
.defaultCommands
.bind
= yuu
.cmd(function (key
, command
) {
255 yuu
.defaultKeybinds
.bind(key
, command
);
256 }, "<key> <command>", "bind a key to a command");
258 yuu
.defaultCommands
.unbind
= yuu
.cmd(function (key
) {
259 yuu
.defaultKeybinds
.unbind(key
);
260 }, "<key>", "unbind a key");
262 yuu
.defaultKeybinds
= new yuu
.KeyBindSet();
263 /** The default / debugging bind set */
266 }).call(typeof exports
=== "undefined" ? this : exports
,
267 typeof exports
=== "undefined"
268 ? this.yuu
: (module
.exports
= require('./core')));