parade 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +21 -0
- data/README.md +542 -0
- data/Rakefile +15 -0
- data/bin/parade +138 -0
- data/lib/parade.rb +43 -0
- data/lib/parade/commands/commands.rb +84 -0
- data/lib/parade/commands/generate_outline.rb +34 -0
- data/lib/parade/commands/generate_presentation.rb +34 -0
- data/lib/parade/commands/generate_rackup.rb +32 -0
- data/lib/parade/commands/html_output.rb +47 -0
- data/lib/parade/commands/render_from_template.rb +50 -0
- data/lib/parade/commands/static_html.rb +38 -0
- data/lib/parade/commands/static_pdf.rb +39 -0
- data/lib/parade/commands/unknown.rb +23 -0
- data/lib/parade/features/live_ruby.rb +18 -0
- data/lib/parade/features/pdf_presentation.rb +24 -0
- data/lib/parade/features/preshow.rb +11 -0
- data/lib/parade/helpers/encode_image.rb +24 -0
- data/lib/parade/helpers/template_generator.rb +130 -0
- data/lib/parade/metadata.rb +73 -0
- data/lib/parade/metadata/assignment.rb +38 -0
- data/lib/parade/metadata/css_classes.rb +22 -0
- data/lib/parade/metadata/html_id.rb +35 -0
- data/lib/parade/metadata/template.rb +31 -0
- data/lib/parade/parsers/dsl.rb +138 -0
- data/lib/parade/parsers/dsl_file_parser.rb +17 -0
- data/lib/parade/parsers/json_file_parser.rb +67 -0
- data/lib/parade/parsers/markdown_image_paths.rb +44 -0
- data/lib/parade/parsers/markdown_slide_splitter.rb +63 -0
- data/lib/parade/parsers/presentation_directory_parser.rb +36 -0
- data/lib/parade/parsers/presentation_file_parser.rb +27 -0
- data/lib/parade/parsers/presentation_filepath_parser.rb +35 -0
- data/lib/parade/parsers/slides_file_content_parser.rb +27 -0
- data/lib/parade/renderers/columns_renderer.rb +68 -0
- data/lib/parade/renderers/command_line_renderer.rb +142 -0
- data/lib/parade/renderers/html_with_pygments.rb +42 -0
- data/lib/parade/renderers/inline_images.rb +31 -0
- data/lib/parade/renderers/special_paragraph_renderer.rb +23 -0
- data/lib/parade/renderers/update_image_paths.rb +75 -0
- data/lib/parade/section.rb +183 -0
- data/lib/parade/server.rb +139 -0
- data/lib/parade/slide.rb +128 -0
- data/lib/parade/version.rb +3 -0
- data/lib/public/css/960.css +653 -0
- data/lib/public/css/fg.menu.css +114 -0
- data/lib/public/css/ghf_marked.css +180 -0
- data/lib/public/css/jquery-terminal.css +73 -0
- data/lib/public/css/onepage.css +62 -0
- data/lib/public/css/parade.css +450 -0
- data/lib/public/css/pdf.css +13 -0
- data/lib/public/css/reset.css +53 -0
- data/lib/public/css/spinner_bar.gif +0 -0
- data/lib/public/css/theme/images/ui-bg_diagonals-small_100_f0efea_40x40.png +0 -0
- data/lib/public/css/theme/images/ui-bg_flat_35_f0f0f0_40x100.png +0 -0
- data/lib/public/css/theme/images/ui-bg_glass_55_fcf0ba_1x400.png +0 -0
- data/lib/public/css/theme/images/ui-bg_glow-ball_25_2e2e28_600x600.png +0 -0
- data/lib/public/css/theme/images/ui-bg_highlight-soft_100_f0efea_1x100.png +0 -0
- data/lib/public/css/theme/images/ui-bg_highlight-soft_25_327E04_1x100.png +0 -0
- data/lib/public/css/theme/images/ui-bg_highlight-soft_25_5A9D1A_1x100.png +0 -0
- data/lib/public/css/theme/images/ui-bg_highlight-soft_95_ffedad_1x100.png +0 -0
- data/lib/public/css/theme/images/ui-bg_inset-soft_22_3b3b35_1x100.png +0 -0
- data/lib/public/css/theme/images/ui-icons_808080_256x240.png +0 -0
- data/lib/public/css/theme/images/ui-icons_8DC262_256x240.png +0 -0
- data/lib/public/css/theme/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/lib/public/css/theme/images/ui-icons_e7e6e4_256x240.png +0 -0
- data/lib/public/css/theme/images/ui-icons_eeeeee_256x240.png +0 -0
- data/lib/public/css/theme/images/ui-icons_ffffff_256x240.png +0 -0
- data/lib/public/css/theme/ui.accordion.css +9 -0
- data/lib/public/css/theme/ui.all.css +2 -0
- data/lib/public/css/theme/ui.base.css +9 -0
- data/lib/public/css/theme/ui.core.css +37 -0
- data/lib/public/css/theme/ui.datepicker.css +62 -0
- data/lib/public/css/theme/ui.dialog.css +13 -0
- data/lib/public/css/theme/ui.progressbar.css +4 -0
- data/lib/public/css/theme/ui.resizable.css +13 -0
- data/lib/public/css/theme/ui.slider.css +17 -0
- data/lib/public/css/theme/ui.tabs.css +9 -0
- data/lib/public/css/theme/ui.theme.css +245 -0
- data/lib/public/favicon.ico +0 -0
- data/lib/public/js/coffee-script.js +8 -0
- data/lib/public/js/fg.menu.js +645 -0
- data/lib/public/js/jTypeWriter.js +26 -0
- data/lib/public/js/jquery-1.4.2.js +6240 -0
- data/lib/public/js/jquery-print.js +109 -0
- data/lib/public/js/jquery-pubsub.js +27 -0
- data/lib/public/js/jquery-terminal.js +2712 -0
- data/lib/public/js/jquery.batchImageLoad.js +56 -0
- data/lib/public/js/jquery.cycle.all.js +1284 -0
- data/lib/public/js/keyboard.js +733 -0
- data/lib/public/js/parade-code-execution.js +122 -0
- data/lib/public/js/parade-command-input.js +16 -0
- data/lib/public/js/parade-command-visor.js +92 -0
- data/lib/public/js/parade-keyboard-input.js +54 -0
- data/lib/public/js/parade.js +675 -0
- data/lib/public/js/spine.js +904 -0
- data/lib/templates/config.ru.erb +4 -0
- data/lib/templates/showoff.erb +27 -0
- data/lib/templates/slides.md.erb +25 -0
- data/lib/views/header.erb +73 -0
- data/lib/views/index.erb +53 -0
- data/lib/views/inline_css.erb +3 -0
- data/lib/views/inline_js.erb +3 -0
- data/lib/views/onepage.erb +17 -0
- data/lib/views/pdf.erb +17 -0
- data/lib/views/slide.erb +5 -0
- metadata +317 -0
@@ -0,0 +1,733 @@
|
|
1
|
+
/**
|
2
|
+
* Title: KeyboardJS
|
3
|
+
* Version: v0.3.0
|
4
|
+
* Description: KeyboardJS is a flexible and easy to use keyboard binding library.
|
5
|
+
* Author: Robert Hurst.
|
6
|
+
*
|
7
|
+
* Copyright 2011, Robert William Hurst
|
8
|
+
* Licenced under the BSD License.
|
9
|
+
* See https://raw.github.com/RobertWHurst/KeyboardJS/master/license.txt
|
10
|
+
*/
|
11
|
+
(function(context, factory) {
|
12
|
+
var namespaces = [], previousValues = {}, library;
|
13
|
+
if(typeof define === 'function' && define.amd) {
|
14
|
+
define(function() { return factory('amd'); });
|
15
|
+
} else {
|
16
|
+
library = factory('global');
|
17
|
+
library.noConflict = function( ) {
|
18
|
+
var args, nI;
|
19
|
+
newNamespaces = Array.prototype.slice.apply(arguments);
|
20
|
+
for(nI = 0; nI < namespaces.length; nI += 1) {
|
21
|
+
if(typeof previousValues[namespaces[nI]] === 'undefined') {
|
22
|
+
delete context[namespaces[nI]];
|
23
|
+
} else {
|
24
|
+
context[namespaces[nI]] = previousValues[namespaces[nI]];
|
25
|
+
}
|
26
|
+
}
|
27
|
+
previousValues = {};
|
28
|
+
for(nI = 0; nI < newNamespaces.length; nI += 1) {
|
29
|
+
if(typeof newNamespaces[nI] !== 'string') { throw new Error('Cannot replace namespaces. All new namespaces must be strings.'); }
|
30
|
+
previousValues[newNamespaces[nI]] = context[newNamespaces[nI]];
|
31
|
+
context[newNamespaces[nI]] = library;
|
32
|
+
}
|
33
|
+
namespaces = newNamespaces;
|
34
|
+
return namespaces;
|
35
|
+
};
|
36
|
+
library.noConflict('KeyboardJS', 'k');
|
37
|
+
}
|
38
|
+
})(this, function(env) {
|
39
|
+
var KeyboardJS = {}, locales, locale, map, macros, activeKeys = [], bindings = [], activeBindings = [], activeMacros = [];
|
40
|
+
|
41
|
+
//INDEXOF POLLYFILL
|
42
|
+
[].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;});
|
43
|
+
|
44
|
+
//BUNDLED LOCALES
|
45
|
+
locales = {
|
46
|
+
'us': {
|
47
|
+
"map": {
|
48
|
+
|
49
|
+
//general
|
50
|
+
"3": ["cancel"],
|
51
|
+
"8": ["backspace"],
|
52
|
+
"9": ["tab"],
|
53
|
+
"12": ["clear"],
|
54
|
+
"13": ["enter"],
|
55
|
+
"16": ["shift"],
|
56
|
+
"17": ["ctrl"],
|
57
|
+
"18": ["alt", "menu"],
|
58
|
+
"19": ["pause", "break"],
|
59
|
+
"20": ["capslock"],
|
60
|
+
"27": ["escape", "esc"],
|
61
|
+
"32": ["space", "spacebar"],
|
62
|
+
"33": ["pageup"],
|
63
|
+
"34": ["pagedown"],
|
64
|
+
"35": ["end"],
|
65
|
+
"36": ["home"],
|
66
|
+
"37": ["left"],
|
67
|
+
"38": ["up"],
|
68
|
+
"39": ["right"],
|
69
|
+
"40": ["down"],
|
70
|
+
"41": ["select"],
|
71
|
+
"42": ["printscreen"],
|
72
|
+
"43": ["execute"],
|
73
|
+
"44": ["snapshot"],
|
74
|
+
"45": ["insert", "ins"],
|
75
|
+
"46": ["delete", "del"],
|
76
|
+
"47": ["help"],
|
77
|
+
"91": ["command", "windows", "win", "super", "leftcommand", "leftwindows", "leftwin", "leftsuper"],
|
78
|
+
"92": ["command", "windows", "win", "super", "rightcommand", "rightwindows", "rightwin", "rightsuper"],
|
79
|
+
"145": ["scrolllock", "scroll"],
|
80
|
+
"186": ["semicolon", ";"],
|
81
|
+
"187": ["equal", "equalsign", "="],
|
82
|
+
"188": ["comma", ","],
|
83
|
+
"189": ["dash", "-"],
|
84
|
+
"190": ["period", "."],
|
85
|
+
"191": ["slash", "forwardslash", "/"],
|
86
|
+
"192": ["graveaccent", "`"],
|
87
|
+
"219": ["openbracket", "["],
|
88
|
+
"220": ["backslash", "\\"],
|
89
|
+
"221": ["closebracket", "]"],
|
90
|
+
"222": ["apostrophe", "'"],
|
91
|
+
|
92
|
+
//a-z
|
93
|
+
"65": ["a"],
|
94
|
+
"66": ["b"],
|
95
|
+
"67": ["c"],
|
96
|
+
"68": ["d"],
|
97
|
+
"69": ["e"],
|
98
|
+
"70": ["f"],
|
99
|
+
"71": ["g"],
|
100
|
+
"72": ["h"],
|
101
|
+
"73": ["i"],
|
102
|
+
"74": ["j"],
|
103
|
+
"75": ["k"],
|
104
|
+
"76": ["l"],
|
105
|
+
"77": ["m"],
|
106
|
+
"78": ["n"],
|
107
|
+
"79": ["o"],
|
108
|
+
"80": ["p"],
|
109
|
+
"81": ["q"],
|
110
|
+
"82": ["r"],
|
111
|
+
"83": ["s"],
|
112
|
+
"84": ["t"],
|
113
|
+
"85": ["u"],
|
114
|
+
"86": ["v"],
|
115
|
+
"87": ["w"],
|
116
|
+
"88": ["x"],
|
117
|
+
"89": ["y"],
|
118
|
+
"90": ["z"],
|
119
|
+
|
120
|
+
//0-9
|
121
|
+
"48": ["zero", "0"],
|
122
|
+
"49": ["one", "1"],
|
123
|
+
"50": ["two", "2"],
|
124
|
+
"51": ["three", "3"],
|
125
|
+
"52": ["four", "4"],
|
126
|
+
"53": ["five", "5"],
|
127
|
+
"54": ["six", "6"],
|
128
|
+
"55": ["seven", "7"],
|
129
|
+
"56": ["eight", "8"],
|
130
|
+
"57": ["nine", "9"],
|
131
|
+
|
132
|
+
//numpad
|
133
|
+
"96": ["numzero", "num0"],
|
134
|
+
"97": ["numone", "num1"],
|
135
|
+
"98": ["numtwo", "num2"],
|
136
|
+
"99": ["numthree", "num3"],
|
137
|
+
"100": ["numfour", "num4"],
|
138
|
+
"101": ["numfive", "num5"],
|
139
|
+
"102": ["numsix", "num6"],
|
140
|
+
"103": ["numseven", "num7"],
|
141
|
+
"104": ["numeight", "num8"],
|
142
|
+
"105": ["numnine", "num9"],
|
143
|
+
"106": ["nummultiply", "num*"],
|
144
|
+
"107": ["numadd", "num+"],
|
145
|
+
"108": ["numenter"],
|
146
|
+
"109": ["numsubtract", "num-"],
|
147
|
+
"110": ["numdecimal", "num."],
|
148
|
+
"111": ["numdevide", "num/"],
|
149
|
+
"144": ["numlock", "num"],
|
150
|
+
|
151
|
+
//function keys
|
152
|
+
"112": ["f1"],
|
153
|
+
"113": ["f2"],
|
154
|
+
"114": ["f3"],
|
155
|
+
"115": ["f4"],
|
156
|
+
"116": ["f5"],
|
157
|
+
"117": ["f6"],
|
158
|
+
"118": ["f7"],
|
159
|
+
"119": ["f8"],
|
160
|
+
"120": ["f9"],
|
161
|
+
"121": ["f10"],
|
162
|
+
"122": ["f11"],
|
163
|
+
"123": ["f12"]
|
164
|
+
},
|
165
|
+
"macros": [
|
166
|
+
|
167
|
+
//secondary key symbols
|
168
|
+
[[[["shift", "graveaccent"]]], ["tilde", "~"]],
|
169
|
+
[[[["shift", "one"]]], ["exclamation", "exclamationpoint", "!"]],
|
170
|
+
[[[["shift", "two"]]], ["at", "@"]],
|
171
|
+
[[[["shift", "three"]]], ["number", "#"]],
|
172
|
+
[[[["shift", "four"]]], ["dollar", "dollars", "dollarsign", "$"]],
|
173
|
+
[[[["shift", "five"]]], ["percent", "%"]],
|
174
|
+
[[[["shift", "six"]]], ["caret", "^"]],
|
175
|
+
[[[["shift", "seven"]]], ["ampersand", "and", "&"]],
|
176
|
+
[[[["shift", "eight"]]], ["asterisk", "*"]],
|
177
|
+
[[[["shift", "nine"]]], ["openparen", "("]],
|
178
|
+
[[[["shift", "zero"]]], ["closeparen", ")"]],
|
179
|
+
[[[["shift", "dash"]]], ["underscore", "_"]],
|
180
|
+
[[[["shift", "equal"]]], ["plus", "+"]],
|
181
|
+
[[[["shift", "openbracket"]]], ["opencurlybrace", "opencurlybracket", "{"]],
|
182
|
+
[[[["shift", "closebracket"]]], ["closecurlybrace", "closecurlybracket", "}"]],
|
183
|
+
[[[["shift", "backslash"]]], ["verticalbar", "|"]],
|
184
|
+
[[[["shift", "semicolon"]]], ["colon", ":"]],
|
185
|
+
[[[["shift", "apostrophe"]]], ["quotationmark", "\""]],
|
186
|
+
[[[["shift", "comma"]]], ["openanglebracket", "<"]],
|
187
|
+
[[[["shift", "period"]]], ["closeanglebracket", ">"]],
|
188
|
+
[[[["shift", "forwardslash"]]], ["questionmark", "?"]],
|
189
|
+
|
190
|
+
//capital A-Z
|
191
|
+
[[[["shift", "a"]]], ["A"]],
|
192
|
+
[[[["shift", "b"]]], ["B"]],
|
193
|
+
[[[["shift", "c"]]], ["C"]],
|
194
|
+
[[[["shift", "d"]]], ["D"]],
|
195
|
+
[[[["shift", "e"]]], ["E"]],
|
196
|
+
[[[["shift", "f"]]], ["F"]],
|
197
|
+
[[[["shift", "g"]]], ["G"]],
|
198
|
+
[[[["shift", "h"]]], ["H"]],
|
199
|
+
[[[["shift", "i"]]], ["I"]],
|
200
|
+
[[[["shift", "j"]]], ["J"]],
|
201
|
+
[[[["shift", "k"]]], ["K"]],
|
202
|
+
[[[["shift", "l"]]], ["L"]],
|
203
|
+
[[[["shift", "m"]]], ["M"]],
|
204
|
+
[[[["shift", "n"]]], ["N"]],
|
205
|
+
[[[["shift", "o"]]], ["O"]],
|
206
|
+
[[[["shift", "p"]]], ["P"]],
|
207
|
+
[[[["shift", "q"]]], ["Q"]],
|
208
|
+
[[[["shift", "r"]]], ["R"]],
|
209
|
+
[[[["shift", "s"]]], ["S"]],
|
210
|
+
[[[["shift", "t"]]], ["T"]],
|
211
|
+
[[[["shift", "u"]]], ["U"]],
|
212
|
+
[[[["shift", "v"]]], ["V"]],
|
213
|
+
[[[["shift", "w"]]], ["W"]],
|
214
|
+
[[[["shift", "x"]]], ["X"]],
|
215
|
+
[[[["shift", "y"]]], ["Y"]],
|
216
|
+
[[[["shift", "z"]]], ["Z"]]
|
217
|
+
]
|
218
|
+
}
|
219
|
+
|
220
|
+
//If you create a new locale please submit it as a pull request or post it in the issue tracker at
|
221
|
+
// http://github.com/RobertWhurst/KeyboardJS/issues/
|
222
|
+
};
|
223
|
+
locale = 'us';
|
224
|
+
map = locales[locale].map;
|
225
|
+
macros = locales[locale].macros;
|
226
|
+
if(document.addEventListener) {
|
227
|
+
document.addEventListener('keydown', keydown, false);
|
228
|
+
document.addEventListener('keyup', keyup, false);
|
229
|
+
document.addEventListener('blur', blur, false);
|
230
|
+
} else if(document.attachEvent) {
|
231
|
+
document.attachEvent('onkeydown', keydown);
|
232
|
+
document.attachEvent('onkeyup', keyup);
|
233
|
+
document.attachEvent('onblur', blur);
|
234
|
+
} else {
|
235
|
+
throw new Error('Cannot bind to keydown event. Both addEventListener and attachEvent are unsupported by your browser.');
|
236
|
+
}
|
237
|
+
|
238
|
+
KeyboardJS.enable = function() {
|
239
|
+
KeyboardJS.enabled = true;
|
240
|
+
};
|
241
|
+
|
242
|
+
KeyboardJS.disable = function() {
|
243
|
+
KeyboardJS.enabled = false;
|
244
|
+
};
|
245
|
+
|
246
|
+
KeyboardJS.toggle = function() {
|
247
|
+
KeyboardJS.enabled = !KeyboardJS.enabled;
|
248
|
+
};
|
249
|
+
|
250
|
+
KeyboardJS.enabled = true;
|
251
|
+
KeyboardJS.activeKeys = getActiveKeys;
|
252
|
+
KeyboardJS.on = createBinding;
|
253
|
+
KeyboardJS.clear = removeBindingByKeyCombo;
|
254
|
+
KeyboardJS.clear.key = removeBindingByKeyName;
|
255
|
+
KeyboardJS.locale = getSetLocale;
|
256
|
+
KeyboardJS.locale.register = registerLocale;
|
257
|
+
KeyboardJS.macro = createMacro;
|
258
|
+
KeyboardJS.macro.remove = removeMacro;
|
259
|
+
KeyboardJS.getKey = getKeyName;
|
260
|
+
KeyboardJS.combo = {};
|
261
|
+
KeyboardJS.combo.parse = parseKeyCombo;
|
262
|
+
KeyboardJS.combo.stringify = stringifyKeyCombo;
|
263
|
+
|
264
|
+
window.map = map;
|
265
|
+
window.macros = macros;
|
266
|
+
|
267
|
+
return KeyboardJS;
|
268
|
+
|
269
|
+
function keydown(event) {
|
270
|
+
if (!KeyboardJS.enabled) { return; }
|
271
|
+
|
272
|
+
var keyNames, kI;
|
273
|
+
keyNames = getKeyName(event.keyCode);
|
274
|
+
if(keyNames.length < 1) { return; }
|
275
|
+
for(kI = 0; kI < keyNames.length; kI += 1) {
|
276
|
+
addActiveKey(keyNames[kI]);
|
277
|
+
}
|
278
|
+
executeMacros();
|
279
|
+
executeBindings(event);
|
280
|
+
}
|
281
|
+
function keyup(event) {
|
282
|
+
if (!KeyboardJS.enabled) { return; }
|
283
|
+
|
284
|
+
var keyNames, kI;
|
285
|
+
keyNames = getKeyName(event.keyCode);
|
286
|
+
if(keyNames.length < 1) { return; }
|
287
|
+
for(kI = 0; kI < keyNames.length; kI += 1) {
|
288
|
+
removeActiveKey(keyNames[kI]);
|
289
|
+
}
|
290
|
+
pruneMacros();
|
291
|
+
pruneBindings(event);
|
292
|
+
}
|
293
|
+
function blur(event) {
|
294
|
+
if (!KeyboardJS.enabled) { return; }
|
295
|
+
|
296
|
+
activeKeys = [];
|
297
|
+
pruneMacros();
|
298
|
+
pruneBindings(event);
|
299
|
+
}
|
300
|
+
function getKeyName(keyCode) {
|
301
|
+
return map[keyCode] || [];
|
302
|
+
}
|
303
|
+
function createMacro(combo, injectedKeys) {
|
304
|
+
if(typeof combo !== 'string' && (typeof combo !== 'object' || typeof combo.push !== 'function')) { throw new Error("Cannot create macro. The combo must be a string or array."); }
|
305
|
+
if(typeof injectedKeys !== 'object' || typeof injectedKeys.push !== 'function') { throw new Error("Cannot create macro. The injectedKeys must be an array."); }
|
306
|
+
marcos.push([combo, injectKeys]);
|
307
|
+
}
|
308
|
+
function removeMacro(combo) {
|
309
|
+
var macro;
|
310
|
+
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."); }
|
311
|
+
for(mI = 0; mI < macros.length; mI += 1) {
|
312
|
+
macro = macros[mI];
|
313
|
+
if(compareCombos(combo, macro[0])) {
|
314
|
+
removeActiveKey(macro[1]);
|
315
|
+
macros.splice(mI, 1);
|
316
|
+
break;
|
317
|
+
}
|
318
|
+
}
|
319
|
+
}
|
320
|
+
function executeMacros() {
|
321
|
+
var mI, combo, kI;
|
322
|
+
for(mI = 0; mI < macros.length; mI += 1) {
|
323
|
+
combo = parseKeyCombo(macros[mI][0]);
|
324
|
+
if(activeMacros.indexOf(macros[mI]) === -1 && isSatifiedCombo(combo)) {
|
325
|
+
activeMacros.push(macros[mI]);
|
326
|
+
for(kI = 0; kI < macros[mI][1].length; kI += 1) {
|
327
|
+
addActiveKey(macros[mI][1][kI]);
|
328
|
+
}
|
329
|
+
}
|
330
|
+
}
|
331
|
+
}
|
332
|
+
function pruneMacros() {
|
333
|
+
var mI, combo, kI;
|
334
|
+
for(mI = 0; mI < activeMacros.length; mI += 1) {
|
335
|
+
combo = parseKeyCombo(activeMacros[mI][0]);
|
336
|
+
if(isSatifiedCombo(combo) === false) {
|
337
|
+
for(kI = 0; kI < activeMacros[mI][1].length; kI += 1) {
|
338
|
+
removeActiveKey(activeMacros[mI][1][kI]);
|
339
|
+
}
|
340
|
+
activeMacros.splice(mI, 1);
|
341
|
+
mI -= 1;
|
342
|
+
}
|
343
|
+
}
|
344
|
+
}
|
345
|
+
function createBinding(keyCombo, keyDownCallback, keyUpCallback) {
|
346
|
+
var binding, subBindings = [], bindingApi = {}, kI, subCombo;
|
347
|
+
if(typeof keyCombo === 'string') {
|
348
|
+
keyCombo = parseKeyCombo(keyCombo);
|
349
|
+
}
|
350
|
+
for(kI = 0; kI < keyCombo.length; kI += 1) {
|
351
|
+
binding = {};
|
352
|
+
|
353
|
+
subCombo = stringifyKeyCombo([keyCombo[kI]]);
|
354
|
+
|
355
|
+
if(typeof subCombo === 'function') {
|
356
|
+
subCombo = false;
|
357
|
+
keyUpCallback = keyDownCallback;
|
358
|
+
keyDownCallback = subCombo;
|
359
|
+
}
|
360
|
+
|
361
|
+
if(subCombo !== false && typeof subCombo !== 'string') { throw new Error('Failed to bind key combo. The key combo must be string.'); }
|
362
|
+
binding.keyCombo = subCombo;
|
363
|
+
binding.keyDownCallback = [];
|
364
|
+
binding.keyUpCallback = [];
|
365
|
+
if(keyDownCallback) { binding.keyDownCallback.push(keyDownCallback); }
|
366
|
+
if(keyUpCallback) { binding.keyUpCallback.push(keyUpCallback); }
|
367
|
+
bindings.push(binding);
|
368
|
+
subBindings.push(binding);
|
369
|
+
}
|
370
|
+
|
371
|
+
return {
|
372
|
+
"clear": clear,
|
373
|
+
"on": on
|
374
|
+
};
|
375
|
+
|
376
|
+
function clear() {
|
377
|
+
var bI;
|
378
|
+
for(bI = 0; bI < subBindings.length; bI += 1) {
|
379
|
+
bindings.splice(bindings.indexOf(subBindings[bI]), 1);
|
380
|
+
}
|
381
|
+
}
|
382
|
+
|
383
|
+
function on(eventName ) {
|
384
|
+
var callbacks, cI, bI;
|
385
|
+
|
386
|
+
if(typeof eventName !== 'string') { throw new Error('Cannot bind callback. The event name must be a string.'); }
|
387
|
+
if(eventName !== 'keyup' && eventName !== 'keydown') { throw new Error('Cannot bind callback. The event name must be a "keyup" or "keydown".'); }
|
388
|
+
callbacks = Array.prototype.slice.apply(arguments, [1]);
|
389
|
+
for(cI = 0; cI < callbacks.length; cI += 1) {
|
390
|
+
if(typeof callbacks[cI] === 'function') {
|
391
|
+
if(eventName === 'keyup') {
|
392
|
+
for(bI = 0; bI < subBindings.length; bI += 1) {
|
393
|
+
subBindings[bI].keyUpCallback.push(callbacks[cI]);
|
394
|
+
}
|
395
|
+
} else {
|
396
|
+
for(bI = 0; bI < subBindings.length; bI += 1) {
|
397
|
+
subBindings[bI].keyDownCallback.push(callbacks[cI]);
|
398
|
+
}
|
399
|
+
}
|
400
|
+
}
|
401
|
+
}
|
402
|
+
|
403
|
+
return { "clear": clear };
|
404
|
+
|
405
|
+
function clear() {
|
406
|
+
var cI, bI;
|
407
|
+
for(cI = 0; cI < callbacks.length; cI += 1) {
|
408
|
+
if(typeof callbacks[cI] === 'function') {
|
409
|
+
if(eventName === 'keyup') {
|
410
|
+
for(bI = 0; bI < subBindings.length; bI += 1) {
|
411
|
+
subBindings[bI].keyUpCallback.splice(subBindings[bI].keyUpCallback.indexOf(callbacks[cI]), 1);
|
412
|
+
}
|
413
|
+
} else {
|
414
|
+
for(bI = 0; bI < subBindings.length; bI += 1) {
|
415
|
+
subBindings[bI].keyDownCallback.splice(subBindings[bI].keyDownCallback.indexOf(callbacks[cI]), 1);
|
416
|
+
}
|
417
|
+
}
|
418
|
+
}
|
419
|
+
}
|
420
|
+
}
|
421
|
+
}
|
422
|
+
}
|
423
|
+
function removeBindingByKeyCombo(keyCombo) {
|
424
|
+
var bI, binding, keyName;
|
425
|
+
for(bI = 0; bI < bindings.length; bI += 1) {
|
426
|
+
binding = bindings[bi];
|
427
|
+
if(compareCombos(keyCombo, binding.keyCombo)) {
|
428
|
+
bindings.splice(bI, 1); bI -= 1;
|
429
|
+
}
|
430
|
+
}
|
431
|
+
}
|
432
|
+
function removeBindingByKeyName(keyName) {
|
433
|
+
var bI, cI, binding;
|
434
|
+
for(bI = 0; bI < bindings.length; bI += 1) {
|
435
|
+
binding = bindings[bi];
|
436
|
+
for(cI = 0; cI < binding.keyCombo.length; cI += 1) {
|
437
|
+
if(binding.keyCombo[kI].indexOf(keyName) > -1) {
|
438
|
+
bindings.splice(bI, 1); bI -= 1;
|
439
|
+
break;
|
440
|
+
}
|
441
|
+
}
|
442
|
+
}
|
443
|
+
}
|
444
|
+
|
445
|
+
/**
|
446
|
+
* Executes bindings that are active. Only allows the keys to be used once as to prevent binding overlap.
|
447
|
+
* @param {KeyboardEvent} event The keyboard event.
|
448
|
+
*/
|
449
|
+
function executeBindings(event) {
|
450
|
+
var bI, sBI, binding, bidningKeys, remainingKeys, cI, killEventBubble, kI, bindingKeysSatified,
|
451
|
+
index, sortedBindings = [], bindingWeight;
|
452
|
+
|
453
|
+
remainingKeys = [].concat(activeKeys);
|
454
|
+
for(bI = 0; bI < bindings.length; bI += 1) {
|
455
|
+
bindingWeight = extractComboKeys(bindings[bI].keyCombo).length;
|
456
|
+
if(!sortedBindings[bindingWeight]) { sortedBindings[bindingWeight] = []; }
|
457
|
+
sortedBindings[bindingWeight].push(bindings[bI]);
|
458
|
+
}
|
459
|
+
for(sBI = sortedBindings.length - 1; sBI >= 0; sBI -= 1) {
|
460
|
+
if(!sortedBindings[sBI]) { continue; }
|
461
|
+
for(bI = 0; bI < sortedBindings[sBI].length; bI += 1) {
|
462
|
+
binding = sortedBindings[sBI][bI];
|
463
|
+
bindingKeys = extractComboKeys(binding.keyCombo);
|
464
|
+
bindingKeysSatified = true;
|
465
|
+
for(kI = 0; kI < bindingKeys.length; kI += 1) {
|
466
|
+
if(remainingKeys.indexOf(bindingKeys[kI]) === -1) {
|
467
|
+
bindingKeysSatified = false;
|
468
|
+
break;
|
469
|
+
}
|
470
|
+
}
|
471
|
+
if(bindingKeysSatified && isSatifiedCombo(binding.keyCombo)) {
|
472
|
+
activeBindings.push(binding);
|
473
|
+
for(kI = 0; kI < bindingKeys.length; kI += 1) {
|
474
|
+
index = remainingKeys.indexOf(bindingKeys[kI]);
|
475
|
+
if(index > -1) {
|
476
|
+
remainingKeys.splice(index, 1);
|
477
|
+
kI -= 1;
|
478
|
+
}
|
479
|
+
}
|
480
|
+
for(cI = 0; cI < binding.keyDownCallback.length; cI += 1) {
|
481
|
+
if (binding.keyDownCallback[cI](event) === false) {
|
482
|
+
killEventBubble = true;
|
483
|
+
}
|
484
|
+
}
|
485
|
+
if(killEventBubble === true) {
|
486
|
+
event.preventDefault();
|
487
|
+
event.stopPropagation();
|
488
|
+
}
|
489
|
+
}
|
490
|
+
}
|
491
|
+
}
|
492
|
+
}
|
493
|
+
|
494
|
+
/**
|
495
|
+
* Removes bindings that are no longer satisfied by the active keys. Also fires the keyup callbacks.
|
496
|
+
* @param {KeyboardEvent} event [description]
|
497
|
+
*/
|
498
|
+
function pruneBindings(event) {
|
499
|
+
var bI, cI, binding, killEventBubble;
|
500
|
+
for(bI = 0; bI < activeBindings.length; bI += 1) {
|
501
|
+
binding = activeBindings[bI];
|
502
|
+
if(isSatifiedCombo(binding.keyCombo) === false) {
|
503
|
+
for(cI = 0; cI < binding.keyUpCallback.length; cI += 1) {
|
504
|
+
if (binding.keyUpCallback[cI](event) === false) {
|
505
|
+
killEventBubble = true;
|
506
|
+
}
|
507
|
+
}
|
508
|
+
if(killEventBubble === true) {
|
509
|
+
event.preventDefault();
|
510
|
+
event.stopPropagation();
|
511
|
+
}
|
512
|
+
activeBindings.splice(bI, 1);
|
513
|
+
bI -= 1;
|
514
|
+
}
|
515
|
+
}
|
516
|
+
}
|
517
|
+
|
518
|
+
/**
|
519
|
+
* Compares two key combos returning true when they are functionally equivalent.
|
520
|
+
* @param {String|Array} keyComboArrayA keyCombo A key combo string or array.
|
521
|
+
* @param {String|Array} keyComboArrayB keyCombo A key combo string or array.
|
522
|
+
* @return {Boolean}
|
523
|
+
*/
|
524
|
+
function compareCombos(keyComboArrayA, keyComboArrayB) {
|
525
|
+
var cI, sI, kI;
|
526
|
+
keyComboArrayA = parseKeyCombo(keyComboArrayA);
|
527
|
+
keyComboArrayB = parseKeyCombo(keyComboArrayB);
|
528
|
+
if(keyComboArrayA.length !== keyComboArrayB.length) { return false; }
|
529
|
+
for(cI = 0; cI < keyComboArrayA.length; cI += 1) {
|
530
|
+
if(keyComboArrayA[aI].length !== keyComboArrayB[aI].length) { return false; }
|
531
|
+
for(sI = 0; sI < keyComboArrayA[aI].length; sI += 1) {
|
532
|
+
if(keyComboArrayA[aI][sI].length !== keyComboArrayB[aI][sI].length) { return false; }
|
533
|
+
for(kI = 0; kI < keyComboArrayA[aI][sI].length; kI += 1) {
|
534
|
+
if(keyComboArrayB[aI][sI].indexOf(keyComboArrayA[aI][sI][kI]) === -1) { return false; }
|
535
|
+
}
|
536
|
+
}
|
537
|
+
}
|
538
|
+
return true;
|
539
|
+
}
|
540
|
+
|
541
|
+
/**
|
542
|
+
* Checks to see if a key combo string or key array is satisfied by the currently active keys. It does not
|
543
|
+
* take into account spent keys.
|
544
|
+
* @param {String|Array} keyCombo A key combo string or array.
|
545
|
+
* @return {Boolean}
|
546
|
+
*/
|
547
|
+
function isSatifiedCombo(keyCombo) {
|
548
|
+
var cI, sI, stage, kI, stageOffset = 0, index;
|
549
|
+
keyCombo = parseKeyCombo(keyCombo);
|
550
|
+
for(cI = 0; cI < keyCombo.length; cI += 1) {
|
551
|
+
for(sI = 0; sI < keyCombo[cI].length; sI += 1) {
|
552
|
+
stage = [].concat(keyCombo[cI][sI]);
|
553
|
+
for(kI = stageOffset; kI < activeKeys.length; kI += 1) {
|
554
|
+
index = stage.indexOf(activeKeys[kI]);
|
555
|
+
if(index > -1) {
|
556
|
+
stage.splice(index, 1);
|
557
|
+
stageOffset = kI;
|
558
|
+
}
|
559
|
+
}
|
560
|
+
if(stage.length !== 0) { return false; }
|
561
|
+
}
|
562
|
+
}
|
563
|
+
return true;
|
564
|
+
}
|
565
|
+
|
566
|
+
/**
|
567
|
+
* Accepts a key combo array or string and returns a flat array containing all keys referenced by
|
568
|
+
* the key combo.
|
569
|
+
* @param {String|Array} keyCombo A key combo string or array.
|
570
|
+
* @return {Array}
|
571
|
+
*/
|
572
|
+
function extractComboKeys(keyCombo) {
|
573
|
+
var cI, sI, kI, keys = [];
|
574
|
+
keyCombo = parseKeyCombo(keyCombo);
|
575
|
+
for(cI = 0; cI < keyCombo.length; cI += 1) {
|
576
|
+
for(sI = 0; sI < keyCombo[cI].length; sI += 1) {
|
577
|
+
keys = keys.concat(keyCombo[cI][sI]);
|
578
|
+
}
|
579
|
+
}
|
580
|
+
return keys;
|
581
|
+
}
|
582
|
+
|
583
|
+
/**
|
584
|
+
* Converts a key combo string into a 3 dimensional array.
|
585
|
+
* Level 1 represents each combo as combo strings can contain more than one.
|
586
|
+
* Level 2 represents each stage. A stages are sub combos that must be satisfied in the order they are defined.
|
587
|
+
* Level 3 represents each key that must be pressed to satisfy a stage.
|
588
|
+
* @param {String} keyCombo A key combo string.
|
589
|
+
* @return {Array}
|
590
|
+
*/
|
591
|
+
function parseKeyCombo(keyCombo) {
|
592
|
+
var s = keyCombo, i = 0, op = 0, ws = false, nc = false, combos = [], combo = [], stage = [], key = '';
|
593
|
+
|
594
|
+
if(typeof keyCombo === 'object' && typeof keyCombo.push === 'function') { return keyCombo; }
|
595
|
+
if(typeof keyCombo !== 'string') { throw new Error('Cannot parse "keyCombo" because its type is "' + (typeof keyCombo) + '". It must be a "string".'); }
|
596
|
+
|
597
|
+
//remove leading whitespace
|
598
|
+
while(s.charAt(i) === ' ') { i += 1; }
|
599
|
+
while(true) {
|
600
|
+
if(s.charAt(i) === ' ') {
|
601
|
+
//white space & next combo op
|
602
|
+
while(s.charAt(i) === ' ') { i += 1; }
|
603
|
+
ws = true;
|
604
|
+
} else if(s.charAt(i) === ',') {
|
605
|
+
if(op || nc) { throw new Error('Failed to parse key combo. Unexpected , at character index ' + i + '.'); }
|
606
|
+
nc = true;
|
607
|
+
i += 1;
|
608
|
+
} else if(s.charAt(i) === '+') {
|
609
|
+
//next key
|
610
|
+
if(key.length) { stage.push(key); key = ''; }
|
611
|
+
if(op || nc) { throw new Error('Failed to parse key combo. Unexpected + at character index ' + i + '.'); }
|
612
|
+
op = true;
|
613
|
+
i += 1;
|
614
|
+
} else if(s.charAt(i) === '>') {
|
615
|
+
//next stage op
|
616
|
+
if(key.length) { stage.push(key); key = ''; }
|
617
|
+
if(stage.length) { combo.push(stage); stage = []; }
|
618
|
+
if(op || nc) { throw new Error('Failed to parse key combo. Unexpected > at character index ' + i + '.'); }
|
619
|
+
op = true;
|
620
|
+
i += 1;
|
621
|
+
} else if(i < s.length - 1 && s.charAt(i) === '!' && (s.charAt(i + 1) === '>' || s.charAt(i + 1) === ',' || s.charAt(i + 1) === '+')) {
|
622
|
+
key += s.charAt(i + 1);
|
623
|
+
op = false;
|
624
|
+
ws = false;
|
625
|
+
nc = false;
|
626
|
+
i += 2;
|
627
|
+
} else if(i < s.length && s.charAt(i) !== '+' && s.charAt(i) !== '>' && s.charAt(i) !== ',' && s.charAt(i) !== ' ') {
|
628
|
+
//end combo
|
629
|
+
if(op === false && ws === true || nc === true) {
|
630
|
+
if(key.length) { stage.push(key); key = ''; }
|
631
|
+
if(stage.length) { combo.push(stage); stage = []; }
|
632
|
+
if(combo.length) { combos.push(combo); combo = []; }
|
633
|
+
}
|
634
|
+
op = false;
|
635
|
+
ws = false;
|
636
|
+
nc = false;
|
637
|
+
//key
|
638
|
+
while(i < s.length && s.charAt(i) !== '+' && s.charAt(i) !== '>' && s.charAt(i) !== ',' && s.charAt(i) !== ' ') {
|
639
|
+
key += s.charAt(i);
|
640
|
+
i += 1;
|
641
|
+
}
|
642
|
+
} else {
|
643
|
+
//unknown char
|
644
|
+
i += 1;
|
645
|
+
continue;
|
646
|
+
}
|
647
|
+
//end of combos string
|
648
|
+
if(i >= s.length) {
|
649
|
+
if(key.length) { stage.push(key); key = ''; }
|
650
|
+
if(stage.length) { combo.push(stage); stage = []; }
|
651
|
+
if(combo.length) { combos.push(combo); combo = []; }
|
652
|
+
break;
|
653
|
+
}
|
654
|
+
}
|
655
|
+
return combos;
|
656
|
+
}
|
657
|
+
|
658
|
+
/**
|
659
|
+
* Stringifys a key combo.
|
660
|
+
* @param {Array|String} keyComboArray A key combo array. If a key combo string is given it will be returned.
|
661
|
+
* @return {String}
|
662
|
+
*/
|
663
|
+
function stringifyKeyCombo(keyComboArray) {
|
664
|
+
var cI, ccI, output = [];
|
665
|
+
if(typeof keyComboArray === 'string') { return keyComboArray; }
|
666
|
+
if(typeof keyComboArray !== 'object' || typeof keyComboArray.push !== 'function') { throw new Error('Cannot stringify key combo.'); }
|
667
|
+
for(cI = 0; cI < keyComboArray.length; cI += 1) {
|
668
|
+
output[cI] = [];
|
669
|
+
for(ccI = 0; ccI < keyComboArray[cI].length; ccI += 1) {
|
670
|
+
output[cI][ccI] = keyComboArray[cI][ccI].join(' + ');
|
671
|
+
}
|
672
|
+
output[cI] = output[cI].join(' > ');
|
673
|
+
}
|
674
|
+
return output.join(' ');
|
675
|
+
}
|
676
|
+
|
677
|
+
/**
|
678
|
+
* Returns the a copy of the active keys array.
|
679
|
+
* @return {Array}
|
680
|
+
*/
|
681
|
+
function getActiveKeys() {
|
682
|
+
return [].concat(activeKeys);
|
683
|
+
}
|
684
|
+
|
685
|
+
/**
|
686
|
+
* Adds a key to the active keys array, but only if it has not already been added.
|
687
|
+
* @param {String} keyName The key name string.
|
688
|
+
*/
|
689
|
+
function addActiveKey(keyName) {
|
690
|
+
if(keyName.match(/\s/)) { throw new Error('Cannot add key name ' + keyName + ' to active keys because it contains whitespace.'); }
|
691
|
+
if(activeKeys.indexOf(keyName) > -1) { return; }
|
692
|
+
activeKeys.push(keyName);
|
693
|
+
}
|
694
|
+
|
695
|
+
/**
|
696
|
+
* Removes a key from the active keys array.
|
697
|
+
* @param {String} keyName The key name string.
|
698
|
+
*/
|
699
|
+
function removeActiveKey(keyName) {
|
700
|
+
if(keyName === 'super') { activeKeys = []; } //remove all key on release of super.
|
701
|
+
activeKeys.splice(activeKeys.indexOf(keyName), 1);
|
702
|
+
}
|
703
|
+
|
704
|
+
/**
|
705
|
+
* 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
|
706
|
+
* alternative key names. For example if you program games you could create a locale for your key mappings. Instead of key 65 mapped
|
707
|
+
* to 'a' you could map it to 'jump'.
|
708
|
+
* @param {String} localeName The name of the new locale.
|
709
|
+
* @param {Object} localeMap The locale map.
|
710
|
+
*/
|
711
|
+
function registerLocale(localeName, localeMap) {
|
712
|
+
if(typeof localeName !== 'string') { throw new Error('Cannot register new locale. The locale name must be a string.'); }
|
713
|
+
if(typeof localeMap !== 'object') { throw new Error('Cannot register ' + localeName + ' locale. The locale map must be an object.'); }
|
714
|
+
if(typeof localeMap.keys !== 'object' || typeof localeMap.keys.push !== 'function' || typeof localeMap.map !== 'object' || typeof localeMap.map !== 'function') { throw new Error('Cannot register ' + localeName + ' locale. The locale map is invalid.'); }
|
715
|
+
locales[localeName] = localeMap;
|
716
|
+
}
|
717
|
+
|
718
|
+
/**
|
719
|
+
* Swaps the current locale.
|
720
|
+
* @param {String} localeName The locale to activate.
|
721
|
+
* @return {Object}
|
722
|
+
*/
|
723
|
+
function getSetLocale(localeName) {
|
724
|
+
if(!localeName) {
|
725
|
+
if(typeof localeName !== 'string') { throw new Error('Cannot set locale. The locale name must be a string.'); }
|
726
|
+
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.'); }
|
727
|
+
locale = localeName;
|
728
|
+
map = locales[locale].map;
|
729
|
+
macros = locales[locale].macros;
|
730
|
+
}
|
731
|
+
return locale;
|
732
|
+
}
|
733
|
+
});
|