keyboardjs-rails 0.2.2 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
});
|