parade 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. data/LICENSE +21 -0
  2. data/README.md +542 -0
  3. data/Rakefile +15 -0
  4. data/bin/parade +138 -0
  5. data/lib/parade.rb +43 -0
  6. data/lib/parade/commands/commands.rb +84 -0
  7. data/lib/parade/commands/generate_outline.rb +34 -0
  8. data/lib/parade/commands/generate_presentation.rb +34 -0
  9. data/lib/parade/commands/generate_rackup.rb +32 -0
  10. data/lib/parade/commands/html_output.rb +47 -0
  11. data/lib/parade/commands/render_from_template.rb +50 -0
  12. data/lib/parade/commands/static_html.rb +38 -0
  13. data/lib/parade/commands/static_pdf.rb +39 -0
  14. data/lib/parade/commands/unknown.rb +23 -0
  15. data/lib/parade/features/live_ruby.rb +18 -0
  16. data/lib/parade/features/pdf_presentation.rb +24 -0
  17. data/lib/parade/features/preshow.rb +11 -0
  18. data/lib/parade/helpers/encode_image.rb +24 -0
  19. data/lib/parade/helpers/template_generator.rb +130 -0
  20. data/lib/parade/metadata.rb +73 -0
  21. data/lib/parade/metadata/assignment.rb +38 -0
  22. data/lib/parade/metadata/css_classes.rb +22 -0
  23. data/lib/parade/metadata/html_id.rb +35 -0
  24. data/lib/parade/metadata/template.rb +31 -0
  25. data/lib/parade/parsers/dsl.rb +138 -0
  26. data/lib/parade/parsers/dsl_file_parser.rb +17 -0
  27. data/lib/parade/parsers/json_file_parser.rb +67 -0
  28. data/lib/parade/parsers/markdown_image_paths.rb +44 -0
  29. data/lib/parade/parsers/markdown_slide_splitter.rb +63 -0
  30. data/lib/parade/parsers/presentation_directory_parser.rb +36 -0
  31. data/lib/parade/parsers/presentation_file_parser.rb +27 -0
  32. data/lib/parade/parsers/presentation_filepath_parser.rb +35 -0
  33. data/lib/parade/parsers/slides_file_content_parser.rb +27 -0
  34. data/lib/parade/renderers/columns_renderer.rb +68 -0
  35. data/lib/parade/renderers/command_line_renderer.rb +142 -0
  36. data/lib/parade/renderers/html_with_pygments.rb +42 -0
  37. data/lib/parade/renderers/inline_images.rb +31 -0
  38. data/lib/parade/renderers/special_paragraph_renderer.rb +23 -0
  39. data/lib/parade/renderers/update_image_paths.rb +75 -0
  40. data/lib/parade/section.rb +183 -0
  41. data/lib/parade/server.rb +139 -0
  42. data/lib/parade/slide.rb +128 -0
  43. data/lib/parade/version.rb +3 -0
  44. data/lib/public/css/960.css +653 -0
  45. data/lib/public/css/fg.menu.css +114 -0
  46. data/lib/public/css/ghf_marked.css +180 -0
  47. data/lib/public/css/jquery-terminal.css +73 -0
  48. data/lib/public/css/onepage.css +62 -0
  49. data/lib/public/css/parade.css +450 -0
  50. data/lib/public/css/pdf.css +13 -0
  51. data/lib/public/css/reset.css +53 -0
  52. data/lib/public/css/spinner_bar.gif +0 -0
  53. data/lib/public/css/theme/images/ui-bg_diagonals-small_100_f0efea_40x40.png +0 -0
  54. data/lib/public/css/theme/images/ui-bg_flat_35_f0f0f0_40x100.png +0 -0
  55. data/lib/public/css/theme/images/ui-bg_glass_55_fcf0ba_1x400.png +0 -0
  56. data/lib/public/css/theme/images/ui-bg_glow-ball_25_2e2e28_600x600.png +0 -0
  57. data/lib/public/css/theme/images/ui-bg_highlight-soft_100_f0efea_1x100.png +0 -0
  58. data/lib/public/css/theme/images/ui-bg_highlight-soft_25_327E04_1x100.png +0 -0
  59. data/lib/public/css/theme/images/ui-bg_highlight-soft_25_5A9D1A_1x100.png +0 -0
  60. data/lib/public/css/theme/images/ui-bg_highlight-soft_95_ffedad_1x100.png +0 -0
  61. data/lib/public/css/theme/images/ui-bg_inset-soft_22_3b3b35_1x100.png +0 -0
  62. data/lib/public/css/theme/images/ui-icons_808080_256x240.png +0 -0
  63. data/lib/public/css/theme/images/ui-icons_8DC262_256x240.png +0 -0
  64. data/lib/public/css/theme/images/ui-icons_cd0a0a_256x240.png +0 -0
  65. data/lib/public/css/theme/images/ui-icons_e7e6e4_256x240.png +0 -0
  66. data/lib/public/css/theme/images/ui-icons_eeeeee_256x240.png +0 -0
  67. data/lib/public/css/theme/images/ui-icons_ffffff_256x240.png +0 -0
  68. data/lib/public/css/theme/ui.accordion.css +9 -0
  69. data/lib/public/css/theme/ui.all.css +2 -0
  70. data/lib/public/css/theme/ui.base.css +9 -0
  71. data/lib/public/css/theme/ui.core.css +37 -0
  72. data/lib/public/css/theme/ui.datepicker.css +62 -0
  73. data/lib/public/css/theme/ui.dialog.css +13 -0
  74. data/lib/public/css/theme/ui.progressbar.css +4 -0
  75. data/lib/public/css/theme/ui.resizable.css +13 -0
  76. data/lib/public/css/theme/ui.slider.css +17 -0
  77. data/lib/public/css/theme/ui.tabs.css +9 -0
  78. data/lib/public/css/theme/ui.theme.css +245 -0
  79. data/lib/public/favicon.ico +0 -0
  80. data/lib/public/js/coffee-script.js +8 -0
  81. data/lib/public/js/fg.menu.js +645 -0
  82. data/lib/public/js/jTypeWriter.js +26 -0
  83. data/lib/public/js/jquery-1.4.2.js +6240 -0
  84. data/lib/public/js/jquery-print.js +109 -0
  85. data/lib/public/js/jquery-pubsub.js +27 -0
  86. data/lib/public/js/jquery-terminal.js +2712 -0
  87. data/lib/public/js/jquery.batchImageLoad.js +56 -0
  88. data/lib/public/js/jquery.cycle.all.js +1284 -0
  89. data/lib/public/js/keyboard.js +733 -0
  90. data/lib/public/js/parade-code-execution.js +122 -0
  91. data/lib/public/js/parade-command-input.js +16 -0
  92. data/lib/public/js/parade-command-visor.js +92 -0
  93. data/lib/public/js/parade-keyboard-input.js +54 -0
  94. data/lib/public/js/parade.js +675 -0
  95. data/lib/public/js/spine.js +904 -0
  96. data/lib/templates/config.ru.erb +4 -0
  97. data/lib/templates/showoff.erb +27 -0
  98. data/lib/templates/slides.md.erb +25 -0
  99. data/lib/views/header.erb +73 -0
  100. data/lib/views/index.erb +53 -0
  101. data/lib/views/inline_css.erb +3 -0
  102. data/lib/views/inline_js.erb +3 -0
  103. data/lib/views/onepage.erb +17 -0
  104. data/lib/views/pdf.erb +17 -0
  105. data/lib/views/slide.erb +5 -0
  106. 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
+ });