keyboardjs-rails 0.2.2 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/README.md +8 -0
- data/keyboardjs-rails.gemspec +2 -2
- data/lib/keyboardjs/rails/version.rb +2 -2
- data/vendor/assets/javascripts/keyboardjs.js +812 -369
- metadata +32 -30
- data/.rvmrc +0 -1
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f9aa6f3d3905e55159d80e25b33798dc3fd9c817
|
4
|
+
data.tar.gz: 49a95410dbaacec8a20d3251a6890e4b05e19c8d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4a9c678da87fdfc14ec1cc4fc8c1073c860a9b65b036e2b2efe397dceeba413991cda3d4c85eb336575bdff5122a93ca367c2b911659761b0f9f184b89101ad4
|
7
|
+
data.tar.gz: 8bf8a4969fcd0444ec6e7912c5e5f4d91c2303004a22be7a19710bc3be92c1d53426f1ce625f649ce6ce6df3fa253503377ae360541821e2a819fe4414dc340f
|
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
keyboardjs-rails
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.9.3
|
data/README.md
CHANGED
@@ -51,6 +51,14 @@ You're done!
|
|
51
51
|
5. Create new Pull Request
|
52
52
|
|
53
53
|
|
54
|
+
## Contributors
|
55
|
+
|
56
|
+
Many thanks go to the following who have contributed to making this cookbook even better:
|
57
|
+
|
58
|
+
* **[@kmmndr](https://github.com/kmmndr)**
|
59
|
+
* upgrade keyboardjs to v0.4.1
|
60
|
+
|
61
|
+
|
54
62
|
## License
|
55
63
|
|
56
64
|
**keyboardjs-rails**
|
data/keyboardjs-rails.gemspec
CHANGED
@@ -16,6 +16,6 @@ Gem::Specification.new do |gem|
|
|
16
16
|
gem.version = KeyboardJS::Rails::VERSION
|
17
17
|
|
18
18
|
gem.add_dependency "railties", ">= 3.0.0"
|
19
|
-
gem.add_development_dependency "bundler"
|
20
|
-
gem.add_development_dependency "rake"
|
19
|
+
gem.add_development_dependency "bundler"
|
20
|
+
gem.add_development_dependency "rake"
|
21
21
|
end
|
@@ -1,483 +1,926 @@
|
|
1
|
-
|
2
|
-
* KeyboardJS
|
3
|
-
*
|
1
|
+
/**
|
2
|
+
* Title: KeyboardJS
|
3
|
+
* Version: v0.4.1
|
4
|
+
* Description: KeyboardJS is a flexible and easy to use keyboard binding
|
5
|
+
* library.
|
6
|
+
* Author: Robert Hurst.
|
7
|
+
*
|
4
8
|
* Copyright 2011, Robert William Hurst
|
5
9
|
* Licenced under the BSD License.
|
6
10
|
* See https://raw.github.com/RobertWHurst/KeyboardJS/master/license.txt
|
7
11
|
*/
|
8
|
-
(function
|
9
|
-
|
10
|
-
|
11
|
-
define(factory);
|
12
|
-
} else {
|
13
|
-
// Browser globals
|
14
|
-
root.KeyboardJS = factory();
|
15
|
-
}
|
16
|
-
}(this, function() {
|
17
|
-
|
18
|
-
//polyfills for ms's peice o' shit browsers
|
19
|
-
function bind(target, type, handler) { if (target.addEventListener) { target.addEventListener(type, handler, false); } else { target.attachEvent("on" + type, function(event) { return handler.call(target, event); });Â } }
|
12
|
+
(function(context, factory) {
|
13
|
+
|
14
|
+
//INDEXOF POLLYFILL
|
20
15
|
[].indexOf||(Array.prototype.indexOf=function(a,b,c){for(c=this.length,b=(c+~~b)%c;b<c&&(!(b in this)||this[b]!==a);b++);return b^c?b:-1;});
|
21
16
|
|
22
|
-
//
|
23
|
-
|
24
|
-
'us': {
|
25
|
-
"backspace": 8,
|
26
|
-
"tab": 9,
|
27
|
-
"enter": 13,
|
28
|
-
"shift": 16,
|
29
|
-
"ctrl": 17,
|
30
|
-
"alt": 18,
|
31
|
-
"pause": 19, "break": 19,
|
32
|
-
"capslock": 20,
|
33
|
-
"escape": 27, "esc": 27,
|
34
|
-
"space": 32, "spacebar": 32,
|
35
|
-
"pageup": 33,
|
36
|
-
"pagedown": 34,
|
37
|
-
"end": 35,
|
38
|
-
"home": 36,
|
39
|
-
"left": 37,
|
40
|
-
"up": 38,
|
41
|
-
"right": 39,
|
42
|
-
"down": 40,
|
43
|
-
"insert": 45,
|
44
|
-
"delete": 46,
|
45
|
-
"0": 48, "1": 49, "2": 50, "3": 51, "4": 52, "5": 53, "6": 54, "7": 55, "8": 56, "9": 57,
|
46
|
-
"a": 65, "b": 66, "c": 67, "d": 68, "e": 69, "f": 70, "g": 71, "h": 72, "i": 73, "j": 74, "k": 75, "l": 76, "m": 77, "n": 78, "o": 79, "p": 80, "q": 81, "r": 82, "s": 83, "t": 84, "u": 85, "v": 86, "w": 87, "x": 88, "y": 89, "z": 90,
|
47
|
-
"meta": 91, "command": 91, "windows": 91, "win": 91,
|
48
|
-
"_91": 92,
|
49
|
-
"select": 93,
|
50
|
-
"num0": 96, "num1": 97, "num2": 98, "num3": 99, "num4": 100, "num5": 101, "num6": 102, "num7": 103, "num8": 104, "num9": 105,
|
51
|
-
"multiply": 106,
|
52
|
-
"add": 107,
|
53
|
-
"subtract": 109,
|
54
|
-
"decimal": 110,
|
55
|
-
"divide": 111,
|
56
|
-
"f1": 112, "f2": 113, "f3": 114, "f4": 115, "f5": 116, "f6": 117, "f7": 118, "f8": 119, "f9": 120, "f10": 121, "f11": 122, "f12": 123,
|
57
|
-
"numlock": 144, "num": 144,
|
58
|
-
"scrolllock": 145, "scroll": 145,
|
59
|
-
"semicolon": 186,
|
60
|
-
"equal": 187, "equalsign": 187,
|
61
|
-
"comma": 188,
|
62
|
-
"dash": 189,
|
63
|
-
"period": 190,
|
64
|
-
"slash": 191, "forwardslash": 191,
|
65
|
-
"graveaccent": 192,
|
66
|
-
"openbracket": 219,
|
67
|
-
"backslash": 220,
|
68
|
-
"closebracket": 221,
|
69
|
-
"singlequote": 222
|
70
|
-
}
|
17
|
+
//AMD
|
18
|
+
if(typeof define === 'function' && define.amd) { define(constructAMD); }
|
71
19
|
|
72
|
-
|
73
|
-
|
74
|
-
}
|
20
|
+
//GLOBAL
|
21
|
+
else { constructGlobal(); }
|
75
22
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
keyBindingGroups = [];
|
23
|
+
/**
|
24
|
+
* Construct AMD version of the library
|
25
|
+
*/
|
26
|
+
function constructAMD() {
|
81
27
|
|
82
|
-
|
83
|
-
|
28
|
+
//create a library instance
|
29
|
+
return init();
|
84
30
|
|
85
|
-
//
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
}
|
31
|
+
//spawns a library instance
|
32
|
+
function init() {
|
33
|
+
var library;
|
34
|
+
library = factory('amd');
|
35
|
+
library.fork = init;
|
36
|
+
return library;
|
92
37
|
}
|
38
|
+
}
|
39
|
+
|
40
|
+
/**
|
41
|
+
* Construct a Global version of the library
|
42
|
+
*/
|
43
|
+
function constructGlobal() {
|
44
|
+
var library;
|
93
45
|
|
94
|
-
//
|
95
|
-
|
46
|
+
//create a library instance
|
47
|
+
library = init();
|
48
|
+
library.noConflict('KeyboardJS', 'k');
|
96
49
|
|
97
|
-
|
50
|
+
//spawns a library instance
|
51
|
+
function init() {
|
52
|
+
var library, namespaces = [], previousValues = {};
|
98
53
|
|
99
|
-
|
100
|
-
|
54
|
+
library = factory('global');
|
55
|
+
library.fork = init;
|
56
|
+
library.noConflict = noConflict;
|
57
|
+
return library;
|
101
58
|
|
102
|
-
|
103
|
-
|
104
|
-
|
59
|
+
//sets library namespaces
|
60
|
+
function noConflict( ) {
|
61
|
+
var args, nI, newNamespaces;
|
105
62
|
|
106
|
-
|
63
|
+
newNamespaces = Array.prototype.slice.apply(arguments);
|
107
64
|
|
108
|
-
|
109
|
-
|
65
|
+
for(nI = 0; nI < namespaces.length; nI += 1) {
|
66
|
+
if(typeof previousValues[namespaces[nI]] === 'undefined') {
|
67
|
+
delete context[namespaces[nI]];
|
68
|
+
} else {
|
69
|
+
context[namespaces[nI]] = previousValues[namespaces[nI]];
|
70
|
+
}
|
110
71
|
}
|
72
|
+
|
73
|
+
previousValues = {};
|
74
|
+
|
75
|
+
for(nI = 0; nI < newNamespaces.length; nI += 1) {
|
76
|
+
if(typeof newNamespaces[nI] !== 'string') {
|
77
|
+
throw new Error('Cannot replace namespaces. All new namespaces must be strings.');
|
78
|
+
}
|
79
|
+
previousValues[newNamespaces[nI]] = context[newNamespaces[nI]];
|
80
|
+
context[newNamespaces[nI]] = library;
|
81
|
+
}
|
82
|
+
|
83
|
+
namespaces = newNamespaces;
|
84
|
+
|
85
|
+
return namespaces;
|
111
86
|
}
|
112
87
|
}
|
88
|
+
}
|
89
|
+
})(this, function(env) {
|
90
|
+
var KeyboardJS = {}, locales = {}, locale, map, macros, activeKeys = [], bindings = [], activeBindings = [],
|
91
|
+
activeMacros = [], aI, usLocale;
|
92
|
+
|
93
|
+
|
94
|
+
///////////////////////
|
95
|
+
// DEFUALT US LOCALE //
|
96
|
+
///////////////////////
|
97
|
+
|
98
|
+
//define US locale
|
99
|
+
//If you create a new locale please submit it as a pull request or post
|
100
|
+
// it in the issue tracker at
|
101
|
+
// http://github.com/RobertWhurst/KeyboardJS/issues/
|
102
|
+
usLocale = {
|
103
|
+
"map": {
|
104
|
+
|
105
|
+
//general
|
106
|
+
"3": ["cancel"],
|
107
|
+
"8": ["backspace"],
|
108
|
+
"9": ["tab"],
|
109
|
+
"12": ["clear"],
|
110
|
+
"13": ["enter"],
|
111
|
+
"16": ["shift"],
|
112
|
+
"17": ["ctrl"],
|
113
|
+
"18": ["alt", "menu"],
|
114
|
+
"19": ["pause", "break"],
|
115
|
+
"20": ["capslock"],
|
116
|
+
"27": ["escape", "esc"],
|
117
|
+
"32": ["space", "spacebar"],
|
118
|
+
"33": ["pageup"],
|
119
|
+
"34": ["pagedown"],
|
120
|
+
"35": ["end"],
|
121
|
+
"36": ["home"],
|
122
|
+
"37": ["left"],
|
123
|
+
"38": ["up"],
|
124
|
+
"39": ["right"],
|
125
|
+
"40": ["down"],
|
126
|
+
"41": ["select"],
|
127
|
+
"42": ["printscreen"],
|
128
|
+
"43": ["execute"],
|
129
|
+
"44": ["snapshot"],
|
130
|
+
"45": ["insert", "ins"],
|
131
|
+
"46": ["delete", "del"],
|
132
|
+
"47": ["help"],
|
133
|
+
"91": ["command", "windows", "win", "super", "leftcommand", "leftwindows", "leftwin", "leftsuper"],
|
134
|
+
"92": ["command", "windows", "win", "super", "rightcommand", "rightwindows", "rightwin", "rightsuper"],
|
135
|
+
"145": ["scrolllock", "scroll"],
|
136
|
+
"186": ["semicolon", ";"],
|
137
|
+
"187": ["equal", "equalsign", "="],
|
138
|
+
"188": ["comma", ","],
|
139
|
+
"189": ["dash", "-"],
|
140
|
+
"190": ["period", "."],
|
141
|
+
"191": ["slash", "forwardslash", "/"],
|
142
|
+
"192": ["graveaccent", "`"],
|
143
|
+
"219": ["openbracket", "["],
|
144
|
+
"220": ["backslash", "\\"],
|
145
|
+
"221": ["closebracket", "]"],
|
146
|
+
"222": ["apostrophe", "'"],
|
147
|
+
|
148
|
+
//0-9
|
149
|
+
"48": ["zero", "0"],
|
150
|
+
"49": ["one", "1"],
|
151
|
+
"50": ["two", "2"],
|
152
|
+
"51": ["three", "3"],
|
153
|
+
"52": ["four", "4"],
|
154
|
+
"53": ["five", "5"],
|
155
|
+
"54": ["six", "6"],
|
156
|
+
"55": ["seven", "7"],
|
157
|
+
"56": ["eight", "8"],
|
158
|
+
"57": ["nine", "9"],
|
159
|
+
|
160
|
+
//numpad
|
161
|
+
"96": ["numzero", "num0"],
|
162
|
+
"97": ["numone", "num1"],
|
163
|
+
"98": ["numtwo", "num2"],
|
164
|
+
"99": ["numthree", "num3"],
|
165
|
+
"100": ["numfour", "num4"],
|
166
|
+
"101": ["numfive", "num5"],
|
167
|
+
"102": ["numsix", "num6"],
|
168
|
+
"103": ["numseven", "num7"],
|
169
|
+
"104": ["numeight", "num8"],
|
170
|
+
"105": ["numnine", "num9"],
|
171
|
+
"106": ["nummultiply", "num*"],
|
172
|
+
"107": ["numadd", "num+"],
|
173
|
+
"108": ["numenter"],
|
174
|
+
"109": ["numsubtract", "num-"],
|
175
|
+
"110": ["numdecimal", "num."],
|
176
|
+
"111": ["numdevide", "num/"],
|
177
|
+
"144": ["numlock", "num"],
|
178
|
+
|
179
|
+
//function keys
|
180
|
+
"112": ["f1"],
|
181
|
+
"113": ["f2"],
|
182
|
+
"114": ["f3"],
|
183
|
+
"115": ["f4"],
|
184
|
+
"116": ["f5"],
|
185
|
+
"117": ["f6"],
|
186
|
+
"118": ["f7"],
|
187
|
+
"119": ["f8"],
|
188
|
+
"120": ["f9"],
|
189
|
+
"121": ["f10"],
|
190
|
+
"122": ["f11"],
|
191
|
+
"123": ["f12"]
|
192
|
+
},
|
193
|
+
"macros": [
|
194
|
+
|
195
|
+
//secondary key symbols
|
196
|
+
['shift + `', ["tilde", "~"]],
|
197
|
+
['shift + 1', ["exclamation", "exclamationpoint", "!"]],
|
198
|
+
['shift + 2', ["at", "@"]],
|
199
|
+
['shift + 3', ["number", "#"]],
|
200
|
+
['shift + 4', ["dollar", "dollars", "dollarsign", "$"]],
|
201
|
+
['shift + 5', ["percent", "%"]],
|
202
|
+
['shift + 6', ["caret", "^"]],
|
203
|
+
['shift + 7', ["ampersand", "and", "&"]],
|
204
|
+
['shift + 8', ["asterisk", "*"]],
|
205
|
+
['shift + 9', ["openparen", "("]],
|
206
|
+
['shift + 0', ["closeparen", ")"]],
|
207
|
+
['shift + -', ["underscore", "_"]],
|
208
|
+
['shift + =', ["plus", "+"]],
|
209
|
+
['shift + (', ["opencurlybrace", "opencurlybracket", "{"]],
|
210
|
+
['shift + )', ["closecurlybrace", "closecurlybracket", "}"]],
|
211
|
+
['shift + \\', ["verticalbar", "|"]],
|
212
|
+
['shift + ;', ["colon", ":"]],
|
213
|
+
['shift + \'', ["quotationmark", "\""]],
|
214
|
+
['shift + !,', ["openanglebracket", "<"]],
|
215
|
+
['shift + .', ["closeanglebracket", ">"]],
|
216
|
+
['shift + /', ["questionmark", "?"]]
|
217
|
+
]
|
218
|
+
};
|
219
|
+
//a-z and A-Z
|
220
|
+
for (aI = 65; aI <= 90; aI += 1) {
|
221
|
+
usLocale.map[aI] = String.fromCharCode(aI + 32);
|
222
|
+
usLocale.macros.push(['shift + ' + String.fromCharCode(aI + 32) + ', capslock + ' + String.fromCharCode(aI + 32), [String.fromCharCode(aI)]]);
|
223
|
+
}
|
224
|
+
registerLocale('us', usLocale);
|
225
|
+
getSetLocale('us');
|
226
|
+
|
227
|
+
|
228
|
+
//////////
|
229
|
+
// INIT //
|
230
|
+
//////////
|
231
|
+
|
232
|
+
//enable the library
|
233
|
+
enable();
|
234
|
+
|
235
|
+
|
236
|
+
/////////
|
237
|
+
// API //
|
238
|
+
/////////
|
239
|
+
|
240
|
+
//assemble the library and return it
|
241
|
+
KeyboardJS.enable = enable;
|
242
|
+
KeyboardJS.disable = disable;
|
243
|
+
KeyboardJS.activeKeys = getActiveKeys;
|
244
|
+
KeyboardJS.on = createBinding;
|
245
|
+
KeyboardJS.clear = removeBindingByKeyCombo;
|
246
|
+
KeyboardJS.clear.key = removeBindingByKeyName;
|
247
|
+
KeyboardJS.locale = getSetLocale;
|
248
|
+
KeyboardJS.locale.register = registerLocale;
|
249
|
+
KeyboardJS.macro = createMacro;
|
250
|
+
KeyboardJS.macro.remove = removeMacro;
|
251
|
+
KeyboardJS.key = {};
|
252
|
+
KeyboardJS.key.name = getKeyName;
|
253
|
+
KeyboardJS.key.code = getKeyCode;
|
254
|
+
KeyboardJS.combo = {};
|
255
|
+
KeyboardJS.combo.active = isSatisfiedCombo;
|
256
|
+
KeyboardJS.combo.parse = parseKeyCombo;
|
257
|
+
KeyboardJS.combo.stringify = stringifyKeyCombo;
|
258
|
+
return KeyboardJS;
|
259
|
+
|
260
|
+
|
261
|
+
//////////////////////
|
262
|
+
// INSTANCE METHODS //
|
263
|
+
//////////////////////
|
113
264
|
|
114
|
-
|
115
|
-
|
265
|
+
/**
|
266
|
+
* Enables KeyboardJS
|
267
|
+
*/
|
268
|
+
function enable() {
|
269
|
+
if(window.addEventListener) {
|
270
|
+
document.addEventListener('keydown', keydown, false);
|
271
|
+
document.addEventListener('keyup', keyup, false);
|
272
|
+
window.addEventListener('blur', reset, false);
|
273
|
+
} else if(window.attachEvent) {
|
274
|
+
document.attachEvent('onkeydown', keydown);
|
275
|
+
document.attachEvent('onkeyup', keyup);
|
276
|
+
window.attachEvent('onblur', reset);
|
277
|
+
}
|
278
|
+
}
|
279
|
+
|
280
|
+
/**
|
281
|
+
* Exits all active bindings and disables KeyboardJS
|
282
|
+
*/
|
283
|
+
function disable() {
|
284
|
+
reset();
|
285
|
+
if(window.removeEventListener) {
|
286
|
+
document.removeEventListener('keydown', keydown, false);
|
287
|
+
document.removeEventListener('keyup', keyup, false);
|
288
|
+
window.removeEventListener('blur', reset, false);
|
289
|
+
} else if(window.detachEvent) {
|
290
|
+
document.detachEvent('onkeydown', keydown);
|
291
|
+
document.detachEvent('onkeyup', keyup);
|
292
|
+
window.detachEvent('onblur', reset);
|
293
|
+
}
|
294
|
+
}
|
295
|
+
|
296
|
+
|
297
|
+
////////////////////
|
298
|
+
// EVENT HANDLERS //
|
299
|
+
////////////////////
|
300
|
+
|
301
|
+
/**
|
302
|
+
* Exits all active bindings. Optionally passes an event to all binding
|
303
|
+
* handlers.
|
304
|
+
* @param {KeyboardEvent} event [Optional]
|
305
|
+
*/
|
306
|
+
function reset(event) {
|
307
|
+
activeKeys = [];
|
308
|
+
pruneMacros();
|
309
|
+
pruneBindings(event);
|
310
|
+
}
|
116
311
|
|
117
|
-
|
312
|
+
/**
|
313
|
+
* Key down event handler.
|
314
|
+
* @param {KeyboardEvent} event
|
315
|
+
*/
|
316
|
+
function keydown(event) {
|
317
|
+
var keyNames, kI;
|
318
|
+
keyNames = getKeyName(event.keyCode);
|
319
|
+
if(keyNames.length < 1) { return; }
|
320
|
+
for(kI = 0; kI < keyNames.length; kI += 1) {
|
321
|
+
addActiveKey(keyNames[kI]);
|
322
|
+
}
|
323
|
+
executeMacros();
|
324
|
+
executeBindings(event);
|
325
|
+
}
|
326
|
+
|
327
|
+
/**
|
328
|
+
* Key up event handler.
|
329
|
+
* @param {KeyboardEvent} event
|
330
|
+
*/
|
331
|
+
function keyup(event) {
|
332
|
+
var keyNames, kI;
|
333
|
+
keyNames = getKeyName(event.keyCode);
|
334
|
+
if(keyNames.length < 1) { return; }
|
335
|
+
for(kI = 0; kI < keyNames.length; kI += 1) {
|
336
|
+
removeActiveKey(keyNames[kI]);
|
337
|
+
}
|
338
|
+
pruneMacros();
|
339
|
+
pruneBindings(event);
|
340
|
+
}
|
118
341
|
|
119
342
|
/**
|
120
|
-
*
|
343
|
+
* Accepts a key code and returns the key names defined by the current
|
344
|
+
* locale.
|
345
|
+
* @param {Number} keyCode
|
346
|
+
* @return {Array} keyNames An array of key names defined for the key
|
347
|
+
* code as defined by the current locale.
|
121
348
|
*/
|
122
|
-
function
|
123
|
-
|
349
|
+
function getKeyName(keyCode) {
|
350
|
+
return map[keyCode] || [];
|
351
|
+
}
|
124
352
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
353
|
+
/**
|
354
|
+
* Accepts a key name and returns the key code defined by the current
|
355
|
+
* locale.
|
356
|
+
* @param {Number} keyName
|
357
|
+
* @return {Number|false}
|
358
|
+
*/
|
359
|
+
function getKeyCode(keyName) {
|
360
|
+
var keyCode;
|
361
|
+
for(keyCode in map) {
|
362
|
+
if(!map.hasOwnProperty(keyCode)) { continue; }
|
363
|
+
if(map[keyCode].indexOf(keyName) > -1) { return keyCode; }
|
364
|
+
}
|
365
|
+
return false;
|
366
|
+
}
|
129
367
|
|
130
|
-
//loop through the key bindings of the same key length.
|
131
|
-
for(var bindingIndex = 0; bindingIndex < KeyBindingGroup.length; bindingIndex += 1) {
|
132
|
-
var binding = KeyBindingGroup[bindingIndex],
|
133
368
|
|
134
|
-
|
135
|
-
|
369
|
+
////////////
|
370
|
+
// MACROS //
|
371
|
+
////////////
|
136
372
|
|
137
|
-
|
138
|
-
|
139
|
-
|
373
|
+
/**
|
374
|
+
* Accepts a key combo and an array of key names to inject once the key
|
375
|
+
* combo is satisfied.
|
376
|
+
* @param {String} combo
|
377
|
+
* @param {Array} injectedKeys
|
378
|
+
*/
|
379
|
+
function createMacro(combo, injectedKeys) {
|
380
|
+
if(typeof combo !== 'string' && (typeof combo !== 'object' || typeof combo.push !== 'function')) {
|
381
|
+
throw new Error("Cannot create macro. The combo must be a string or array.");
|
382
|
+
}
|
383
|
+
if(typeof injectedKeys !== 'object' || typeof injectedKeys.push !== 'function') {
|
384
|
+
throw new Error("Cannot create macro. The injectedKeys must be an array.");
|
385
|
+
}
|
386
|
+
macros.push([combo, injectedKeys]);
|
387
|
+
}
|
140
388
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
389
|
+
/**
|
390
|
+
* Accepts a key combo and clears any and all macros bound to that key
|
391
|
+
* combo.
|
392
|
+
* @param {String} combo
|
393
|
+
*/
|
394
|
+
function removeMacro(combo) {
|
395
|
+
var macro;
|
396
|
+
if(typeof combo !== 'string' && (typeof combo !== 'object' || typeof combo.push !== 'function')) { throw new Error("Cannot remove macro. The combo must be a string or array."); }
|
397
|
+
for(mI = 0; mI < macros.length; mI += 1) {
|
398
|
+
macro = macros[mI];
|
399
|
+
if(compareCombos(combo, macro[0])) {
|
400
|
+
removeActiveKey(macro[1]);
|
401
|
+
macros.splice(mI, 1);
|
402
|
+
break;
|
403
|
+
}
|
404
|
+
}
|
405
|
+
}
|
146
406
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
407
|
+
/**
|
408
|
+
* Executes macros against the active keys. Each macro's key combo is
|
409
|
+
* checked and if found to be satisfied, the macro's key names are injected
|
410
|
+
* into active keys.
|
411
|
+
*/
|
412
|
+
function executeMacros() {
|
413
|
+
var mI, combo, kI;
|
414
|
+
for(mI = 0; mI < macros.length; mI += 1) {
|
415
|
+
combo = parseKeyCombo(macros[mI][0]);
|
416
|
+
if(activeMacros.indexOf(macros[mI]) === -1 && isSatisfiedCombo(combo)) {
|
417
|
+
activeMacros.push(macros[mI]);
|
418
|
+
for(kI = 0; kI < macros[mI][1].length; kI += 1) {
|
419
|
+
addActiveKey(macros[mI][1][kI]);
|
151
420
|
}
|
152
421
|
}
|
153
422
|
}
|
423
|
+
}
|
154
424
|
|
155
|
-
|
425
|
+
/**
|
426
|
+
* Prunes active macros. Checks each active macro's key combo and if found
|
427
|
+
* to no longer to be satisfied, each of the macro's key names are removed
|
428
|
+
* from active keys.
|
429
|
+
*/
|
430
|
+
function pruneMacros() {
|
431
|
+
var mI, combo, kI;
|
432
|
+
for(mI = 0; mI < activeMacros.length; mI += 1) {
|
433
|
+
combo = parseKeyCombo(activeMacros[mI][0]);
|
434
|
+
if(isSatisfiedCombo(combo) === false) {
|
435
|
+
for(kI = 0; kI < activeMacros[mI][1].length; kI += 1) {
|
436
|
+
removeActiveKey(activeMacros[mI][1][kI]);
|
437
|
+
}
|
438
|
+
activeMacros.splice(mI, 1);
|
439
|
+
mI -= 1;
|
440
|
+
}
|
441
|
+
}
|
156
442
|
}
|
157
443
|
|
444
|
+
|
445
|
+
//////////////
|
446
|
+
// BINDINGS //
|
447
|
+
//////////////
|
448
|
+
|
158
449
|
/**
|
159
|
-
*
|
160
|
-
*
|
450
|
+
* Creates a binding object, and, if provided, binds a key down hander and
|
451
|
+
* a key up handler. Returns a binding object that emits keyup and
|
452
|
+
* keydown events.
|
453
|
+
* @param {String} keyCombo
|
454
|
+
* @param {Function} keyDownCallback [Optional]
|
455
|
+
* @param {Function} keyUpCallback [Optional]
|
456
|
+
* @return {Object} binding
|
161
457
|
*/
|
162
|
-
function
|
458
|
+
function createBinding(keyCombo, keyDownCallback, keyUpCallback) {
|
459
|
+
var api = {}, binding, subBindings = [], bindingApi = {}, kI,
|
460
|
+
subCombo;
|
163
461
|
|
164
|
-
|
165
|
-
|
462
|
+
//break the combo down into a combo array
|
463
|
+
if(typeof keyCombo === 'string') {
|
464
|
+
keyCombo = parseKeyCombo(keyCombo);
|
166
465
|
}
|
167
466
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
//loop through each active binding
|
173
|
-
for (var bindingIndex = 0; bindingIndex < bindingStack.length; bindingIndex += 1) {
|
174
|
-
var binding = bindingStack[bindingIndex],
|
175
|
-
usesSpentKey = false;
|
176
|
-
|
177
|
-
//check each of the required keys. Make sure they have not been used by another binding
|
178
|
-
for(var keyIndex = 0; keyIndex < binding.keys.length; keyIndex += 1) {
|
179
|
-
var key = binding.keys[keyIndex];
|
180
|
-
if(spentKeys.indexOf(key) > -1) {
|
181
|
-
usesSpentKey = true;
|
182
|
-
break;
|
183
|
-
}
|
184
|
-
}
|
467
|
+
//bind each sub combo contained within the combo string
|
468
|
+
for(kI = 0; kI < keyCombo.length; kI += 1) {
|
469
|
+
binding = {};
|
185
470
|
|
186
|
-
//
|
187
|
-
|
471
|
+
//stringify the combo again
|
472
|
+
subCombo = stringifyKeyCombo([keyCombo[kI]]);
|
188
473
|
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
474
|
+
//validate the sub combo
|
475
|
+
if(typeof subCombo !== 'string') { throw new Error('Failed to bind key combo. The key combo must be string.'); }
|
476
|
+
|
477
|
+
//create the binding
|
478
|
+
binding.keyCombo = subCombo;
|
479
|
+
binding.keyDownCallback = [];
|
480
|
+
binding.keyUpCallback = [];
|
481
|
+
|
482
|
+
//inject the key down and key up callbacks if given
|
483
|
+
if(keyDownCallback) { binding.keyDownCallback.push(keyDownCallback); }
|
484
|
+
if(keyUpCallback) { binding.keyUpCallback.push(keyUpCallback); }
|
195
485
|
|
196
|
-
|
197
|
-
|
198
|
-
|
486
|
+
//stash the new binding
|
487
|
+
bindings.push(binding);
|
488
|
+
subBindings.push(binding);
|
489
|
+
}
|
490
|
+
|
491
|
+
//build the binding api
|
492
|
+
api.clear = clear;
|
493
|
+
api.on = on;
|
494
|
+
return api;
|
495
|
+
|
496
|
+
/**
|
497
|
+
* Clears the binding
|
498
|
+
*/
|
499
|
+
function clear() {
|
500
|
+
var bI;
|
501
|
+
for(bI = 0; bI < subBindings.length; bI += 1) {
|
502
|
+
bindings.splice(bindings.indexOf(subBindings[bI]), 1);
|
503
|
+
}
|
504
|
+
}
|
505
|
+
|
506
|
+
/**
|
507
|
+
* Accepts an event name. and any number of callbacks. When the event is
|
508
|
+
* emitted, all callbacks are executed. Available events are key up and
|
509
|
+
* key down.
|
510
|
+
* @param {String} eventName
|
511
|
+
* @return {Object} subBinding
|
512
|
+
*/
|
513
|
+
function on(eventName ) {
|
514
|
+
var api = {}, callbacks, cI, bI;
|
515
|
+
|
516
|
+
//validate event name
|
517
|
+
if(typeof eventName !== 'string') { throw new Error('Cannot bind callback. The event name must be a string.'); }
|
518
|
+
if(eventName !== 'keyup' && eventName !== 'keydown') { throw new Error('Cannot bind callback. The event name must be a "keyup" or "keydown".'); }
|
519
|
+
|
520
|
+
//gather the callbacks
|
521
|
+
callbacks = Array.prototype.slice.apply(arguments, [1]);
|
522
|
+
|
523
|
+
//stash each the new binding
|
524
|
+
for(cI = 0; cI < callbacks.length; cI += 1) {
|
525
|
+
if(typeof callbacks[cI] === 'function') {
|
526
|
+
if(eventName === 'keyup') {
|
527
|
+
for(bI = 0; bI < subBindings.length; bI += 1) {
|
528
|
+
subBindings[bI].keyUpCallback.push(callbacks[cI]);
|
529
|
+
}
|
530
|
+
} else if(eventName === 'keydown') {
|
531
|
+
for(bI = 0; bI < subBindings.length; bI += 1) {
|
532
|
+
subBindings[bI].keyDownCallback.push(callbacks[cI]);
|
533
|
+
}
|
534
|
+
}
|
199
535
|
}
|
536
|
+
}
|
200
537
|
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
538
|
+
//construct and return the sub binding api
|
539
|
+
api.clear = clear;
|
540
|
+
return api;
|
541
|
+
|
542
|
+
/**
|
543
|
+
* Clears the binding
|
544
|
+
*/
|
545
|
+
function clear() {
|
546
|
+
var cI, bI;
|
547
|
+
for(cI = 0; cI < callbacks.length; cI += 1) {
|
548
|
+
if(typeof callbacks[cI] === 'function') {
|
549
|
+
if(eventName === 'keyup') {
|
550
|
+
for(bI = 0; bI < subBindings.length; bI += 1) {
|
551
|
+
subBindings[bI].keyUpCallback.splice(subBindings[bI].keyUpCallback.indexOf(callbacks[cI]), 1);
|
552
|
+
}
|
553
|
+
} else {
|
554
|
+
for(bI = 0; bI < subBindings.length; bI += 1) {
|
555
|
+
subBindings[bI].keyDownCallback.splice(subBindings[bI].keyDownCallback.indexOf(callbacks[cI]), 1);
|
556
|
+
}
|
557
|
+
}
|
206
558
|
}
|
207
559
|
}
|
208
560
|
}
|
209
561
|
}
|
562
|
+
}
|
210
563
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
564
|
+
/**
|
565
|
+
* Clears all binding attached to a given key combo. Key name order does not
|
566
|
+
* matter as long as the key combos equate.
|
567
|
+
* @param {String} keyCombo
|
568
|
+
*/
|
569
|
+
function removeBindingByKeyCombo(keyCombo) {
|
570
|
+
var bI, binding, keyName;
|
571
|
+
for(bI = 0; bI < bindings.length; bI += 1) {
|
572
|
+
binding = bindings[bI];
|
573
|
+
if(compareCombos(keyCombo, binding.keyCombo)) {
|
574
|
+
bindings.splice(bI, 1); bI -= 1;
|
575
|
+
}
|
215
576
|
}
|
216
|
-
|
217
|
-
return output;
|
218
577
|
}
|
219
578
|
|
220
579
|
/**
|
221
|
-
*
|
222
|
-
* @param
|
580
|
+
* Clears all binding attached to key combos containing a given key name.
|
581
|
+
* @param {String} keyName
|
223
582
|
*/
|
224
|
-
function
|
225
|
-
var
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
active = false;
|
233
|
-
|
234
|
-
//loop thorugh the active bindings
|
235
|
-
for(var bindingIndex = 0; bindingIndex < bindingStack.length; bindingIndex += 1) {
|
236
|
-
var activeCombo = bindingStack[bindingIndex].keyCombo;
|
237
|
-
|
238
|
-
//check to see if the combo is still active
|
239
|
-
if(activeCombo === bindingCombo) {
|
240
|
-
active = true;
|
583
|
+
function removeBindingByKeyName(keyName) {
|
584
|
+
var bI, cI, binding;
|
585
|
+
if(keyName) {
|
586
|
+
for(bI = 0; bI < bindings.length; bI += 1) {
|
587
|
+
binding = bindings[bI];
|
588
|
+
for(cI = 0; cI < binding.keyCombo.length; cI += 1) {
|
589
|
+
if(binding.keyCombo[kI].indexOf(keyName) > -1) {
|
590
|
+
bindings.splice(bI, 1); bI -= 1;
|
241
591
|
break;
|
242
592
|
}
|
243
593
|
}
|
594
|
+
}
|
595
|
+
} else {
|
596
|
+
bindings = [];
|
597
|
+
}
|
598
|
+
}
|
244
599
|
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
600
|
+
/**
|
601
|
+
* Executes bindings that are active. Only allows the keys to be used once
|
602
|
+
* as to prevent binding overlap.
|
603
|
+
* @param {KeyboardEvent} event The keyboard event.
|
604
|
+
*/
|
605
|
+
function executeBindings(event) {
|
606
|
+
var bI, sBI, binding, bidningKeys, remainingKeys, cI, killEventBubble, kI, bindingKeysSatisfied,
|
607
|
+
index, sortedBindings = [], bindingWeight;
|
608
|
+
|
609
|
+
remainingKeys = [].concat(activeKeys);
|
610
|
+
for(bI = 0; bI < bindings.length; bI += 1) {
|
611
|
+
bindingWeight = extractComboKeys(bindings[bI].keyCombo).length;
|
612
|
+
if(!sortedBindings[bindingWeight]) { sortedBindings[bindingWeight] = []; }
|
613
|
+
sortedBindings[bindingWeight].push(bindings[bI]);
|
614
|
+
}
|
615
|
+
for(sBI = sortedBindings.length - 1; sBI >= 0; sBI -= 1) {
|
616
|
+
if(!sortedBindings[sBI]) { continue; }
|
617
|
+
for(bI = 0; bI < sortedBindings[sBI].length; bI += 1) {
|
618
|
+
binding = sortedBindings[sBI][bI];
|
619
|
+
bindingKeys = extractComboKeys(binding.keyCombo);
|
620
|
+
bindingKeysSatisfied = true;
|
621
|
+
for(kI = 0; kI < bindingKeys.length; kI += 1) {
|
622
|
+
if(remainingKeys.indexOf(bindingKeys[kI]) === -1) {
|
623
|
+
bindingKeysSatisfied = false;
|
624
|
+
break;
|
625
|
+
}
|
626
|
+
}
|
627
|
+
if(bindingKeysSatisfied && isSatisfiedCombo(binding.keyCombo)) {
|
628
|
+
activeBindings.push(binding);
|
629
|
+
for(kI = 0; kI < bindingKeys.length; kI += 1) {
|
630
|
+
index = remainingKeys.indexOf(bindingKeys[kI]);
|
631
|
+
if(index > -1) {
|
632
|
+
remainingKeys.splice(index, 1);
|
633
|
+
kI -= 1;
|
251
634
|
}
|
252
635
|
}
|
253
|
-
|
254
|
-
|
636
|
+
for(cI = 0; cI < binding.keyDownCallback.length; cI += 1) {
|
637
|
+
if (binding.keyDownCallback[cI](event, getActiveKeys(), binding.keyCombo) === false) {
|
638
|
+
killEventBubble = true;
|
639
|
+
}
|
640
|
+
}
|
641
|
+
if(killEventBubble === true) {
|
642
|
+
event.preventDefault();
|
643
|
+
event.stopPropagation();
|
644
|
+
}
|
255
645
|
}
|
256
646
|
}
|
257
647
|
}
|
258
|
-
|
259
|
-
return output;
|
260
648
|
}
|
261
649
|
|
262
650
|
/**
|
263
|
-
*
|
264
|
-
*
|
265
|
-
*
|
266
|
-
* @param keyCombo
|
267
|
-
* @param callback
|
268
|
-
* @param endCallback
|
651
|
+
* Removes bindings that are no longer satisfied by the active keys. Also
|
652
|
+
* fires the key up callbacks.
|
653
|
+
* @param {KeyboardEvent} event
|
269
654
|
*/
|
270
|
-
function
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
655
|
+
function pruneBindings(event) {
|
656
|
+
var bI, cI, binding, killEventBubble;
|
657
|
+
for(bI = 0; bI < activeBindings.length; bI += 1) {
|
658
|
+
binding = activeBindings[bI];
|
659
|
+
if(isSatisfiedCombo(binding.keyCombo) === false) {
|
660
|
+
for(cI = 0; cI < binding.keyUpCallback.length; cI += 1) {
|
661
|
+
if (binding.keyUpCallback[cI](event, getActiveKeys(), binding.keyCombo) === false) {
|
662
|
+
killEventBubble = true;
|
663
|
+
}
|
664
|
+
}
|
665
|
+
if(killEventBubble === true) {
|
666
|
+
event.preventDefault();
|
667
|
+
event.stopPropagation();
|
279
668
|
}
|
669
|
+
activeBindings.splice(bI, 1);
|
670
|
+
bI -= 1;
|
280
671
|
}
|
281
672
|
}
|
673
|
+
}
|
282
674
|
|
283
|
-
//create an array of combos from the first argument
|
284
|
-
var bindSets = keyCombo.toLowerCase().replace(/\s/g, '').split(',');
|
285
|
-
|
286
|
-
//create a binding for each key combo
|
287
|
-
for(var i = 0; i < bindSets.length; i += 1) {
|
288
|
-
|
289
|
-
//split up the keys
|
290
|
-
var keys = bindSets[i].split('+');
|
291
|
-
|
292
|
-
//if there are keys in the current combo
|
293
|
-
if(keys.length) {
|
294
|
-
if(!keyBindingGroups[keys.length]) { keyBindingGroups[keys.length] = []; }
|
295
675
|
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
"endCallback": endCallback,
|
300
|
-
"keyCombo": bindSets[i],
|
301
|
-
"keys": keys
|
302
|
-
};
|
676
|
+
///////////////////
|
677
|
+
// COMBO STRINGS //
|
678
|
+
///////////////////
|
303
679
|
|
304
|
-
|
305
|
-
|
680
|
+
/**
|
681
|
+
* Compares two key combos returning true when they are functionally
|
682
|
+
* equivalent.
|
683
|
+
* @param {String} keyComboArrayA keyCombo A key combo string or array.
|
684
|
+
* @param {String} keyComboArrayB keyCombo A key combo string or array.
|
685
|
+
* @return {Boolean}
|
686
|
+
*/
|
687
|
+
function compareCombos(keyComboArrayA, keyComboArrayB) {
|
688
|
+
var cI, sI, kI;
|
689
|
+
keyComboArrayA = parseKeyCombo(keyComboArrayA);
|
690
|
+
keyComboArrayB = parseKeyCombo(keyComboArrayB);
|
691
|
+
if(keyComboArrayA.length !== keyComboArrayB.length) { return false; }
|
692
|
+
for(cI = 0; cI < keyComboArrayA.length; cI += 1) {
|
693
|
+
if(keyComboArrayA[aI].length !== keyComboArrayB[aI].length) { return false; }
|
694
|
+
for(sI = 0; sI < keyComboArrayA[aI].length; sI += 1) {
|
695
|
+
if(keyComboArrayA[aI][sI].length !== keyComboArrayB[aI][sI].length) { return false; }
|
696
|
+
for(kI = 0; kI < keyComboArrayA[aI][sI].length; kI += 1) {
|
697
|
+
if(keyComboArrayB[aI][sI].indexOf(keyComboArrayA[aI][sI][kI]) === -1) { return false; }
|
698
|
+
}
|
306
699
|
}
|
307
700
|
}
|
308
|
-
|
309
|
-
return {
|
310
|
-
"clear": clear
|
311
|
-
}
|
701
|
+
return true;
|
312
702
|
}
|
313
703
|
|
314
704
|
/**
|
315
|
-
*
|
316
|
-
*
|
317
|
-
*
|
318
|
-
*
|
319
|
-
* @param up
|
320
|
-
* @param down
|
321
|
-
* @param left
|
322
|
-
* @param right
|
323
|
-
* @param callback
|
705
|
+
* Checks to see if a key combo string or key array is satisfied by the
|
706
|
+
* currently active keys. It does not take into account spent keys.
|
707
|
+
* @param {String} keyCombo A key combo string or array.
|
708
|
+
* @return {Boolean}
|
324
709
|
*/
|
325
|
-
function
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
710
|
+
function isSatisfiedCombo(keyCombo) {
|
711
|
+
var cI, sI, stage, kI, stageOffset = 0, index, comboMatches;
|
712
|
+
keyCombo = parseKeyCombo(keyCombo);
|
713
|
+
for(cI = 0; cI < keyCombo.length; cI += 1) {
|
714
|
+
comboMatches = true;
|
715
|
+
stageOffset = 0;
|
716
|
+
for(sI = 0; sI < keyCombo[cI].length; sI += 1) {
|
717
|
+
stage = [].concat(keyCombo[cI][sI]);
|
718
|
+
for(kI = stageOffset; kI < activeKeys.length; kI += 1) {
|
719
|
+
index = stage.indexOf(activeKeys[kI]);
|
720
|
+
if(index > -1) {
|
721
|
+
stage.splice(index, 1);
|
722
|
+
stageOffset = kI;
|
723
|
+
}
|
724
|
+
}
|
725
|
+
if(stage.length !== 0) { comboMatches = false; break; }
|
726
|
+
}
|
727
|
+
if(comboMatches) { return true; }
|
333
728
|
}
|
729
|
+
return false;
|
730
|
+
}
|
334
731
|
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
732
|
+
/**
|
733
|
+
* Accepts a key combo array or string and returns a flat array containing all keys referenced by
|
734
|
+
* the key combo.
|
735
|
+
* @param {String} keyCombo A key combo string or array.
|
736
|
+
* @return {Array}
|
737
|
+
*/
|
738
|
+
function extractComboKeys(keyCombo) {
|
739
|
+
var cI, sI, kI, keys = [];
|
740
|
+
keyCombo = parseKeyCombo(keyCombo);
|
741
|
+
for(cI = 0; cI < keyCombo.length; cI += 1) {
|
742
|
+
for(sI = 0; sI < keyCombo[cI].length; sI += 1) {
|
743
|
+
keys = keys.concat(keyCombo[cI][sI]);
|
744
|
+
}
|
339
745
|
}
|
746
|
+
return keys;
|
747
|
+
}
|
340
748
|
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
var
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
}
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
749
|
+
/**
|
750
|
+
* Parses a key combo string into a 3 dimensional array.
|
751
|
+
* - Level 1 - sub combos.
|
752
|
+
* - Level 2 - combo stages. A stage is a set of key name pairs that must
|
753
|
+
* be satisfied in the order they are defined.
|
754
|
+
* - Level 3 - each key name to the stage.
|
755
|
+
* @param {String|Array} keyCombo A key combo string.
|
756
|
+
* @return {Array}
|
757
|
+
*/
|
758
|
+
function parseKeyCombo(keyCombo) {
|
759
|
+
var s = keyCombo, i = 0, op = 0, ws = false, nc = false, combos = [], combo = [], stage = [], key = '';
|
760
|
+
|
761
|
+
if(typeof keyCombo === 'object' && typeof keyCombo.push === 'function') { return keyCombo; }
|
762
|
+
if(typeof keyCombo !== 'string') { throw new Error('Cannot parse "keyCombo" because its type is "' + (typeof keyCombo) + '". It must be a "string".'); }
|
763
|
+
|
764
|
+
//remove leading whitespace
|
765
|
+
while(s.charAt(i) === ' ') { i += 1; }
|
766
|
+
while(true) {
|
767
|
+
if(s.charAt(i) === ' ') {
|
768
|
+
//white space & next combo op
|
769
|
+
while(s.charAt(i) === ' ') { i += 1; }
|
770
|
+
ws = true;
|
771
|
+
} else if(s.charAt(i) === ',') {
|
772
|
+
if(op || nc) { throw new Error('Failed to parse key combo. Unexpected , at character index ' + i + '.'); }
|
773
|
+
nc = true;
|
774
|
+
i += 1;
|
775
|
+
} else if(s.charAt(i) === '+') {
|
776
|
+
//next key
|
777
|
+
if(key.length) { stage.push(key); key = ''; }
|
778
|
+
if(op || nc) { throw new Error('Failed to parse key combo. Unexpected + at character index ' + i + '.'); }
|
779
|
+
op = true;
|
780
|
+
i += 1;
|
781
|
+
} else if(s.charAt(i) === '>') {
|
782
|
+
//next stage op
|
783
|
+
if(key.length) { stage.push(key); key = ''; }
|
784
|
+
if(stage.length) { combo.push(stage); stage = []; }
|
785
|
+
if(op || nc) { throw new Error('Failed to parse key combo. Unexpected > at character index ' + i + '.'); }
|
786
|
+
op = true;
|
787
|
+
i += 1;
|
788
|
+
} else if(i < s.length - 1 && s.charAt(i) === '!' && (s.charAt(i + 1) === '>' || s.charAt(i + 1) === ',' || s.charAt(i + 1) === '+')) {
|
789
|
+
key += s.charAt(i + 1);
|
790
|
+
op = false;
|
791
|
+
ws = false;
|
792
|
+
nc = false;
|
793
|
+
i += 2;
|
794
|
+
} else if(i < s.length && s.charAt(i) !== '+' && s.charAt(i) !== '>' && s.charAt(i) !== ',' && s.charAt(i) !== ' ') {
|
795
|
+
//end combo
|
796
|
+
if(op === false && ws === true || nc === true) {
|
797
|
+
if(key.length) { stage.push(key); key = ''; }
|
798
|
+
if(stage.length) { combo.push(stage); stage = []; }
|
799
|
+
if(combo.length) { combos.push(combo); combo = []; }
|
800
|
+
}
|
801
|
+
op = false;
|
802
|
+
ws = false;
|
803
|
+
nc = false;
|
804
|
+
//key
|
805
|
+
while(i < s.length && s.charAt(i) !== '+' && s.charAt(i) !== '>' && s.charAt(i) !== ',' && s.charAt(i) !== ' ') {
|
806
|
+
key += s.charAt(i);
|
807
|
+
i += 1;
|
808
|
+
}
|
809
|
+
} else {
|
810
|
+
//unknown char
|
811
|
+
i += 1;
|
812
|
+
continue;
|
372
813
|
}
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
//NO CHANGE
|
380
|
-
if(axis[0] === 0 && axis[1] === 0) {
|
381
|
-
return;
|
814
|
+
//end of combos string
|
815
|
+
if(i >= s.length) {
|
816
|
+
if(key.length) { stage.push(key); key = ''; }
|
817
|
+
if(stage.length) { combo.push(stage); stage = []; }
|
818
|
+
if(combo.length) { combos.push(combo); combo = []; }
|
819
|
+
break;
|
382
820
|
}
|
383
|
-
|
384
|
-
//run the callback
|
385
|
-
callback(axis);
|
386
|
-
|
387
|
-
}, 1);
|
388
|
-
|
389
|
-
return {
|
390
|
-
"clear": clear
|
391
821
|
}
|
822
|
+
return combos;
|
392
823
|
}
|
393
824
|
|
394
825
|
/**
|
395
|
-
*
|
396
|
-
* @param
|
826
|
+
* Stringifys a key combo.
|
827
|
+
* @param {Array|String} keyComboArray A key combo array. If a key
|
828
|
+
* combo string is given it will be returned.
|
829
|
+
* @return {String}
|
397
830
|
*/
|
398
|
-
function
|
399
|
-
|
400
|
-
if(
|
401
|
-
|
402
|
-
|
831
|
+
function stringifyKeyCombo(keyComboArray) {
|
832
|
+
var cI, ccI, output = [];
|
833
|
+
if(typeof keyComboArray === 'string') { return keyComboArray; }
|
834
|
+
if(typeof keyComboArray !== 'object' || typeof keyComboArray.push !== 'function') { throw new Error('Cannot stringify key combo.'); }
|
835
|
+
for(cI = 0; cI < keyComboArray.length; cI += 1) {
|
836
|
+
output[cI] = [];
|
837
|
+
for(ccI = 0; ccI < keyComboArray[cI].length; ccI += 1) {
|
838
|
+
output[cI][ccI] = keyComboArray[cI][ccI].join(' + ');
|
839
|
+
}
|
840
|
+
output[cI] = output[cI].join(' > ');
|
403
841
|
}
|
842
|
+
return output.join(' ');
|
843
|
+
}
|
404
844
|
|
405
|
-
keys = keys.replace(/\s/g, '').split(',');
|
406
|
-
|
407
|
-
//loop through the key binding groups.
|
408
|
-
for(var iKCL = keyBindingGroups.length; iKCL > -1; iKCL -= 1) {
|
409
|
-
if(keyBindingGroups[iKCL]) {
|
410
|
-
var KeyBindingGroup = keyBindingGroups[iKCL];
|
411
|
-
|
412
|
-
//loop through the key bindings.
|
413
|
-
for(var iB = 0; iB < KeyBindingGroup.length; iB += 1) {
|
414
|
-
var keyBinding = KeyBindingGroup[iB],
|
415
|
-
remove = false;
|
416
845
|
|
417
|
-
|
418
|
-
|
419
|
-
|
846
|
+
/////////////////
|
847
|
+
// ACTIVE KEYS //
|
848
|
+
/////////////////
|
420
849
|
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
}
|
428
|
-
}
|
429
|
-
if(remove) { break; }
|
430
|
-
}
|
431
|
-
if(remove) {
|
432
|
-
keyBindingGroups[iKCL].splice(iB, 1); iB -= 1;
|
433
|
-
if(keyBindingGroups[iKCL].length < 1) {
|
434
|
-
delete keyBindingGroups[iKCL];
|
435
|
-
}
|
436
|
-
}
|
437
|
-
}
|
438
|
-
}
|
439
|
-
}
|
850
|
+
/**
|
851
|
+
* Returns the a copy of the active keys array.
|
852
|
+
* @return {Array}
|
853
|
+
*/
|
854
|
+
function getActiveKeys() {
|
855
|
+
return [].concat(activeKeys);
|
440
856
|
}
|
441
857
|
|
442
858
|
/**
|
443
|
-
*
|
859
|
+
* Adds a key to the active keys array, but only if it has not already been
|
860
|
+
* added.
|
861
|
+
* @param {String} keyName The key name string.
|
444
862
|
*/
|
445
|
-
function
|
446
|
-
|
863
|
+
function addActiveKey(keyName) {
|
864
|
+
if(keyName.match(/\s/)) { throw new Error('Cannot add key name ' + keyName + ' to active keys because it contains whitespace.'); }
|
865
|
+
if(activeKeys.indexOf(keyName) > -1) { return; }
|
866
|
+
activeKeys.push(keyName);
|
447
867
|
}
|
448
868
|
|
449
869
|
/**
|
450
|
-
*
|
451
|
-
* @param
|
452
|
-
* @param keys
|
870
|
+
* Removes a key from the active keys array.
|
871
|
+
* @param {String} keyNames The key name string.
|
453
872
|
*/
|
454
|
-
function
|
455
|
-
|
873
|
+
function removeActiveKey(keyName) {
|
874
|
+
var keyCode = getKeyCode(keyName);
|
875
|
+
if(keyCode === '91' || keyCode === '92') { activeKeys = []; } //remove all key on release of super.
|
876
|
+
else { activeKeys.splice(activeKeys.indexOf(keyName), 1); }
|
456
877
|
}
|
457
878
|
|
879
|
+
|
880
|
+
/////////////
|
881
|
+
// LOCALES //
|
882
|
+
/////////////
|
883
|
+
|
458
884
|
/**
|
459
|
-
*
|
460
|
-
*
|
885
|
+
* Registers a new locale. This is useful if you would like to add support for a new keyboard layout. It could also be useful for
|
886
|
+
* alternative key names. For example if you program games you could create a locale for your key mappings. Instead of key 65 mapped
|
887
|
+
* to 'a' you could map it to 'jump'.
|
888
|
+
* @param {String} localeName The name of the new locale.
|
889
|
+
* @param {Object} localeMap The locale map.
|
461
890
|
*/
|
462
|
-
function
|
463
|
-
|
464
|
-
|
465
|
-
}
|
891
|
+
function registerLocale(localeName, localeMap) {
|
892
|
+
|
893
|
+
//validate arguments
|
894
|
+
if(typeof localeName !== 'string') { throw new Error('Cannot register new locale. The locale name must be a string.'); }
|
895
|
+
if(typeof localeMap !== 'object') { throw new Error('Cannot register ' + localeName + ' locale. The locale map must be an object.'); }
|
896
|
+
if(typeof localeMap.map !== 'object') { throw new Error('Cannot register ' + localeName + ' locale. The locale map is invalid.'); }
|
466
897
|
|
898
|
+
//stash the locale
|
899
|
+
if(!localeMap.macros) { localeMap.macros = []; }
|
900
|
+
locales[localeName] = localeMap;
|
467
901
|
}
|
468
902
|
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
903
|
+
/**
|
904
|
+
* Swaps the current locale.
|
905
|
+
* @param {String} localeName The locale to activate.
|
906
|
+
* @return {Object}
|
907
|
+
*/
|
908
|
+
function getSetLocale(localeName) {
|
909
|
+
|
910
|
+
//if a new locale is given then set it
|
911
|
+
if(localeName) {
|
912
|
+
if(typeof localeName !== 'string') { throw new Error('Cannot set locale. The locale name must be a string.'); }
|
913
|
+
if(!locales[localeName]) { throw new Error('Cannot set locale to ' + localeName + ' because it does not exist. If you would like to submit a ' + localeName + ' locale map for KeyboardJS please submit it at https://github.com/RobertWHurst/KeyboardJS/issues.'); }
|
914
|
+
|
915
|
+
//set the current map and macros
|
916
|
+
map = locales[localeName].map;
|
917
|
+
macros = locales[localeName].macros;
|
918
|
+
|
919
|
+
//set the current locale
|
920
|
+
locale = localeName;
|
481
921
|
}
|
922
|
+
|
923
|
+
//return the current locale
|
924
|
+
return locale;
|
482
925
|
}
|
483
|
-
})
|
926
|
+
});
|