├── .gitignore
├── .jshintrc
├── LICENSE
├── README.md
├── browser.js
├── dist
├── aframe-keyboard-controls.js
└── aframe-keyboard-controls.min.js
├── examples
├── assets
│ ├── 321103__nsstudios__blip1.wav
│ ├── peach-gradient-1.jpg
│ ├── tex
│ │ └── shadow-circle.png
│ ├── tree1.dae
│ └── tree2.dae
└── index.html
├── keyboard-controls.js
├── lib
└── keyboard.polyfill.js
├── package.json
└── tests
├── .jshintrc
├── __init.test.js
├── helpers.js
├── karma.conf.js
└── keyboard-controls.test.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .DS_Store
3 | .gh-pages
4 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "globals": {
3 | "THREE": false,
4 | "window": false,
5 | "document": false,
6 | "require": false,
7 | "console": false,
8 | "module": false
9 | },
10 | "bitwise": false,
11 | "browser": true,
12 | "eqeqeq": true,
13 | "esnext": true,
14 | "expr": true,
15 | "forin": true,
16 | "immed": true,
17 | "latedef": "nofunc",
18 | "laxbreak": true,
19 | "maxlen": 100,
20 | "newcap": true,
21 | "noarg": true,
22 | "noempty": true,
23 | "noyield": true,
24 | "quotmark": "single",
25 | "smarttabs": false,
26 | "trailing": true,
27 | "undef": true,
28 | "unused": true,
29 | "white": false
30 | }
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | The MIT License (MIT)
3 |
4 | Copyright (c) 2015 Don McCurdy
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # A-Frame `keyboard-controls` Component
2 |
3 | > **NOTICE**: This project is no longer maintained. I recommend using `movement-controls` from [A-Frame Extras](https://github.com/donmccurdy/aframe-extras) instead.
4 |
5 | Keyboard controls for A-Frame.
6 |
7 | ## Overview
8 |
9 | Bindings and events for keyboard controls on an A-Frame VR scene. When combined with [ProxyControls.js](https://proxy-controls.donmccurdy.com) (with the [proxy-controls](https://github.com/donmccurdy/aframe-proxy-controls) component for A-Frame), can also receive remote keyboard input by WebRTC.
10 |
11 | ## Usage
12 |
13 | Movement + keyboard events:
14 |
15 | ```html
16 |
17 |
19 |
20 |
21 | ```
22 |
23 | Keyboard events only (no movement):
24 |
25 | ```html
26 |
27 |
29 |
30 |
31 | ```
32 |
33 | The full list of options can be seen in
34 | [`keyboard-controls.js`](keyboard-controls.js).
35 |
36 | ## Usage + Remote Device
37 |
38 | [ProxyControls.js ⇢ Docs](http://localhost:3000/#/docs#remote-device)
39 |
40 | Example:
41 |
42 | ```html
43 |
44 |
46 |
47 |
48 | ```
49 |
50 | ## Check Keyboard State
51 |
52 | To check the pressed/unpressed state of a given [Keyboard.code](https://w3c.github.io/uievents-code/#code-value-tables), use the `isPressed()` method:
53 |
54 | ```javascript
55 | var keyboardControls = el.components['keyboard-controls'];
56 | keyboardControls.isPressed('ArrowLeft');
57 | ```
58 |
59 | ## Events
60 |
61 | `keyboard-controls` comes with a polyfill guaranteeing support for [KeyboardEvent.key](https://www.w3.org/TR/DOM-Level-3-Events-key/) and [KeyboardEvent.code](https://w3c.github.io/uievents-code/). When a `keydown` or `keyup` event is detected, an extra event is created with the `code` attached. Example usage:
62 |
63 | ```html
64 |
67 |
68 | ```
69 |
70 | A complete list of `code` values may be found [here](https://w3c.github.io/uievents-code/#code-value-tables).
71 |
72 | ## Known Issues
73 |
74 | In OS X, pressing the Command/Meta (⌘) key blocks all other key events. For example, pressing `A`, pressing `⌘`, releasing `A`, and then releasing `⌘` would create a `keydown:KeyA` event, but no `keyup:KeyA`. Because of this, I do not recommend using the Command/Meta key in your apps.
75 |
--------------------------------------------------------------------------------
/browser.js:
--------------------------------------------------------------------------------
1 |
2 | // Browser distrubution of the A-Frame component.
3 | (function (AFRAME) {
4 | if (!AFRAME) {
5 | console.error('Component attempted to register before AFRAME was available.');
6 | return;
7 | }
8 |
9 | (AFRAME.aframeCore || AFRAME).registerComponent('keyboard-controls', require('./keyboard-controls'));
10 |
11 | }(window.AFRAME));
12 |
--------------------------------------------------------------------------------
/dist/aframe-keyboard-controls.js:
--------------------------------------------------------------------------------
1 | /******/ (function(modules) { // webpackBootstrap
2 | /******/ // The module cache
3 | /******/ var installedModules = {};
4 |
5 | /******/ // The require function
6 | /******/ function __webpack_require__(moduleId) {
7 |
8 | /******/ // Check if module is in cache
9 | /******/ if(installedModules[moduleId])
10 | /******/ return installedModules[moduleId].exports;
11 |
12 | /******/ // Create a new module (and put it into the cache)
13 | /******/ var module = installedModules[moduleId] = {
14 | /******/ exports: {},
15 | /******/ id: moduleId,
16 | /******/ loaded: false
17 | /******/ };
18 |
19 | /******/ // Execute the module function
20 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
21 |
22 | /******/ // Flag the module as loaded
23 | /******/ module.loaded = true;
24 |
25 | /******/ // Return the exports of the module
26 | /******/ return module.exports;
27 | /******/ }
28 |
29 |
30 | /******/ // expose the modules object (__webpack_modules__)
31 | /******/ __webpack_require__.m = modules;
32 |
33 | /******/ // expose the module cache
34 | /******/ __webpack_require__.c = installedModules;
35 |
36 | /******/ // __webpack_public_path__
37 | /******/ __webpack_require__.p = "";
38 |
39 | /******/ // Load entry module and return exports
40 | /******/ return __webpack_require__(0);
41 | /******/ })
42 | /************************************************************************/
43 | /******/ ([
44 | /* 0 */
45 | /***/ function(module, exports, __webpack_require__) {
46 |
47 |
48 | // Browser distrubution of the A-Frame component.
49 | (function (AFRAME) {
50 | if (!AFRAME) {
51 | console.error('Component attempted to register before AFRAME was available.');
52 | return;
53 | }
54 |
55 | (AFRAME.aframeCore || AFRAME).registerComponent('keyboard-controls', __webpack_require__(1));
56 |
57 | }(window.AFRAME));
58 |
59 |
60 | /***/ },
61 | /* 1 */
62 | /***/ function(module, exports, __webpack_require__) {
63 |
64 | __webpack_require__(2);
65 |
66 | var MAX_DELTA = 200,
67 | PROXY_FLAG = '__keyboard-controls-proxy';
68 |
69 | var KeyboardEvent = window.KeyboardEvent;
70 |
71 | /**
72 | * Keyboard Controls component.
73 | *
74 | * Bind keyboard events to components, or control your entities with the WASD keys.
75 | *
76 | * @namespace wasd-controls
77 | * @param {number} [easing=20] - How fast the movement decelerates. If you hold the
78 | * keys the entity moves and if you release it will stop. Easing simulates friction.
79 | * @param {number} [acceleration=65] - Determines the acceleration given
80 | * to the entity when pressing the keys.
81 | * @param {number} [angularAcceleration=Math.PI*0.25] - Determines the angular
82 | * acceleration given to the entity when pressing the keys. Only applied when
83 | * mode == 'fps'. Measured in Radians.
84 | * @param {bool} [enabled=true] - To completely enable or disable the controls
85 | * @param {string} [mode='default'] -
86 | * 'default' enforces the direction of the movement to stick to the plane
87 | * where the entity started off.
88 | * 'fps' extends 'default' by enabling "First Person Shooter" controls: W/S =
89 | * Forward/Back, Q/E = Strafe left/right, A/D = Rotate left/right
90 | * 'fly' enables 6 degrees of freedom as a diver underwater or a plane flying.
91 | * @param {string} [rollAxis='z'] - The front-to-back axis.
92 | * @param {string} [pitchAxis='x'] - The left-to-right axis.
93 | * @param {bool} [rollAxisInverted=false] - Roll axis is inverted
94 | * @param {bool} [pitchAxisInverted=false] - Pitch axis is inverted
95 | * @param {bool} [yawAxisInverted=false] - Yaw axis is inverted. Used when
96 | * mode == 'fps'
97 | */
98 | module.exports = {
99 | schema: {
100 | easing: { default: 20 },
101 | acceleration: { default: 65 },
102 | angularAcceleration: { default: Math.PI / 4 },
103 | enabled: { default: true },
104 | mode: { default: 'default', oneOf: ['default', 'fly', 'fps']},
105 | rollAxis: { default: 'z', oneOf: [ 'x', 'y', 'z' ] },
106 | pitchAxis: { default: 'x', oneOf: [ 'x', 'y', 'z' ] },
107 | rollAxisInverted: { default: false },
108 | rollAxisEnabled: { default: true },
109 | pitchAxisInverted: { default: false },
110 | pitchAxisEnabled: { default: true },
111 | yawAxisInverted: { default: false },
112 | debug: { default: false }
113 | },
114 |
115 | init: function () {
116 | this.velocity = new THREE.Vector3();
117 | this.angularVelocity = 0;
118 | this.localKeys = {};
119 | this.listeners = {
120 | keydown: this.onKeyDown.bind(this),
121 | keyup: this.onKeyUp.bind(this),
122 | blur: this.onBlur.bind(this)
123 | };
124 | this.attachEventListeners();
125 | },
126 |
127 | /*******************************************************************
128 | * Movement
129 | */
130 |
131 | tick: (function () {
132 | var upVector = new THREE.Vector3(0, 1, 0);
133 | var rotation = new THREE.Euler(0, 0, 0, 'YXZ');
134 | return function (t, dt) {
135 | var data = this.data;
136 | var acceleration = data.acceleration;
137 | var angularAcceleration = data.angularAcceleration;
138 | var easing = data.easing;
139 | var velocity = this.velocity;
140 | var keys = this.getKeys();
141 | var movementVector;
142 | var pitchAxis = data.pitchAxis;
143 | var rollAxis = data.rollAxis;
144 | var pitchSign = data.pitchAxisInverted ? -1 : 1;
145 | var rollSign = data.rollAxisInverted ? -1 : 1;
146 | var yawSign = data.yawAxisInverted ? 1 : -1;
147 | var el = this.el;
148 | var strafeLeft = data.mode === 'fps' ? ['KeyQ', 'ArrowLeft'] : ['KeyA', 'ArrowLeft'];
149 | var strafeRight = data.mode === 'fps' ? ['KeyE', 'ArrowRight'] : ['KeyD', 'ArrowRight'];
150 |
151 | // If data changed or FPS too low, reset velocity.
152 | if (isNaN(dt) || dt > MAX_DELTA) {
153 | velocity[pitchAxis] = 0;
154 | velocity[rollAxis] = 0;
155 | this.angularVelocity = 0;
156 | return;
157 | }
158 |
159 | velocity[pitchAxis] -= velocity[pitchAxis] * easing * dt / 1000;
160 | velocity[rollAxis] -= velocity[rollAxis] * easing * dt / 1000;
161 | this.angularVelocity -= this.angularVelocity * easing * dt / 1000;
162 |
163 | var position = el.getAttribute('position');
164 |
165 | if (data.enabled) {
166 | if (data.pitchAxisEnabled) {
167 | if (keys[strafeLeft[0]] || keys[strafeLeft[1]]) {
168 | velocity[pitchAxis] -= pitchSign * acceleration * dt / 1000;
169 | }
170 | if (keys[strafeRight[0]] || keys[strafeRight[1]]) {
171 | velocity[pitchAxis] += pitchSign * acceleration * dt / 1000;
172 | }
173 | }
174 | if (data.rollAxisEnabled) {
175 | if (keys.KeyW || keys.ArrowUp) {
176 | velocity[rollAxis] -= rollSign * acceleration * dt / 1000;
177 | }
178 | if (keys.KeyS || keys.ArrowDown) {
179 | velocity[rollAxis] += rollSign * acceleration * dt / 1000;
180 | }
181 | }
182 | if (data.mode === 'fps') {
183 | if (keys.KeyA) {
184 | this.angularVelocity -= yawSign * angularAcceleration * dt / 1000;
185 | }
186 | if (keys.KeyD) {
187 | this.angularVelocity += yawSign * angularAcceleration * dt / 1000;
188 | }
189 | }
190 | }
191 |
192 | if (data.mode === 'fps') {
193 | this.rotateOnAxis(rotation, upVector, this.angularVelocity);
194 |
195 | el.setAttribute('rotation', {
196 | x: THREE.Math.radToDeg(rotation.x),
197 | y: THREE.Math.radToDeg(rotation.y),
198 | z: THREE.Math.radToDeg(rotation.z)
199 | });
200 | }
201 |
202 | movementVector = this.getMovementVector(dt);
203 | el.object3D.translateX(movementVector.x);
204 | el.object3D.translateY(movementVector.y);
205 | el.object3D.translateZ(movementVector.z);
206 |
207 | el.setAttribute('position', {
208 | x: position.x + movementVector.x,
209 | y: position.y + movementVector.y,
210 | z: position.z + movementVector.z
211 | });
212 | };
213 | })(),
214 |
215 |
216 | rotateOnAxis: (function () {
217 |
218 | var quaternion = new THREE.Quaternion();
219 | var eulerAsQuaternion = new THREE.Quaternion();
220 |
221 | return function (euler, axis, angle) {
222 | quaternion.setFromAxisAngle(axis, angle);
223 | eulerAsQuaternion.setFromEuler(euler);
224 | eulerAsQuaternion.multiply(quaternion);
225 | euler.setFromQuaternion(eulerAsQuaternion, euler.order);
226 | };
227 |
228 | })(),
229 |
230 | getMovementVector: (function () {
231 | var direction = new THREE.Vector3(0, 0, 0);
232 | var rotation = new THREE.Euler(0, 0, 0, 'YXZ');
233 | return function (dt) {
234 | var velocity = this.velocity;
235 | var elRotation = this.el.getAttribute('rotation');
236 | direction.copy(velocity);
237 | direction.multiplyScalar(dt / 1000);
238 | if (!elRotation) { return direction; }
239 | if (this.data.mode !== 'fly') { elRotation.x = 0; }
240 | rotation.set(THREE.Math.degToRad(elRotation.x),
241 | THREE.Math.degToRad(elRotation.y), 0);
242 | direction.applyEuler(rotation);
243 | return direction;
244 | };
245 | })(),
246 |
247 | /*******************************************************************
248 | * Events
249 | */
250 |
251 | play: function () {
252 | this.attachEventListeners();
253 | },
254 |
255 | pause: function () {
256 | this.removeEventListeners();
257 | },
258 |
259 | remove: function () {
260 | this.pause();
261 | },
262 |
263 | attachEventListeners: function () {
264 | window.addEventListener('keydown', this.listeners.keydown, false);
265 | window.addEventListener('keyup', this.listeners.keyup, false);
266 | window.addEventListener('blur', this.listeners.blur, false);
267 | },
268 |
269 | removeEventListeners: function () {
270 | window.removeEventListener('keydown', this.listeners.keydown);
271 | window.removeEventListener('keyup', this.listeners.keyup);
272 | window.removeEventListener('blur', this.listeners.blur);
273 | },
274 |
275 | onKeyDown: function (event) {
276 | this.localKeys[event.code] = true;
277 | this.emit(event);
278 | },
279 |
280 | onKeyUp: function (event) {
281 | delete this.localKeys[event.code];
282 | this.emit(event);
283 | },
284 |
285 | onBlur: function () {
286 | for (var code in this.localKeys) {
287 | if (this.localKeys.hasOwnProperty(code)) {
288 | delete this.localKeys[code];
289 | }
290 | }
291 | },
292 |
293 | emit: function (event) {
294 | // TODO - keydown only initially?
295 | // TODO - where the f is the spacebar
296 |
297 | // Emit original event.
298 | if (PROXY_FLAG in event) {
299 | // TODO - Method never triggered.
300 | this.el.emit(event.type, event);
301 | }
302 |
303 | // Emit convenience event, identifying key.
304 | this.el.emit(event.type + ':' + event.code, new KeyboardEvent(event.type, event));
305 | if (this.data.debug) console.log(event.type + ':' + event.code);
306 | },
307 |
308 | /*******************************************************************
309 | * Accessors
310 | */
311 |
312 | isPressed: function (code) {
313 | return code in this.getKeys();
314 | },
315 |
316 | getKeys: function () {
317 | if (this.isProxied()) {
318 | return this.el.sceneEl.components['proxy-controls'].getKeyboard();
319 | }
320 | return this.localKeys;
321 | },
322 |
323 | isProxied: function () {
324 | var proxyControls = this.el.sceneEl.components['proxy-controls'];
325 | return proxyControls && proxyControls.isConnected();
326 | }
327 |
328 | };
329 |
330 |
331 | /***/ },
332 | /* 2 */
333 | /***/ function(module, exports) {
334 |
335 | /**
336 | * Polyfill for the additional KeyboardEvent properties defined in the D3E and
337 | * D4E draft specifications, by @inexorabletash.
338 | *
339 | * See: https://github.com/inexorabletash/polyfill
340 | */
341 | (function(global) {
342 | var nativeKeyboardEvent = ('KeyboardEvent' in global);
343 | if (!nativeKeyboardEvent)
344 | global.KeyboardEvent = function KeyboardEvent() { throw TypeError('Illegal constructor'); };
345 |
346 | global.KeyboardEvent.DOM_KEY_LOCATION_STANDARD = 0x00; // Default or unknown location
347 | global.KeyboardEvent.DOM_KEY_LOCATION_LEFT = 0x01; // e.g. Left Alt key
348 | global.KeyboardEvent.DOM_KEY_LOCATION_RIGHT = 0x02; // e.g. Right Alt key
349 | global.KeyboardEvent.DOM_KEY_LOCATION_NUMPAD = 0x03; // e.g. Numpad 0 or +
350 |
351 | var STANDARD = window.KeyboardEvent.DOM_KEY_LOCATION_STANDARD,
352 | LEFT = window.KeyboardEvent.DOM_KEY_LOCATION_LEFT,
353 | RIGHT = window.KeyboardEvent.DOM_KEY_LOCATION_RIGHT,
354 | NUMPAD = window.KeyboardEvent.DOM_KEY_LOCATION_NUMPAD;
355 |
356 | //--------------------------------------------------------------------
357 | //
358 | // Utilities
359 | //
360 | //--------------------------------------------------------------------
361 |
362 | function contains(s, ss) { return String(s).indexOf(ss) !== -1; }
363 |
364 | var os = (function() {
365 | if (contains(navigator.platform, 'Win')) { return 'win'; }
366 | if (contains(navigator.platform, 'Mac')) { return 'mac'; }
367 | if (contains(navigator.platform, 'CrOS')) { return 'cros'; }
368 | if (contains(navigator.platform, 'Linux')) { return 'linux'; }
369 | if (contains(navigator.userAgent, 'iPad') || contains(navigator.platform, 'iPod') || contains(navigator.platform, 'iPhone')) { return 'ios'; }
370 | return '';
371 | } ());
372 |
373 | var browser = (function() {
374 | if (contains(navigator.userAgent, 'Chrome/')) { return 'chrome'; }
375 | if (contains(navigator.vendor, 'Apple')) { return 'safari'; }
376 | if (contains(navigator.userAgent, 'MSIE')) { return 'ie'; }
377 | if (contains(navigator.userAgent, 'Gecko/')) { return 'moz'; }
378 | if (contains(navigator.userAgent, 'Opera/')) { return 'opera'; }
379 | return '';
380 | } ());
381 |
382 | var browser_os = browser + '-' + os;
383 |
384 | function mergeIf(baseTable, select, table) {
385 | if (browser_os === select || browser === select || os === select) {
386 | Object.keys(table).forEach(function(keyCode) {
387 | baseTable[keyCode] = table[keyCode];
388 | });
389 | }
390 | }
391 |
392 | function remap(o, key) {
393 | var r = {};
394 | Object.keys(o).forEach(function(k) {
395 | var item = o[k];
396 | if (key in item) {
397 | r[item[key]] = item;
398 | }
399 | });
400 | return r;
401 | }
402 |
403 | function invert(o) {
404 | var r = {};
405 | Object.keys(o).forEach(function(k) {
406 | r[o[k]] = k;
407 | });
408 | return r;
409 | }
410 |
411 | //--------------------------------------------------------------------
412 | //
413 | // Generic Mappings
414 | //
415 | //--------------------------------------------------------------------
416 |
417 | // "keyInfo" is a dictionary:
418 | // code: string - name from DOM Level 3 KeyboardEvent code Values
419 | // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3Events-code.html
420 | // location (optional): number - one of the DOM_KEY_LOCATION values
421 | // keyCap (optional): string - keyboard label in en-US locale
422 | // USB code Usage ID from page 0x07 unless otherwise noted (Informative)
423 |
424 | // Map of keyCode to keyInfo
425 | var keyCodeToInfoTable = {
426 | // 0x01 - VK_LBUTTON
427 | // 0x02 - VK_RBUTTON
428 | 0x03: { code: 'Cancel' }, // [USB: 0x9b] char \x0018 ??? (Not in D3E)
429 | // 0x04 - VK_MBUTTON
430 | // 0x05 - VK_XBUTTON1
431 | // 0x06 - VK_XBUTTON2
432 | 0x06: { code: 'Help' }, // [USB: 0x75] ???
433 | // 0x07 - undefined
434 | 0x08: { code: 'Backspace' }, // [USB: 0x2a] Labelled Delete on Macintosh keyboards.
435 | 0x09: { code: 'Tab' }, // [USB: 0x2b]
436 | // 0x0A-0x0B - reserved
437 | 0X0C: { code: 'Clear' }, // [USB: 0x9c] NumPad Center (Not in D3E)
438 | 0X0D: { code: 'Enter' }, // [USB: 0x28]
439 | // 0x0E-0x0F - undefined
440 |
441 | 0x10: { code: 'Shift' },
442 | 0x11: { code: 'Control' },
443 | 0x12: { code: 'Alt' },
444 | 0x13: { code: 'Pause' }, // [USB: 0x48]
445 | 0x14: { code: 'CapsLock' }, // [USB: 0x39]
446 | 0x15: { code: 'KanaMode' }, // [USB: 0x88] - "HangulMode" for Korean layout
447 | 0x16: { code: 'HangulMode' }, // [USB: 0x90] 0x15 as well in MSDN VK table ???
448 | 0x17: { code: 'JunjaMode' }, // (Not in D3E)
449 | 0x18: { code: 'FinalMode' }, // (Not in D3E)
450 | 0x19: { code: 'KanjiMode' }, // [USB: 0x91] - "HanjaMode" for Korean layout
451 | // 0x1A - undefined
452 | 0x1B: { code: 'Escape' }, // [USB: 0x29]
453 | 0x1C: { code: 'Convert' }, // [USB: 0x8a]
454 | 0x1D: { code: 'NonConvert' }, // [USB: 0x8b]
455 | 0x1E: { code: 'Accept' }, // (Not in D3E)
456 | 0x1F: { code: 'ModeChange' }, // (Not in D3E)
457 |
458 | 0x20: { code: 'Space' }, // [USB: 0x2c]
459 | 0x21: { code: 'PageUp' }, // [USB: 0x4b]
460 | 0x22: { code: 'PageDown' }, // [USB: 0x4e]
461 | 0x23: { code: 'End' }, // [USB: 0x4d]
462 | 0x24: { code: 'Home' }, // [USB: 0x4a]
463 | 0x25: { code: 'ArrowLeft' }, // [USB: 0x50]
464 | 0x26: { code: 'ArrowUp' }, // [USB: 0x52]
465 | 0x27: { code: 'ArrowRight' }, // [USB: 0x4f]
466 | 0x28: { code: 'ArrowDown' }, // [USB: 0x51]
467 | 0x29: { code: 'Select' }, // (Not in D3E)
468 | 0x2A: { code: 'Print' }, // (Not in D3E)
469 | 0x2B: { code: 'Execute' }, // [USB: 0x74] (Not in D3E)
470 | 0x2C: { code: 'PrintScreen' }, // [USB: 0x46]
471 | 0x2D: { code: 'Insert' }, // [USB: 0x49]
472 | 0x2E: { code: 'Delete' }, // [USB: 0x4c]
473 | 0x2F: { code: 'Help' }, // [USB: 0x75] ???
474 |
475 | 0x30: { code: 'Digit0', keyCap: '0' }, // [USB: 0x27] 0)
476 | 0x31: { code: 'Digit1', keyCap: '1' }, // [USB: 0x1e] 1!
477 | 0x32: { code: 'Digit2', keyCap: '2' }, // [USB: 0x1f] 2@
478 | 0x33: { code: 'Digit3', keyCap: '3' }, // [USB: 0x20] 3#
479 | 0x34: { code: 'Digit4', keyCap: '4' }, // [USB: 0x21] 4$
480 | 0x35: { code: 'Digit5', keyCap: '5' }, // [USB: 0x22] 5%
481 | 0x36: { code: 'Digit6', keyCap: '6' }, // [USB: 0x23] 6^
482 | 0x37: { code: 'Digit7', keyCap: '7' }, // [USB: 0x24] 7&
483 | 0x38: { code: 'Digit8', keyCap: '8' }, // [USB: 0x25] 8*
484 | 0x39: { code: 'Digit9', keyCap: '9' }, // [USB: 0x26] 9(
485 | // 0x3A-0x40 - undefined
486 |
487 | 0x41: { code: 'KeyA', keyCap: 'a' }, // [USB: 0x04]
488 | 0x42: { code: 'KeyB', keyCap: 'b' }, // [USB: 0x05]
489 | 0x43: { code: 'KeyC', keyCap: 'c' }, // [USB: 0x06]
490 | 0x44: { code: 'KeyD', keyCap: 'd' }, // [USB: 0x07]
491 | 0x45: { code: 'KeyE', keyCap: 'e' }, // [USB: 0x08]
492 | 0x46: { code: 'KeyF', keyCap: 'f' }, // [USB: 0x09]
493 | 0x47: { code: 'KeyG', keyCap: 'g' }, // [USB: 0x0a]
494 | 0x48: { code: 'KeyH', keyCap: 'h' }, // [USB: 0x0b]
495 | 0x49: { code: 'KeyI', keyCap: 'i' }, // [USB: 0x0c]
496 | 0x4A: { code: 'KeyJ', keyCap: 'j' }, // [USB: 0x0d]
497 | 0x4B: { code: 'KeyK', keyCap: 'k' }, // [USB: 0x0e]
498 | 0x4C: { code: 'KeyL', keyCap: 'l' }, // [USB: 0x0f]
499 | 0x4D: { code: 'KeyM', keyCap: 'm' }, // [USB: 0x10]
500 | 0x4E: { code: 'KeyN', keyCap: 'n' }, // [USB: 0x11]
501 | 0x4F: { code: 'KeyO', keyCap: 'o' }, // [USB: 0x12]
502 |
503 | 0x50: { code: 'KeyP', keyCap: 'p' }, // [USB: 0x13]
504 | 0x51: { code: 'KeyQ', keyCap: 'q' }, // [USB: 0x14]
505 | 0x52: { code: 'KeyR', keyCap: 'r' }, // [USB: 0x15]
506 | 0x53: { code: 'KeyS', keyCap: 's' }, // [USB: 0x16]
507 | 0x54: { code: 'KeyT', keyCap: 't' }, // [USB: 0x17]
508 | 0x55: { code: 'KeyU', keyCap: 'u' }, // [USB: 0x18]
509 | 0x56: { code: 'KeyV', keyCap: 'v' }, // [USB: 0x19]
510 | 0x57: { code: 'KeyW', keyCap: 'w' }, // [USB: 0x1a]
511 | 0x58: { code: 'KeyX', keyCap: 'x' }, // [USB: 0x1b]
512 | 0x59: { code: 'KeyY', keyCap: 'y' }, // [USB: 0x1c]
513 | 0x5A: { code: 'KeyZ', keyCap: 'z' }, // [USB: 0x1d]
514 | 0x5B: { code: 'OSLeft', location: LEFT }, // [USB: 0xe3]
515 | 0x5C: { code: 'OSRight', location: RIGHT }, // [USB: 0xe7]
516 | 0x5D: { code: 'ContextMenu' }, // [USB: 0x65] Context Menu
517 | // 0x5E - reserved
518 | 0x5F: { code: 'Standby' }, // [USB: 0x82] Sleep
519 |
520 | 0x60: { code: 'Numpad0', keyCap: '0', location: NUMPAD }, // [USB: 0x62]
521 | 0x61: { code: 'Numpad1', keyCap: '1', location: NUMPAD }, // [USB: 0x59]
522 | 0x62: { code: 'Numpad2', keyCap: '2', location: NUMPAD }, // [USB: 0x5a]
523 | 0x63: { code: 'Numpad3', keyCap: '3', location: NUMPAD }, // [USB: 0x5b]
524 | 0x64: { code: 'Numpad4', keyCap: '4', location: NUMPAD }, // [USB: 0x5c]
525 | 0x65: { code: 'Numpad5', keyCap: '5', location: NUMPAD }, // [USB: 0x5d]
526 | 0x66: { code: 'Numpad6', keyCap: '6', location: NUMPAD }, // [USB: 0x5e]
527 | 0x67: { code: 'Numpad7', keyCap: '7', location: NUMPAD }, // [USB: 0x5f]
528 | 0x68: { code: 'Numpad8', keyCap: '8', location: NUMPAD }, // [USB: 0x60]
529 | 0x69: { code: 'Numpad9', keyCap: '9', location: NUMPAD }, // [USB: 0x61]
530 | 0x6A: { code: 'NumpadMultiply', keyCap: '*', location: NUMPAD }, // [USB: 0x55]
531 | 0x6B: { code: 'NumpadAdd', keyCap: '+', location: NUMPAD }, // [USB: 0x57]
532 | 0x6C: { code: 'NumpadComma', keyCap: ',', location: NUMPAD }, // [USB: 0x85]
533 | 0x6D: { code: 'NumpadSubtract', keyCap: '-', location: NUMPAD }, // [USB: 0x56]
534 | 0x6E: { code: 'NumpadDecimal', keyCap: '.', location: NUMPAD }, // [USB: 0x63]
535 | 0x6F: { code: 'NumpadDivide', keyCap: '/', location: NUMPAD }, // [USB: 0x54]
536 |
537 | 0x70: { code: 'F1' }, // [USB: 0x3a]
538 | 0x71: { code: 'F2' }, // [USB: 0x3b]
539 | 0x72: { code: 'F3' }, // [USB: 0x3c]
540 | 0x73: { code: 'F4' }, // [USB: 0x3d]
541 | 0x74: { code: 'F5' }, // [USB: 0x3e]
542 | 0x75: { code: 'F6' }, // [USB: 0x3f]
543 | 0x76: { code: 'F7' }, // [USB: 0x40]
544 | 0x77: { code: 'F8' }, // [USB: 0x41]
545 | 0x78: { code: 'F9' }, // [USB: 0x42]
546 | 0x79: { code: 'F10' }, // [USB: 0x43]
547 | 0x7A: { code: 'F11' }, // [USB: 0x44]
548 | 0x7B: { code: 'F12' }, // [USB: 0x45]
549 | 0x7C: { code: 'F13' }, // [USB: 0x68]
550 | 0x7D: { code: 'F14' }, // [USB: 0x69]
551 | 0x7E: { code: 'F15' }, // [USB: 0x6a]
552 | 0x7F: { code: 'F16' }, // [USB: 0x6b]
553 |
554 | 0x80: { code: 'F17' }, // [USB: 0x6c]
555 | 0x81: { code: 'F18' }, // [USB: 0x6d]
556 | 0x82: { code: 'F19' }, // [USB: 0x6e]
557 | 0x83: { code: 'F20' }, // [USB: 0x6f]
558 | 0x84: { code: 'F21' }, // [USB: 0x70]
559 | 0x85: { code: 'F22' }, // [USB: 0x71]
560 | 0x86: { code: 'F23' }, // [USB: 0x72]
561 | 0x87: { code: 'F24' }, // [USB: 0x73]
562 | // 0x88-0x8F - unassigned
563 |
564 | 0x90: { code: 'NumLock', location: NUMPAD }, // [USB: 0x53]
565 | 0x91: { code: 'ScrollLock' }, // [USB: 0x47]
566 | // 0x92-0x96 - OEM specific
567 | // 0x97-0x9F - unassigned
568 |
569 | // NOTE: 0xA0-0xA5 usually mapped to 0x10-0x12 in browsers
570 | 0xA0: { code: 'ShiftLeft', location: LEFT }, // [USB: 0xe1]
571 | 0xA1: { code: 'ShiftRight', location: RIGHT }, // [USB: 0xe5]
572 | 0xA2: { code: 'ControlLeft', location: LEFT }, // [USB: 0xe0]
573 | 0xA3: { code: 'ControlRight', location: RIGHT }, // [USB: 0xe4]
574 | 0xA4: { code: 'AltLeft', location: LEFT }, // [USB: 0xe2]
575 | 0xA5: { code: 'AltRight', location: RIGHT }, // [USB: 0xe6]
576 |
577 | 0xA6: { code: 'BrowserBack' }, // [USB: 0x0c/0x0224]
578 | 0xA7: { code: 'BrowserForward' }, // [USB: 0x0c/0x0225]
579 | 0xA8: { code: 'BrowserRefresh' }, // [USB: 0x0c/0x0227]
580 | 0xA9: { code: 'BrowserStop' }, // [USB: 0x0c/0x0226]
581 | 0xAA: { code: 'BrowserSearch' }, // [USB: 0x0c/0x0221]
582 | 0xAB: { code: 'BrowserFavorites' }, // [USB: 0x0c/0x0228]
583 | 0xAC: { code: 'BrowserHome' }, // [USB: 0x0c/0x0222]
584 | 0xAD: { code: 'VolumeMute' }, // [USB: 0x7f]
585 | 0xAE: { code: 'VolumeDown' }, // [USB: 0x81]
586 | 0xAF: { code: 'VolumeUp' }, // [USB: 0x80]
587 |
588 | 0xB0: { code: 'MediaTrackNext' }, // [USB: 0x0c/0x00b5]
589 | 0xB1: { code: 'MediaTrackPrevious' }, // [USB: 0x0c/0x00b6]
590 | 0xB2: { code: 'MediaStop' }, // [USB: 0x0c/0x00b7]
591 | 0xB3: { code: 'MediaPlayPause' }, // [USB: 0x0c/0x00cd]
592 | 0xB4: { code: 'LaunchMail' }, // [USB: 0x0c/0x018a]
593 | 0xB5: { code: 'MediaSelect' },
594 | 0xB6: { code: 'LaunchApp1' },
595 | 0xB7: { code: 'LaunchApp2' },
596 | // 0xB8-0xB9 - reserved
597 | 0xBA: { code: 'Semicolon', keyCap: ';' }, // [USB: 0x33] ;: (US Standard 101)
598 | 0xBB: { code: 'Equal', keyCap: '=' }, // [USB: 0x2e] =+
599 | 0xBC: { code: 'Comma', keyCap: ',' }, // [USB: 0x36] ,<
600 | 0xBD: { code: 'Minus', keyCap: '-' }, // [USB: 0x2d] -_
601 | 0xBE: { code: 'Period', keyCap: '.' }, // [USB: 0x37] .>
602 | 0xBF: { code: 'Slash', keyCap: '/' }, // [USB: 0x38] /? (US Standard 101)
603 |
604 | 0xC0: { code: 'Backquote', keyCap: '`' }, // [USB: 0x35] `~ (US Standard 101)
605 | // 0xC1-0xCF - reserved
606 |
607 | // 0xD0-0xD7 - reserved
608 | // 0xD8-0xDA - unassigned
609 | 0xDB: { code: 'BracketLeft', keyCap: '[' }, // [USB: 0x2f] [{ (US Standard 101)
610 | 0xDC: { code: 'Backslash', keyCap: '\\' }, // [USB: 0x31] \| (US Standard 101)
611 | 0xDD: { code: 'BracketRight', keyCap: ']' }, // [USB: 0x30] ]} (US Standard 101)
612 | 0xDE: { code: 'Quote', keyCap: '\'' }, // [USB: 0x34] '" (US Standard 101)
613 | // 0xDF - miscellaneous/varies
614 |
615 | // 0xE0 - reserved
616 | // 0xE1 - OEM specific
617 | 0xE2: { code: 'IntlBackslash', keyCap: '\\' }, // [USB: 0x64] \| (UK Standard 102)
618 | // 0xE3-0xE4 - OEM specific
619 | 0xE5: { code: 'Process' }, // (Not in D3E)
620 | // 0xE6 - OEM specific
621 | // 0xE7 - VK_PACKET
622 | // 0xE8 - unassigned
623 | // 0xE9-0xEF - OEM specific
624 |
625 | // 0xF0-0xF5 - OEM specific
626 | 0xF6: { code: 'Attn' }, // [USB: 0x9a] (Not in D3E)
627 | 0xF7: { code: 'CrSel' }, // [USB: 0xa3] (Not in D3E)
628 | 0xF8: { code: 'ExSel' }, // [USB: 0xa4] (Not in D3E)
629 | 0xF9: { code: 'EraseEof' }, // (Not in D3E)
630 | 0xFA: { code: 'Play' }, // (Not in D3E)
631 | 0xFB: { code: 'ZoomToggle' }, // (Not in D3E)
632 | // 0xFC - VK_NONAME - reserved
633 | // 0xFD - VK_PA1
634 | 0xFE: { code: 'Clear' } // [USB: 0x9c] (Not in D3E)
635 | };
636 |
637 | // No legacy keyCode, but listed in D3E:
638 |
639 | // code: usb
640 | // 'IntlHash': 0x070032,
641 | // 'IntlRo': 0x070087,
642 | // 'IntlYen': 0x070089,
643 | // 'NumpadBackspace': 0x0700bb,
644 | // 'NumpadClear': 0x0700d8,
645 | // 'NumpadClearEntry': 0x0700d9,
646 | // 'NumpadMemoryAdd': 0x0700d3,
647 | // 'NumpadMemoryClear': 0x0700d2,
648 | // 'NumpadMemoryRecall': 0x0700d1,
649 | // 'NumpadMemoryStore': 0x0700d0,
650 | // 'NumpadMemorySubtract': 0x0700d4,
651 | // 'NumpadParenLeft': 0x0700b6,
652 | // 'NumpadParenRight': 0x0700b7,
653 |
654 | //--------------------------------------------------------------------
655 | //
656 | // Browser/OS Specific Mappings
657 | //
658 | //--------------------------------------------------------------------
659 |
660 | mergeIf(keyCodeToInfoTable,
661 | 'moz', {
662 | 0x3B: { code: 'Semicolon', keyCap: ';' }, // [USB: 0x33] ;: (US Standard 101)
663 | 0x3D: { code: 'Equal', keyCap: '=' }, // [USB: 0x2e] =+
664 | 0x6B: { code: 'Equal', keyCap: '=' }, // [USB: 0x2e] =+
665 | 0x6D: { code: 'Minus', keyCap: '-' }, // [USB: 0x2d] -_
666 | 0xBB: { code: 'NumpadAdd', keyCap: '+', location: NUMPAD }, // [USB: 0x57]
667 | 0xBD: { code: 'NumpadSubtract', keyCap: '-', location: NUMPAD } // [USB: 0x56]
668 | });
669 |
670 | mergeIf(keyCodeToInfoTable,
671 | 'moz-mac', {
672 | 0x0C: { code: 'NumLock', location: NUMPAD }, // [USB: 0x53]
673 | 0xAD: { code: 'Minus', keyCap: '-' } // [USB: 0x2d] -_
674 | });
675 |
676 | mergeIf(keyCodeToInfoTable,
677 | 'moz-win', {
678 | 0xAD: { code: 'Minus', keyCap: '-' } // [USB: 0x2d] -_
679 | });
680 |
681 | mergeIf(keyCodeToInfoTable,
682 | 'chrome-mac', {
683 | 0x5D: { code: 'OSRight', location: RIGHT } // [USB: 0xe7]
684 | });
685 |
686 | // Windows via Bootcamp (!)
687 | if (0) {
688 | mergeIf(keyCodeToInfoTable,
689 | 'chrome-win', {
690 | 0xC0: { code: 'Quote', keyCap: '\'' }, // [USB: 0x34] '" (US Standard 101)
691 | 0xDE: { code: 'Backslash', keyCap: '\\' }, // [USB: 0x31] \| (US Standard 101)
692 | 0xDF: { code: 'Backquote', keyCap: '`' } // [USB: 0x35] `~ (US Standard 101)
693 | });
694 |
695 | mergeIf(keyCodeToInfoTable,
696 | 'ie', {
697 | 0xC0: { code: 'Quote', keyCap: '\'' }, // [USB: 0x34] '" (US Standard 101)
698 | 0xDE: { code: 'Backslash', keyCap: '\\' }, // [USB: 0x31] \| (US Standard 101)
699 | 0xDF: { code: 'Backquote', keyCap: '`' } // [USB: 0x35] `~ (US Standard 101)
700 | });
701 | }
702 |
703 | mergeIf(keyCodeToInfoTable,
704 | 'safari', {
705 | 0x03: { code: 'Enter' }, // [USB: 0x28] old Safari
706 | 0x19: { code: 'Tab' } // [USB: 0x2b] old Safari for Shift+Tab
707 | });
708 |
709 | mergeIf(keyCodeToInfoTable,
710 | 'ios', {
711 | 0x0A: { code: 'Enter', location: STANDARD } // [USB: 0x28]
712 | });
713 |
714 | mergeIf(keyCodeToInfoTable,
715 | 'safari-mac', {
716 | 0x5B: { code: 'OSLeft', location: LEFT }, // [USB: 0xe3]
717 | 0x5D: { code: 'OSRight', location: RIGHT }, // [USB: 0xe7]
718 | 0xE5: { code: 'KeyQ', keyCap: 'Q' } // [USB: 0x14] On alternate presses, Ctrl+Q sends this
719 | });
720 |
721 | //--------------------------------------------------------------------
722 | //
723 | // Identifier Mappings
724 | //
725 | //--------------------------------------------------------------------
726 |
727 | // Cases where newer-ish browsers send keyIdentifier which can be
728 | // used to disambiguate keys.
729 |
730 | // keyIdentifierTable[keyIdentifier] -> keyInfo
731 |
732 | var keyIdentifierTable = {};
733 | if ('cros' === os) {
734 | keyIdentifierTable['U+00A0'] = { code: 'ShiftLeft', location: LEFT };
735 | keyIdentifierTable['U+00A1'] = { code: 'ShiftRight', location: RIGHT };
736 | keyIdentifierTable['U+00A2'] = { code: 'ControlLeft', location: LEFT };
737 | keyIdentifierTable['U+00A3'] = { code: 'ControlRight', location: RIGHT };
738 | keyIdentifierTable['U+00A4'] = { code: 'AltLeft', location: LEFT };
739 | keyIdentifierTable['U+00A5'] = { code: 'AltRight', location: RIGHT };
740 | }
741 | if ('chrome-mac' === browser_os) {
742 | keyIdentifierTable['U+0010'] = { code: 'ContextMenu' };
743 | }
744 | if ('safari-mac' === browser_os) {
745 | keyIdentifierTable['U+0010'] = { code: 'ContextMenu' };
746 | }
747 | if ('ios' === os) {
748 | // These only generate keyup events
749 | keyIdentifierTable['U+0010'] = { code: 'Function' };
750 |
751 | keyIdentifierTable['U+001C'] = { code: 'ArrowLeft' };
752 | keyIdentifierTable['U+001D'] = { code: 'ArrowRight' };
753 | keyIdentifierTable['U+001E'] = { code: 'ArrowUp' };
754 | keyIdentifierTable['U+001F'] = { code: 'ArrowDown' };
755 |
756 | keyIdentifierTable['U+0001'] = { code: 'Home' }; // [USB: 0x4a] Fn + ArrowLeft
757 | keyIdentifierTable['U+0004'] = { code: 'End' }; // [USB: 0x4d] Fn + ArrowRight
758 | keyIdentifierTable['U+000B'] = { code: 'PageUp' }; // [USB: 0x4b] Fn + ArrowUp
759 | keyIdentifierTable['U+000C'] = { code: 'PageDown' }; // [USB: 0x4e] Fn + ArrowDown
760 | }
761 |
762 | //--------------------------------------------------------------------
763 | //
764 | // Location Mappings
765 | //
766 | //--------------------------------------------------------------------
767 |
768 | // Cases where newer-ish browsers send location/keyLocation which
769 | // can be used to disambiguate keys.
770 |
771 | // locationTable[location][keyCode] -> keyInfo
772 | var locationTable = [];
773 | locationTable[LEFT] = {
774 | 0x10: { code: 'ShiftLeft', location: LEFT }, // [USB: 0xe1]
775 | 0x11: { code: 'ControlLeft', location: LEFT }, // [USB: 0xe0]
776 | 0x12: { code: 'AltLeft', location: LEFT } // [USB: 0xe2]
777 | };
778 | locationTable[RIGHT] = {
779 | 0x10: { code: 'ShiftRight', location: RIGHT }, // [USB: 0xe5]
780 | 0x11: { code: 'ControlRight', location: RIGHT }, // [USB: 0xe4]
781 | 0x12: { code: 'AltRight', location: RIGHT } // [USB: 0xe6]
782 | };
783 | locationTable[NUMPAD] = {
784 | 0x0D: { code: 'NumpadEnter', location: NUMPAD } // [USB: 0x58]
785 | };
786 |
787 | mergeIf(locationTable[NUMPAD], 'moz', {
788 | 0x6D: { code: 'NumpadSubtract', location: NUMPAD }, // [USB: 0x56]
789 | 0x6B: { code: 'NumpadAdd', location: NUMPAD } // [USB: 0x57]
790 | });
791 | mergeIf(locationTable[LEFT], 'moz-mac', {
792 | 0xE0: { code: 'OSLeft', location: LEFT } // [USB: 0xe3]
793 | });
794 | mergeIf(locationTable[RIGHT], 'moz-mac', {
795 | 0xE0: { code: 'OSRight', location: RIGHT } // [USB: 0xe7]
796 | });
797 | mergeIf(locationTable[RIGHT], 'moz-win', {
798 | 0x5B: { code: 'OSRight', location: RIGHT } // [USB: 0xe7]
799 | });
800 |
801 |
802 | mergeIf(locationTable[RIGHT], 'mac', {
803 | 0x5D: { code: 'OSRight', location: RIGHT } // [USB: 0xe7]
804 | });
805 |
806 | mergeIf(locationTable[NUMPAD], 'chrome-mac', {
807 | 0x0C: { code: 'NumLock', location: NUMPAD } // [USB: 0x53]
808 | });
809 |
810 | mergeIf(locationTable[NUMPAD], 'safari-mac', {
811 | 0x0C: { code: 'NumLock', location: NUMPAD }, // [USB: 0x53]
812 | 0xBB: { code: 'NumpadAdd', location: NUMPAD }, // [USB: 0x57]
813 | 0xBD: { code: 'NumpadSubtract', location: NUMPAD }, // [USB: 0x56]
814 | 0xBE: { code: 'NumpadDecimal', location: NUMPAD }, // [USB: 0x63]
815 | 0xBF: { code: 'NumpadDivide', location: NUMPAD } // [USB: 0x54]
816 | });
817 |
818 |
819 | //--------------------------------------------------------------------
820 | //
821 | // Key Values
822 | //
823 | //--------------------------------------------------------------------
824 |
825 | // Mapping from `code` values to `key` values. Values defined at:
826 | // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3Events-key.html
827 | // Entries are only provided when `key` differs from `code`. If
828 | // printable, `shiftKey` has the shifted printable character. This
829 | // assumes US Standard 101 layout
830 |
831 | var codeToKeyTable = {
832 | // Modifier Keys
833 | ShiftLeft: { key: 'Shift' },
834 | ShiftRight: { key: 'Shift' },
835 | ControlLeft: { key: 'Control' },
836 | ControlRight: { key: 'Control' },
837 | AltLeft: { key: 'Alt' },
838 | AltRight: { key: 'Alt' },
839 | OSLeft: { key: 'OS' },
840 | OSRight: { key: 'OS' },
841 |
842 | // Whitespace Keys
843 | NumpadEnter: { key: 'Enter' },
844 | Space: { key: ' ' },
845 |
846 | // Printable Keys
847 | Digit0: { key: '0', shiftKey: ')' },
848 | Digit1: { key: '1', shiftKey: '!' },
849 | Digit2: { key: '2', shiftKey: '@' },
850 | Digit3: { key: '3', shiftKey: '#' },
851 | Digit4: { key: '4', shiftKey: '$' },
852 | Digit5: { key: '5', shiftKey: '%' },
853 | Digit6: { key: '6', shiftKey: '^' },
854 | Digit7: { key: '7', shiftKey: '&' },
855 | Digit8: { key: '8', shiftKey: '*' },
856 | Digit9: { key: '9', shiftKey: '(' },
857 | KeyA: { key: 'a', shiftKey: 'A' },
858 | KeyB: { key: 'b', shiftKey: 'B' },
859 | KeyC: { key: 'c', shiftKey: 'C' },
860 | KeyD: { key: 'd', shiftKey: 'D' },
861 | KeyE: { key: 'e', shiftKey: 'E' },
862 | KeyF: { key: 'f', shiftKey: 'F' },
863 | KeyG: { key: 'g', shiftKey: 'G' },
864 | KeyH: { key: 'h', shiftKey: 'H' },
865 | KeyI: { key: 'i', shiftKey: 'I' },
866 | KeyJ: { key: 'j', shiftKey: 'J' },
867 | KeyK: { key: 'k', shiftKey: 'K' },
868 | KeyL: { key: 'l', shiftKey: 'L' },
869 | KeyM: { key: 'm', shiftKey: 'M' },
870 | KeyN: { key: 'n', shiftKey: 'N' },
871 | KeyO: { key: 'o', shiftKey: 'O' },
872 | KeyP: { key: 'p', shiftKey: 'P' },
873 | KeyQ: { key: 'q', shiftKey: 'Q' },
874 | KeyR: { key: 'r', shiftKey: 'R' },
875 | KeyS: { key: 's', shiftKey: 'S' },
876 | KeyT: { key: 't', shiftKey: 'T' },
877 | KeyU: { key: 'u', shiftKey: 'U' },
878 | KeyV: { key: 'v', shiftKey: 'V' },
879 | KeyW: { key: 'w', shiftKey: 'W' },
880 | KeyX: { key: 'x', shiftKey: 'X' },
881 | KeyY: { key: 'y', shiftKey: 'Y' },
882 | KeyZ: { key: 'z', shiftKey: 'Z' },
883 | Numpad0: { key: '0' },
884 | Numpad1: { key: '1' },
885 | Numpad2: { key: '2' },
886 | Numpad3: { key: '3' },
887 | Numpad4: { key: '4' },
888 | Numpad5: { key: '5' },
889 | Numpad6: { key: '6' },
890 | Numpad7: { key: '7' },
891 | Numpad8: { key: '8' },
892 | Numpad9: { key: '9' },
893 | NumpadMultiply: { key: '*' },
894 | NumpadAdd: { key: '+' },
895 | NumpadComma: { key: ',' },
896 | NumpadSubtract: { key: '-' },
897 | NumpadDecimal: { key: '.' },
898 | NumpadDivide: { key: '/' },
899 | Semicolon: { key: ';', shiftKey: ':' },
900 | Equal: { key: '=', shiftKey: '+' },
901 | Comma: { key: ',', shiftKey: '<' },
902 | Minus: { key: '-', shiftKey: '_' },
903 | Period: { key: '.', shiftKey: '>' },
904 | Slash: { key: '/', shiftKey: '?' },
905 | Backquote: { key: '`', shiftKey: '~' },
906 | BracketLeft: { key: '[', shiftKey: '{' },
907 | Backslash: { key: '\\', shiftKey: '|' },
908 | BracketRight: { key: ']', shiftKey: '}' },
909 | Quote: { key: '\'', shiftKey: '"' },
910 | IntlBackslash: { key: '\\', shiftKey: '|' }
911 | };
912 |
913 | mergeIf(codeToKeyTable, 'mac', {
914 | OSLeft: { key: 'Meta' },
915 | OSRight: { key: 'Meta' }
916 | });
917 |
918 | // Corrections for 'key' names in older browsers (e.g. FF36-)
919 | // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key#Key_values
920 | var keyFixTable = {
921 | Esc: 'Escape',
922 | Nonconvert: 'NonConvert',
923 | Left: 'ArrowLeft',
924 | Up: 'ArrowUp',
925 | Right: 'ArrowRight',
926 | Down: 'ArrowDown',
927 | Del: 'Delete',
928 | Menu: 'ContextMenu',
929 | MediaNextTrack: 'MediaTrackNext',
930 | MediaPreviousTrack: 'MediaTrackPrevious',
931 | SelectMedia: 'MediaSelect',
932 | HalfWidth: 'Hankaku',
933 | FullWidth: 'Zenkaku',
934 | RomanCharacters: 'Romaji',
935 | Crsel: 'CrSel',
936 | Exsel: 'ExSel',
937 | Zoom: 'ZoomToggle'
938 | };
939 |
940 | //--------------------------------------------------------------------
941 | //
942 | // Exported Functions
943 | //
944 | //--------------------------------------------------------------------
945 |
946 |
947 | var codeTable = remap(keyCodeToInfoTable, 'code');
948 |
949 | try {
950 | var nativeLocation = nativeKeyboardEvent && ('location' in new KeyboardEvent(''));
951 | } catch (_) {}
952 |
953 | function keyInfoForEvent(event) {
954 | var keyCode = 'keyCode' in event ? event.keyCode : 'which' in event ? event.which : 0;
955 |
956 | var keyInfo = (function(){
957 | if (nativeLocation || 'keyLocation' in event) {
958 | var location = nativeLocation ? event.location : event.keyLocation;
959 | if (location && keyCode in locationTable[location]) {
960 | return locationTable[location][keyCode];
961 | }
962 | }
963 | if ('keyIdentifier' in event && event.keyIdentifier in keyIdentifierTable) {
964 | return keyIdentifierTable[event.keyIdentifier];
965 | }
966 | if (keyCode in keyCodeToInfoTable) {
967 | return keyCodeToInfoTable[keyCode];
968 | }
969 | return null;
970 | }());
971 |
972 | // TODO: Track these down and move to general tables
973 | if (0) {
974 | // TODO: Map these for newerish browsers?
975 | // TODO: iOS only?
976 | // TODO: Override with more common keyIdentifier name?
977 | switch (event.keyIdentifier) {
978 | case 'U+0010': keyInfo = { code: 'Function' }; break;
979 | case 'U+001C': keyInfo = { code: 'ArrowLeft' }; break;
980 | case 'U+001D': keyInfo = { code: 'ArrowRight' }; break;
981 | case 'U+001E': keyInfo = { code: 'ArrowUp' }; break;
982 | case 'U+001F': keyInfo = { code: 'ArrowDown' }; break;
983 | }
984 | }
985 |
986 | if (!keyInfo)
987 | return null;
988 |
989 | var key = (function() {
990 | var entry = codeToKeyTable[keyInfo.code];
991 | if (!entry) return keyInfo.code;
992 | return (event.shiftKey && 'shiftKey' in entry) ? entry.shiftKey : entry.key;
993 | }());
994 |
995 | return {
996 | code: keyInfo.code,
997 | key: key,
998 | location: keyInfo.location,
999 | keyCap: keyInfo.keyCap
1000 | };
1001 | }
1002 |
1003 | function queryKeyCap(code, locale) {
1004 | code = String(code);
1005 | if (!codeTable.hasOwnProperty(code)) return 'Undefined';
1006 | if (locale && String(locale).toLowerCase() !== 'en-us') throw Error('Unsupported locale');
1007 | var keyInfo = codeTable[code];
1008 | return keyInfo.keyCap || keyInfo.code || 'Undefined';
1009 | }
1010 |
1011 | if ('KeyboardEvent' in global && 'defineProperty' in Object) {
1012 | (function() {
1013 | function define(o, p, v) {
1014 | if (p in o) return;
1015 | Object.defineProperty(o, p, v);
1016 | }
1017 |
1018 | define(KeyboardEvent.prototype, 'code', { get: function() {
1019 | var keyInfo = keyInfoForEvent(this);
1020 | return keyInfo ? keyInfo.code : '';
1021 | }});
1022 |
1023 | // Fix for nonstandard `key` values (FF36-)
1024 | if ('key' in KeyboardEvent.prototype) {
1025 | var desc = Object.getOwnPropertyDescriptor(KeyboardEvent.prototype, 'key');
1026 | Object.defineProperty(KeyboardEvent.prototype, 'key', { get: function() {
1027 | var key = desc.get.call(this);
1028 | return keyFixTable.hasOwnProperty(key) ? keyFixTable[key] : key;
1029 | }});
1030 | }
1031 |
1032 | define(KeyboardEvent.prototype, 'key', { get: function() {
1033 | var keyInfo = keyInfoForEvent(this);
1034 | return (keyInfo && 'key' in keyInfo) ? keyInfo.key : 'Unidentified';
1035 | }});
1036 |
1037 | define(KeyboardEvent.prototype, 'location', { get: function() {
1038 | var keyInfo = keyInfoForEvent(this);
1039 | return (keyInfo && 'location' in keyInfo) ? keyInfo.location : STANDARD;
1040 | }});
1041 |
1042 | define(KeyboardEvent.prototype, 'locale', { get: function() {
1043 | return '';
1044 | }});
1045 | }());
1046 | }
1047 |
1048 | if (!('queryKeyCap' in global.KeyboardEvent))
1049 | global.KeyboardEvent.queryKeyCap = queryKeyCap;
1050 |
1051 | // Helper for IE8-
1052 | global.identifyKey = function(event) {
1053 | if ('code' in event)
1054 | return;
1055 |
1056 | var keyInfo = keyInfoForEvent(event);
1057 | event.code = keyInfo ? keyInfo.code : '';
1058 | event.key = (keyInfo && 'key' in keyInfo) ? keyInfo.key : 'Unidentified';
1059 | event.location = ('location' in event) ? event.location :
1060 | ('keyLocation' in event) ? event.keyLocation :
1061 | (keyInfo && 'location' in keyInfo) ? keyInfo.location : STANDARD;
1062 | event.locale = '';
1063 | };
1064 |
1065 | } (window));
1066 |
1067 |
1068 | /***/ }
1069 | /******/ ]);
--------------------------------------------------------------------------------
/dist/aframe-keyboard-controls.min.js:
--------------------------------------------------------------------------------
1 | !function(e){function o(i){if(t[i])return t[i].exports;var a=t[i]={exports:{},id:i,loaded:!1};return e[i].call(a.exports,a,a.exports,o),a.loaded=!0,a.exports}var t={};return o.m=e,o.c=t,o.p="",o(0)}([function(e,o,t){!function(e){return e?void(e.aframeCore||e).registerComponent("keyboard-controls",t(1)):void console.error("Component attempted to register before AFRAME was available.")}(window.AFRAME)},function(e,o,t){t(2);var i=200,a="__keyboard-controls-proxy",c=window.KeyboardEvent;e.exports={schema:{easing:{"default":20},acceleration:{"default":65},angularAcceleration:{"default":Math.PI/4},enabled:{"default":!0},mode:{"default":"default",oneOf:["default","fly","fps"]},rollAxis:{"default":"z",oneOf:["x","y","z"]},pitchAxis:{"default":"x",oneOf:["x","y","z"]},rollAxisInverted:{"default":!1},rollAxisEnabled:{"default":!0},pitchAxisInverted:{"default":!1},pitchAxisEnabled:{"default":!0},yawAxisInverted:{"default":!1},debug:{"default":!1}},init:function(){this.velocity=new THREE.Vector3,this.angularVelocity=0,this.localKeys={},this.listeners={keydown:this.onKeyDown.bind(this),keyup:this.onKeyUp.bind(this),blur:this.onBlur.bind(this)},this.attachEventListeners()},tick:function(){var e=new THREE.Vector3(0,1,0),o=new THREE.Euler(0,0,0,"YXZ");return function(t,a){var c,n=this.data,d=n.acceleration,r=n.angularAcceleration,y=n.easing,l=this.velocity,s=this.getKeys(),u=n.pitchAxis,p=n.rollAxis,k=n.pitchAxisInverted?-1:1,f=n.rollAxisInverted?-1:1,h=n.yawAxisInverted?1:-1,K=this.el,m="fps"===n.mode?["KeyQ","ArrowLeft"]:["KeyA","ArrowLeft"],C="fps"===n.mode?["KeyE","ArrowRight"]:["KeyD","ArrowRight"];if(isNaN(a)||a>i)return l[u]=0,l[p]=0,void(this.angularVelocity=0);l[u]-=l[u]*y*a/1e3,l[p]-=l[p]*y*a/1e3,this.angularVelocity-=this.angularVelocity*y*a/1e3;var g=K.getAttribute("position");n.enabled&&(n.pitchAxisEnabled&&((s[m[0]]||s[m[1]])&&(l[u]-=k*d*a/1e3),(s[C[0]]||s[C[1]])&&(l[u]+=k*d*a/1e3)),n.rollAxisEnabled&&((s.KeyW||s.ArrowUp)&&(l[p]-=f*d*a/1e3),(s.KeyS||s.ArrowDown)&&(l[p]+=f*d*a/1e3)),"fps"===n.mode&&(s.KeyA&&(this.angularVelocity-=h*r*a/1e3),s.KeyD&&(this.angularVelocity+=h*r*a/1e3))),"fps"===n.mode&&(this.rotateOnAxis(o,e,this.angularVelocity),K.setAttribute("rotation",{x:THREE.Math.radToDeg(o.x),y:THREE.Math.radToDeg(o.y),z:THREE.Math.radToDeg(o.z)})),c=this.getMovementVector(a),K.object3D.translateX(c.x),K.object3D.translateY(c.y),K.object3D.translateZ(c.z),K.setAttribute("position",{x:g.x+c.x,y:g.y+c.y,z:g.z+c.z})}}(),rotateOnAxis:function(){var e=new THREE.Quaternion,o=new THREE.Quaternion;return function(t,i,a){e.setFromAxisAngle(i,a),o.setFromEuler(t),o.multiply(e),t.setFromQuaternion(o,t.order)}}(),getMovementVector:function(){var e=new THREE.Vector3(0,0,0),o=new THREE.Euler(0,0,0,"YXZ");return function(t){var i=this.velocity,a=this.el.getAttribute("rotation");return e.copy(i),e.multiplyScalar(t/1e3),a?("fly"!==this.data.mode&&(a.x=0),o.set(THREE.Math.degToRad(a.x),THREE.Math.degToRad(a.y),0),e.applyEuler(o),e):e}}(),play:function(){this.attachEventListeners()},pause:function(){this.removeEventListeners()},remove:function(){this.pause()},attachEventListeners:function(){window.addEventListener("keydown",this.listeners.keydown,!1),window.addEventListener("keyup",this.listeners.keyup,!1),window.addEventListener("blur",this.listeners.blur,!1)},removeEventListeners:function(){window.removeEventListener("keydown",this.listeners.keydown),window.removeEventListener("keyup",this.listeners.keyup),window.removeEventListener("blur",this.listeners.blur)},onKeyDown:function(e){this.localKeys[e.code]=!0,this.emit(e)},onKeyUp:function(e){delete this.localKeys[e.code],this.emit(e)},onBlur:function(){for(var e in this.localKeys)this.localKeys.hasOwnProperty(e)&&delete this.localKeys[e]},emit:function(e){a in e&&this.el.emit(e.type,e),this.el.emit(e.type+":"+e.code,new c(e.type,e)),this.data.debug&&console.log(e.type+":"+e.code)},isPressed:function(e){return e in this.getKeys()},getKeys:function(){return this.isProxied()?this.el.sceneEl.components["proxy-controls"].getKeyboard():this.localKeys},isProxied:function(){var e=this.el.sceneEl.components["proxy-controls"];return e&&e.isConnected()}}},function(e,o){!function(e){function o(e,o){return-1!==String(e).indexOf(o)}function t(e,o,t){(p===o||u===o||s===o)&&Object.keys(t).forEach(function(o){e[o]=t[o]})}function i(e,o){var t={};return Object.keys(e).forEach(function(i){var a=e[i];o in a&&(t[a[o]]=a)}),t}function a(e){var o="keyCode"in e?e.keyCode:"which"in e?e.which:0,t=function(){if(g||"keyLocation"in e){var t=g?e.location:e.keyLocation;if(t&&o in h[t])return h[t][o]}return"keyIdentifier"in e&&e.keyIdentifier in f?f[e.keyIdentifier]:o in k?k[o]:null}();if(!t)return null;var i=function(){var o=K[t.code];return o?e.shiftKey&&"shiftKey"in o?o.shiftKey:o.key:t.code}();return{code:t.code,key:i,location:t.location,keyCap:t.keyCap}}function c(e,o){if(e=String(e),!C.hasOwnProperty(e))return"Undefined";if(o&&"en-us"!==String(o).toLowerCase())throw Error("Unsupported locale");var t=C[e];return t.keyCap||t.code||"Undefined"}var n="KeyboardEvent"in e;n||(e.KeyboardEvent=function(){throw TypeError("Illegal constructor")}),e.KeyboardEvent.DOM_KEY_LOCATION_STANDARD=0,e.KeyboardEvent.DOM_KEY_LOCATION_LEFT=1,e.KeyboardEvent.DOM_KEY_LOCATION_RIGHT=2,e.KeyboardEvent.DOM_KEY_LOCATION_NUMPAD=3;var d=window.KeyboardEvent.DOM_KEY_LOCATION_STANDARD,r=window.KeyboardEvent.DOM_KEY_LOCATION_LEFT,y=window.KeyboardEvent.DOM_KEY_LOCATION_RIGHT,l=window.KeyboardEvent.DOM_KEY_LOCATION_NUMPAD,s=function(){return o(navigator.platform,"Win")?"win":o(navigator.platform,"Mac")?"mac":o(navigator.platform,"CrOS")?"cros":o(navigator.platform,"Linux")?"linux":o(navigator.userAgent,"iPad")||o(navigator.platform,"iPod")||o(navigator.platform,"iPhone")?"ios":""}(),u=function(){return o(navigator.userAgent,"Chrome/")?"chrome":o(navigator.vendor,"Apple")?"safari":o(navigator.userAgent,"MSIE")?"ie":o(navigator.userAgent,"Gecko/")?"moz":o(navigator.userAgent,"Opera/")?"opera":""}(),p=u+"-"+s,k={3:{code:"Cancel"},6:{code:"Help"},8:{code:"Backspace"},9:{code:"Tab"},12:{code:"Clear"},13:{code:"Enter"},16:{code:"Shift"},17:{code:"Control"},18:{code:"Alt"},19:{code:"Pause"},20:{code:"CapsLock"},21:{code:"KanaMode"},22:{code:"HangulMode"},23:{code:"JunjaMode"},24:{code:"FinalMode"},25:{code:"KanjiMode"},27:{code:"Escape"},28:{code:"Convert"},29:{code:"NonConvert"},30:{code:"Accept"},31:{code:"ModeChange"},32:{code:"Space"},33:{code:"PageUp"},34:{code:"PageDown"},35:{code:"End"},36:{code:"Home"},37:{code:"ArrowLeft"},38:{code:"ArrowUp"},39:{code:"ArrowRight"},40:{code:"ArrowDown"},41:{code:"Select"},42:{code:"Print"},43:{code:"Execute"},44:{code:"PrintScreen"},45:{code:"Insert"},46:{code:"Delete"},47:{code:"Help"},48:{code:"Digit0",keyCap:"0"},49:{code:"Digit1",keyCap:"1"},50:{code:"Digit2",keyCap:"2"},51:{code:"Digit3",keyCap:"3"},52:{code:"Digit4",keyCap:"4"},53:{code:"Digit5",keyCap:"5"},54:{code:"Digit6",keyCap:"6"},55:{code:"Digit7",keyCap:"7"},56:{code:"Digit8",keyCap:"8"},57:{code:"Digit9",keyCap:"9"},65:{code:"KeyA",keyCap:"a"},66:{code:"KeyB",keyCap:"b"},67:{code:"KeyC",keyCap:"c"},68:{code:"KeyD",keyCap:"d"},69:{code:"KeyE",keyCap:"e"},70:{code:"KeyF",keyCap:"f"},71:{code:"KeyG",keyCap:"g"},72:{code:"KeyH",keyCap:"h"},73:{code:"KeyI",keyCap:"i"},74:{code:"KeyJ",keyCap:"j"},75:{code:"KeyK",keyCap:"k"},76:{code:"KeyL",keyCap:"l"},77:{code:"KeyM",keyCap:"m"},78:{code:"KeyN",keyCap:"n"},79:{code:"KeyO",keyCap:"o"},80:{code:"KeyP",keyCap:"p"},81:{code:"KeyQ",keyCap:"q"},82:{code:"KeyR",keyCap:"r"},83:{code:"KeyS",keyCap:"s"},84:{code:"KeyT",keyCap:"t"},85:{code:"KeyU",keyCap:"u"},86:{code:"KeyV",keyCap:"v"},87:{code:"KeyW",keyCap:"w"},88:{code:"KeyX",keyCap:"x"},89:{code:"KeyY",keyCap:"y"},90:{code:"KeyZ",keyCap:"z"},91:{code:"OSLeft",location:r},92:{code:"OSRight",location:y},93:{code:"ContextMenu"},95:{code:"Standby"},96:{code:"Numpad0",keyCap:"0",location:l},97:{code:"Numpad1",keyCap:"1",location:l},98:{code:"Numpad2",keyCap:"2",location:l},99:{code:"Numpad3",keyCap:"3",location:l},100:{code:"Numpad4",keyCap:"4",location:l},101:{code:"Numpad5",keyCap:"5",location:l},102:{code:"Numpad6",keyCap:"6",location:l},103:{code:"Numpad7",keyCap:"7",location:l},104:{code:"Numpad8",keyCap:"8",location:l},105:{code:"Numpad9",keyCap:"9",location:l},106:{code:"NumpadMultiply",keyCap:"*",location:l},107:{code:"NumpadAdd",keyCap:"+",location:l},108:{code:"NumpadComma",keyCap:",",location:l},109:{code:"NumpadSubtract",keyCap:"-",location:l},110:{code:"NumpadDecimal",keyCap:".",location:l},111:{code:"NumpadDivide",keyCap:"/",location:l},112:{code:"F1"},113:{code:"F2"},114:{code:"F3"},115:{code:"F4"},116:{code:"F5"},117:{code:"F6"},118:{code:"F7"},119:{code:"F8"},120:{code:"F9"},121:{code:"F10"},122:{code:"F11"},123:{code:"F12"},124:{code:"F13"},125:{code:"F14"},126:{code:"F15"},127:{code:"F16"},128:{code:"F17"},129:{code:"F18"},130:{code:"F19"},131:{code:"F20"},132:{code:"F21"},133:{code:"F22"},134:{code:"F23"},135:{code:"F24"},144:{code:"NumLock",location:l},145:{code:"ScrollLock"},160:{code:"ShiftLeft",location:r},161:{code:"ShiftRight",location:y},162:{code:"ControlLeft",location:r},163:{code:"ControlRight",location:y},164:{code:"AltLeft",location:r},165:{code:"AltRight",location:y},166:{code:"BrowserBack"},167:{code:"BrowserForward"},168:{code:"BrowserRefresh"},169:{code:"BrowserStop"},170:{code:"BrowserSearch"},171:{code:"BrowserFavorites"},172:{code:"BrowserHome"},173:{code:"VolumeMute"},174:{code:"VolumeDown"},175:{code:"VolumeUp"},176:{code:"MediaTrackNext"},177:{code:"MediaTrackPrevious"},178:{code:"MediaStop"},179:{code:"MediaPlayPause"},180:{code:"LaunchMail"},181:{code:"MediaSelect"},182:{code:"LaunchApp1"},183:{code:"LaunchApp2"},186:{code:"Semicolon",keyCap:";"},187:{code:"Equal",keyCap:"="},188:{code:"Comma",keyCap:","},189:{code:"Minus",keyCap:"-"},190:{code:"Period",keyCap:"."},191:{code:"Slash",keyCap:"/"},192:{code:"Backquote",keyCap:"`"},219:{code:"BracketLeft",keyCap:"["},220:{code:"Backslash",keyCap:"\\"},221:{code:"BracketRight",keyCap:"]"},222:{code:"Quote",keyCap:"'"},226:{code:"IntlBackslash",keyCap:"\\"},229:{code:"Process"},246:{code:"Attn"},247:{code:"CrSel"},248:{code:"ExSel"},249:{code:"EraseEof"},250:{code:"Play"},251:{code:"ZoomToggle"},254:{code:"Clear"}};t(k,"moz",{59:{code:"Semicolon",keyCap:";"},61:{code:"Equal",keyCap:"="},107:{code:"Equal",keyCap:"="},109:{code:"Minus",keyCap:"-"},187:{code:"NumpadAdd",keyCap:"+",location:l},189:{code:"NumpadSubtract",keyCap:"-",location:l}}),t(k,"moz-mac",{12:{code:"NumLock",location:l},173:{code:"Minus",keyCap:"-"}}),t(k,"moz-win",{173:{code:"Minus",keyCap:"-"}}),t(k,"chrome-mac",{93:{code:"OSRight",location:y}}),t(k,"safari",{3:{code:"Enter"},25:{code:"Tab"}}),t(k,"ios",{10:{code:"Enter",location:d}}),t(k,"safari-mac",{91:{code:"OSLeft",location:r},93:{code:"OSRight",location:y},229:{code:"KeyQ",keyCap:"Q"}});var f={};"cros"===s&&(f["U+00A0"]={code:"ShiftLeft",location:r},f["U+00A1"]={code:"ShiftRight",location:y},f["U+00A2"]={code:"ControlLeft",location:r},f["U+00A3"]={code:"ControlRight",location:y},f["U+00A4"]={code:"AltLeft",location:r},f["U+00A5"]={code:"AltRight",location:y}),"chrome-mac"===p&&(f["U+0010"]={code:"ContextMenu"}),"safari-mac"===p&&(f["U+0010"]={code:"ContextMenu"}),"ios"===s&&(f["U+0010"]={code:"Function"},f["U+001C"]={code:"ArrowLeft"},f["U+001D"]={code:"ArrowRight"},f["U+001E"]={code:"ArrowUp"},f["U+001F"]={code:"ArrowDown"},f["U+0001"]={code:"Home"},f["U+0004"]={code:"End"},f["U+000B"]={code:"PageUp"},f["U+000C"]={code:"PageDown"});var h=[];h[r]={16:{code:"ShiftLeft",location:r},17:{code:"ControlLeft",location:r},18:{code:"AltLeft",location:r}},h[y]={16:{code:"ShiftRight",location:y},17:{code:"ControlRight",location:y},18:{code:"AltRight",location:y}},h[l]={13:{code:"NumpadEnter",location:l}},t(h[l],"moz",{109:{code:"NumpadSubtract",location:l},107:{code:"NumpadAdd",location:l}}),t(h[r],"moz-mac",{224:{code:"OSLeft",location:r}}),t(h[y],"moz-mac",{224:{code:"OSRight",location:y}}),t(h[y],"moz-win",{91:{code:"OSRight",location:y}}),t(h[y],"mac",{93:{code:"OSRight",location:y}}),t(h[l],"chrome-mac",{12:{code:"NumLock",location:l}}),t(h[l],"safari-mac",{12:{code:"NumLock",location:l},187:{code:"NumpadAdd",location:l},189:{code:"NumpadSubtract",location:l},190:{code:"NumpadDecimal",location:l},191:{code:"NumpadDivide",location:l}});var K={ShiftLeft:{key:"Shift"},ShiftRight:{key:"Shift"},ControlLeft:{key:"Control"},ControlRight:{key:"Control"},AltLeft:{key:"Alt"},AltRight:{key:"Alt"},OSLeft:{key:"OS"},OSRight:{key:"OS"},NumpadEnter:{key:"Enter"},Space:{key:" "},Digit0:{key:"0",shiftKey:")"},Digit1:{key:"1",shiftKey:"!"},Digit2:{key:"2",shiftKey:"@"},Digit3:{key:"3",shiftKey:"#"},Digit4:{key:"4",shiftKey:"$"},Digit5:{key:"5",shiftKey:"%"},Digit6:{key:"6",shiftKey:"^"},Digit7:{key:"7",shiftKey:"&"},Digit8:{key:"8",shiftKey:"*"},Digit9:{key:"9",shiftKey:"("},KeyA:{key:"a",shiftKey:"A"},KeyB:{key:"b",shiftKey:"B"},KeyC:{key:"c",shiftKey:"C"},KeyD:{key:"d",shiftKey:"D"},KeyE:{key:"e",shiftKey:"E"},KeyF:{key:"f",shiftKey:"F"},KeyG:{key:"g",shiftKey:"G"},KeyH:{key:"h",shiftKey:"H"},KeyI:{key:"i",shiftKey:"I"},KeyJ:{key:"j",shiftKey:"J"},KeyK:{key:"k",shiftKey:"K"},KeyL:{key:"l",shiftKey:"L"},KeyM:{key:"m",shiftKey:"M"},KeyN:{key:"n",shiftKey:"N"},KeyO:{key:"o",shiftKey:"O"},KeyP:{key:"p",shiftKey:"P"},KeyQ:{key:"q",shiftKey:"Q"},KeyR:{key:"r",shiftKey:"R"},KeyS:{key:"s",shiftKey:"S"},KeyT:{key:"t",shiftKey:"T"},KeyU:{key:"u",shiftKey:"U"},KeyV:{key:"v",shiftKey:"V"},KeyW:{key:"w",shiftKey:"W"},KeyX:{key:"x",shiftKey:"X"},KeyY:{key:"y",shiftKey:"Y"},KeyZ:{key:"z",shiftKey:"Z"},Numpad0:{key:"0"},Numpad1:{key:"1"},Numpad2:{key:"2"},Numpad3:{key:"3"},Numpad4:{key:"4"},Numpad5:{key:"5"},Numpad6:{key:"6"},Numpad7:{key:"7"},Numpad8:{key:"8"},Numpad9:{key:"9"},NumpadMultiply:{key:"*"},NumpadAdd:{key:"+"},NumpadComma:{key:","},NumpadSubtract:{key:"-"},NumpadDecimal:{key:"."},NumpadDivide:{key:"/"},Semicolon:{key:";",shiftKey:":"},Equal:{key:"=",shiftKey:"+"},Comma:{key:",",shiftKey:"<"},Minus:{key:"-",shiftKey:"_"},Period:{key:".",shiftKey:">"},Slash:{key:"/",shiftKey:"?"},Backquote:{key:"`",shiftKey:"~"},BracketLeft:{key:"[",shiftKey:"{"},Backslash:{key:"\\",shiftKey:"|"},BracketRight:{key:"]",shiftKey:"}"},Quote:{key:"'",shiftKey:'"'},IntlBackslash:{key:"\\",shiftKey:"|"}};t(K,"mac",{OSLeft:{key:"Meta"},OSRight:{key:"Meta"}});var m={Esc:"Escape",Nonconvert:"NonConvert",Left:"ArrowLeft",Up:"ArrowUp",Right:"ArrowRight",Down:"ArrowDown",Del:"Delete",Menu:"ContextMenu",MediaNextTrack:"MediaTrackNext",MediaPreviousTrack:"MediaTrackPrevious",SelectMedia:"MediaSelect",HalfWidth:"Hankaku",FullWidth:"Zenkaku",RomanCharacters:"Romaji",Crsel:"CrSel",Exsel:"ExSel",Zoom:"ZoomToggle"},C=i(k,"code");try{var g=n&&"location"in new KeyboardEvent("")}catch(v){}"KeyboardEvent"in e&&"defineProperty"in Object&&!function(){function e(e,o,t){o in e||Object.defineProperty(e,o,t)}if(e(KeyboardEvent.prototype,"code",{get:function(){var e=a(this);return e?e.code:""}}),"key"in KeyboardEvent.prototype){var o=Object.getOwnPropertyDescriptor(KeyboardEvent.prototype,"key");Object.defineProperty(KeyboardEvent.prototype,"key",{get:function(){var e=o.get.call(this);return m.hasOwnProperty(e)?m[e]:e}})}e(KeyboardEvent.prototype,"key",{get:function(){var e=a(this);return e&&"key"in e?e.key:"Unidentified"}}),e(KeyboardEvent.prototype,"location",{get:function(){var e=a(this);return e&&"location"in e?e.location:d}}),e(KeyboardEvent.prototype,"locale",{get:function(){return""}})}(),"queryKeyCap"in e.KeyboardEvent||(e.KeyboardEvent.queryKeyCap=c),e.identifyKey=function(e){if(!("code"in e)){var o=a(e);e.code=o?o.code:"",e.key=o&&"key"in o?o.key:"Unidentified",e.location="location"in e?e.location:"keyLocation"in e?e.keyLocation:o&&"location"in o?o.location:d,e.locale=""}}}(window)}]);
--------------------------------------------------------------------------------
/examples/assets/321103__nsstudios__blip1.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/donmccurdy/aframe-keyboard-controls/f62aab778a12eebda9aac38d8b67564161302214/examples/assets/321103__nsstudios__blip1.wav
--------------------------------------------------------------------------------
/examples/assets/peach-gradient-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/donmccurdy/aframe-keyboard-controls/f62aab778a12eebda9aac38d8b67564161302214/examples/assets/peach-gradient-1.jpg
--------------------------------------------------------------------------------
/examples/assets/tex/shadow-circle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/donmccurdy/aframe-keyboard-controls/f62aab778a12eebda9aac38d8b67564161302214/examples/assets/tex/shadow-circle.png
--------------------------------------------------------------------------------
/examples/assets/tree1.dae:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CINEMA4D 15.064 COLLADA Exporter
6 |
7 | 2015-08-28T05:40:04Z
8 | 2015-08-28T05:40:04Z
9 |
10 | Y_UP
11 |
12 |
13 |
14 | tex/shadow-circle.png
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | 0.8 0.8 0.8 1
24 |
25 |
26 | 0.2 0.2 0.2 1
27 |
28 |
29 | 0.5
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | 0.0627451 0.396078 0.513725 1
41 |
42 |
43 | 0.2 0.2 0.2 1
44 |
45 |
46 | 0.5
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | 0.0235294 0.564706 0.607843 1
58 |
59 |
60 | 0.2 0.2 0.2 1
61 |
62 |
63 | 0.5
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | 0.164706 0.72549 0.701961 1
75 |
76 |
77 | 0.2 0.2 0.2 1
78 |
79 |
80 | 0.5
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 | 0.654902 0.47451 0.415686 1
92 |
93 |
94 | 0.2 0.2 0.2 1
95 |
96 |
97 | 0.5
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 | ID13
108 |
109 |
110 |
111 |
112 | ID14
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 | 1 1 1 1
122 |
123 |
124 | 0.88
125 |
126 |
127 | 1
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 | -40 -80.5 40 40 -80.5 40 40 -80.5 -40 -40 -80.5 -40 -40 -77.5711 47.0711 40 -77.5711 47.0711 45 -77.5711 45 47.0711 -77.5711 40 47.0711 -77.5711 -40 45 -77.5711 -45 40 -77.5711 -47.0711 -40 -77.5711 -47.0711 -45 -77.5711 -45 -47.0711 -77.5711 -40 -47.0711 -77.5711 40 -45 -77.5711 45 -40 -70.5 50 40 -70.5 50 47.0711 -70.5 47.0711 50 -70.5 40 50 -70.5 -40 47.0711 -70.5 -47.0711 40 -70.5 -50 -40 -70.5 -50 -47.0711 -70.5 -47.0711 -50 -70.5 -40 -50 -70.5 40 -47.0711 -70.5 47.0711 -40 70.5 50 40 70.5 50 47.0711 70.5 47.0711 50 70.5 40 50 70.5 -40 47.0711 70.5 -47.0711 40 70.5 -50 -40 70.5 -50 -47.0711 70.5 -47.0711 -50 70.5 -40 -50 70.5 40 -47.0711 70.5 47.0711 -40 77.5711 47.0711 40 77.5711 47.0711 45 77.5711 45 47.0711 77.5711 40 47.0711 77.5711 -40 45 77.5711 -45 40 77.5711 -47.0711 -40 77.5711 -47.0711 -45 77.5711 -45 -47.0711 77.5711 -40 -47.0711 77.5711 40 -45 77.5711 45 -40 80.5 40 40 80.5 40 40 80.5 -40 -40 80.5 -40
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 | -0.0785214 -0.921027 0.381502 0.0785214 -0.921027 0.381502 0 -0.382683 0.92388 0 0 1 0 0.382683 0.92388 -0.0785214 0.921027 0.381502 0.0785214 0.921027 0.381502 0.156558 -0.912487 0.377964 0.357407 -0.357407 0.862856 0.382683 0 0.92388 0.357407 0.357407 0.862856 0.156558 0.912487 0.377964 0.381502 -0.921027 0.0785214 0.377964 -0.912487 0.156558 0.862856 -0.357407 0.357407 0.92388 0 0.382683 0.862856 0.357407 0.357407 0.377964 0.912487 0.156558 0.381502 0.921027 0.0785214 0.381502 -0.921027 -0.0785214 0.92388 -0.382683 -0 1 0 -0 0.92388 0.382683 -0 0.381502 0.921027 -0.0785214 0.377964 -0.912487 -0.156558 0.862856 -0.357407 -0.357407 0.92388 0 -0.382683 0.862856 0.357407 -0.357407 0.377964 0.912487 -0.156558 0.0785214 -0.921027 -0.381502 0.156558 -0.912487 -0.377964 0.357407 -0.357407 -0.862856 0.382683 0 -0.92388 0.357407 0.357407 -0.862856 0.156558 0.912487 -0.377964 0.0785214 0.921027 -0.381502 -0.0785214 -0.921027 -0.381502 0 -0.382683 -0.92388 0 0 -1 0 0.382683 -0.92388 -0.0785214 0.921027 -0.381502 -0.156558 -0.912487 -0.377964 -0.357407 -0.357407 -0.862856 -0.382683 0 -0.92388 -0.357407 0.357407 -0.862856 -0.156558 0.912487 -0.377964 -0.381502 -0.921027 -0.0785214 -0.377964 -0.912487 -0.156558 -0.862856 -0.357407 -0.357407 -0.92388 0 -0.382683 -0.862856 0.357407 -0.357407 -0.377964 0.912487 -0.156558 -0.381502 0.921027 -0.0785214 -0.381502 -0.921027 0.0785214 -0.92388 -0.382683 -0 -1 0 -0 -0.92388 0.382683 -0 -0.381502 0.921027 0.0785214 -0.377964 -0.912487 0.156558 -0.862856 -0.357407 0.357407 -0.92388 0 0.382683 -0.862856 0.357407 0.357407 -0.377964 0.912487 0.156558 -0.156558 -0.912487 0.377964 -0.357407 -0.357407 0.862856 -0.382683 0 0.92388 -0.357407 0.357407 0.862856 -0.156558 0.912487 0.377964 0 1 -0 0 -1 -0
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 | 0 0 0 0.0455526 0.835876 0.0455526 0.835876 0 0 0.0911051 0.835876 0.0911051 0 0.908895 0.835876 0.908895 0 0.954447 0.835876 0.954447 0 1 0.835876 1 0.876907 0 0.917938 0.0455526 0.917938 0.0911051 0.917938 0.908895 0.917938 0.954447 0.876907 1 0.958969 0 1 0.0455526 1 0.0911051 1 0.908895 1 0.954447 0.958969 1 1 1 1 0
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 | 4 4 4 4 4 3 4 4 4 3 3 4 4 4 3 4 4 4 4 4 3 4 4 4 3 3 4 4 4 3 4 4 4 4 4 3 4 4 4 3 3 4 4 4 3 4 4 4 4 4 3 4 4 4 3 3 4 4 4 3 4 4
194 | 1 1 3 5 1 2 4 0 1 0 0 0 5 2 2 17 2 5 16 2 4 4 2 1 17 3 5 29 3 7 28 3 6 16 3 4 29 4 7 41 4 9 40 4 8 28 4 6 41 6 9 53 6 11 52 5 10 40 5 8 6 7 13 5 1 2 1 1 12 6 8 13 18 8 14 17 8 5 5 8 2 18 9 14 30 9 15 29 9 7 17 9 5 30 10 15 42 10 16 41 10 9 29 10 7 42 11 16 53 6 17 41 6 9 7 12 19 6 13 13 1 12 18 7 14 19 19 14 20 18 14 14 6 14 13 19 15 20 31 15 21 30 15 15 18 15 14 31 16 21 43 16 22 42 16 16 30 16 15 43 18 22 53 18 23 42 17 16 2 19 3 8 19 2 7 12 1 1 12 0 8 20 2 20 20 5 19 20 4 7 20 1 20 21 5 32 21 7 31 21 6 19 21 4 32 22 7 44 22 9 43 22 8 31 22 6 44 23 9 54 23 11 53 18 10 43 18 8 9 24 13 8 19 2 2 19 12 9 25 13 21 25 14 20 25 5 8 25 2 21 26 14 33 26 15 32 26 7 20 26 5 33 27 15 45 27 16 44 27 9 32 27 7 45 28 16 54 23 17 44 23 9 10 29 19 9 30 13 2 29 18 10 31 19 22 31 20 21 31 14 9 31 13 22 32 20 34 32 21 33 32 15 21 32 14 34 33 21 46 33 22 45 33 16 33 33 15 46 35 22 54 35 23 45 34 16 3 36 3 11 36 2 10 29 1 2 29 0 11 37 2 23 37 5 22 37 4 10 37 1 23 38 5 35 38 7 34 38 6 22 38 4 35 39 7 47 39 9 46 39 8 34 39 6 47 40 9 55 40 11 54 35 10 46 35 8 12 41 13 11 36 2 3 36 12 12 42 13 24 42 14 23 42 5 11 42 2 24 43 14 36 43 15 35 43 7 23 43 5 36 44 15 48 44 16 47 44 9 35 44 7 48 45 16 55 40 17 47 40 9 13 46 19 12 47 13 3 46 18 13 48 19 25 48 20 24 48 14 12 48 13 25 49 20 37 49 21 36 49 15 24 49 14 37 50 21 49 50 22 48 50 16 36 50 15 49 52 22 55 52 23 48 51 16 0 53 3 14 53 2 13 46 1 3 46 0 14 54 2 26 54 5 25 54 4 13 54 1 26 55 5 38 55 7 37 55 6 25 55 4 38 56 7 50 56 9 49 56 8 37 56 6 50 57 9 52 57 11 55 52 10 49 52 8 15 58 13 14 53 2 0 53 12 15 59 13 27 59 14 26 59 5 14 59 2 27 60 14 39 60 15 38 60 7 26 60 5 39 61 15 51 61 16 50 61 9 38 61 7 51 62 16 52 57 17 50 57 9 4 0 19 15 63 13 0 0 18 4 64 19 16 64 20 27 64 14 15 64 13 16 65 20 28 65 21 39 65 15 27 65 14 28 66 21 40 66 22 51 66 16 39 66 15 40 5 22 52 5 23 51 67 16 53 68 25 54 68 24 55 68 10 52 68 0 3 69 0 2 69 25 1 69 24 0 69 10
195 |
196 |
197 |
198 |
199 |
200 |
201 | 0 -50 0 0 50 0 10 -50 -0 10 -50 -0 10 50 -0 10 50 -0 8.66025 -50 -5 8.66025 -50 -5 8.66025 50 -5 8.66025 50 -5 5 -50 -8.66025 5 -50 -8.66025 5 50 -8.66025 5 50 -8.66025 6.12323e-16 -50 -10 6.12323e-16 -50 -10 6.12323e-16 50 -10 6.12323e-16 50 -10 -5 -50 -8.66025 -5 -50 -8.66025 -5 50 -8.66025 -5 50 -8.66025 -8.66025 -50 -5 -8.66025 -50 -5 -8.66025 50 -5 -8.66025 50 -5 -10 -50 -1.22465e-15 -10 -50 -1.22465e-15 -10 50 -1.22465e-15 -10 50 -1.22465e-15 -8.66025 -50 5 -8.66025 -50 5 -8.66025 50 5 -8.66025 50 5 -5 -50 8.66025 -5 -50 8.66025 -5 50 8.66025 -5 50 8.66025 -1.83697e-15 -50 10 -1.83697e-15 -50 10 -1.83697e-15 50 10 -1.83697e-15 50 10 5 -50 8.66025 5 -50 8.66025 5 50 8.66025 5 50 8.66025 8.66025 -50 5 8.66025 -50 5 8.66025 50 5 8.66025 50 5
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 | 0 -1 -0 1 0 -0 0.866026 0 -0.5 0 1 -0 0.5 0 -0.866026 0 0 -1 -0.5 0 -0.866026 -0.866026 0 -0.5 -1 0 -0 -0.866026 0 0.5 -0.5 0 0.866026 0 0 1 0.5 0 0.866026 0.866026 0 0.5
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 | 0.5 0.5 0 0.5 0.0669873 0.75 0 0 0 1 0.0833333 1 0.0833333 0 1 0.5 0.933013 0.75 0.25 0.933013 0.166667 1 0.166667 0 0.75 0.933013 0.5 1 0.25 1 0.25 0 0.333333 1 0.333333 0 0.416667 1 0.416667 0 0.5 0 0.933013 0.25 0.583333 1 0.583333 0 0.0669873 0.25 0.75 0.0669873 0.666667 1 0.666667 0 0.25 0.0669873 0.75 1 0.75 0 0.833333 1 0.833333 0 0.916667 1 0.916667 0 1 1 1 0
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 | 3 4 3 3 4 3 3 4 3 3 4 3 3 4 3 3 4 3 3 4 3 3 4 3 3 4 3 3 4 3 3 4 3 3 4 3
237 | 6 0 2 2 0 1 0 0 0 7 2 6 8 2 5 4 1 4 3 1 3 9 3 8 1 3 0 5 3 7 10 0 9 6 0 2 0 0 0 11 4 11 12 4 10 8 2 5 7 2 6 13 3 12 1 3 0 9 3 8 14 0 13 10 0 9 0 0 0 15 5 15 16 5 14 12 4 10 11 4 11 17 3 13 1 3 0 13 3 12 18 0 12 14 0 13 0 0 0 19 6 17 20 6 16 16 5 14 15 5 15 21 3 9 1 3 0 17 3 13 22 0 8 18 0 12 0 0 0 23 7 19 24 7 18 20 6 16 19 6 17 25 3 2 1 3 0 21 3 9 26 0 7 22 0 8 0 0 0 27 8 20 28 8 13 24 7 18 23 7 19 29 3 1 1 3 0 25 3 2 30 0 21 26 0 7 0 0 0 31 9 23 32 9 22 28 8 13 27 8 20 33 3 24 1 3 0 29 3 1 34 0 25 30 0 21 0 0 0 35 10 27 36 10 26 32 9 22 31 9 23 37 3 28 1 3 0 33 3 24 38 0 20 34 0 25 0 0 0 39 11 30 40 11 29 36 10 26 35 10 27 41 3 20 1 3 0 37 3 28 42 0 28 38 0 20 0 0 0 43 12 32 44 12 31 40 11 29 39 11 30 45 3 25 1 3 0 41 3 20 46 0 24 42 0 28 0 0 0 47 13 34 48 13 33 44 12 31 43 12 32 49 3 21 1 3 0 45 3 25 2 0 1 46 0 24 0 0 0 3 1 36 4 1 35 48 13 33 47 13 34 5 3 7 1 3 0 49 3 21
238 |
239 |
240 |
241 |
242 |
243 |
244 | -50 0 50 50 0 50 -50 0 -50 50 0 -50
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 | 0 1 -0
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 | 0 0 0 1 1 1 1 0
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 | 4
280 | 1 0 3 3 0 2 2 0 1 0 0 0
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 | 0 0 -0
289 | 0 1 0 -0
290 | 1 0 0 0
291 | 0 0 1 -0
292 | 1 1 1
293 |
294 | 0 180 -0
295 | 0 1 0 -0
296 | 1 0 0 0
297 | 0 0 1 -0
298 | 1 1 1
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 | 0 50 -0
311 | 0 1 0 -0
312 | 1 0 0 0
313 | 0 0 1 -0
314 | 1 1 1
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 | 0 0 -0
327 | 0 1 0 -0
328 | 1 0 0 0
329 | 0 0 1 -0
330 | 1 1 1
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
--------------------------------------------------------------------------------
/examples/assets/tree2.dae:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CINEMA4D 15.064 COLLADA Exporter
6 |
7 | 2015-08-28T05:43:48Z
8 | 2015-08-28T05:43:48Z
9 |
10 | Y_UP
11 |
12 |
13 |
14 | tex/shadow-circle.png
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | 0.8 0.8 0.8 1
24 |
25 |
26 | 0.2 0.2 0.2 1
27 |
28 |
29 | 0.5
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | 0.0627451 0.396078 0.513725 1
41 |
42 |
43 | 0.2 0.2 0.2 1
44 |
45 |
46 | 0.5
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | 0.0235294 0.564706 0.607843 1
58 |
59 |
60 | 0.2 0.2 0.2 1
61 |
62 |
63 | 0.5
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | 0.164706 0.72549 0.701961 1
75 |
76 |
77 | 0.2 0.2 0.2 1
78 |
79 |
80 | 0.5
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 | 0.654902 0.47451 0.415686 1
92 |
93 |
94 | 0.2 0.2 0.2 1
95 |
96 |
97 | 0.5
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 | ID13
108 |
109 |
110 |
111 |
112 | ID14
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 | 1 1 1 1
122 |
123 |
124 | 0.88
125 |
126 |
127 | 1
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 | -32.0367 -60 32.0367 32.0367 -60 32.0367 32.0367 -60 -32.0367 -32.0367 -60 -32.0367 -32.0367 -57.6676 37.6676 32.0367 -57.6676 37.6676 36.0183 -57.6676 36.0183 37.6676 -57.6676 32.0367 37.6676 -57.6676 -32.0367 36.0183 -57.6676 -36.0183 32.0367 -57.6676 -37.6676 -32.0367 -57.6676 -37.6676 -36.0183 -57.6676 -36.0183 -37.6676 -57.6676 -32.0367 -37.6676 -57.6676 32.0367 -36.0183 -57.6676 36.0183 -32.0367 -52.0367 40 32.0367 -52.0367 40 37.6676 -52.0367 37.6676 40 -52.0367 32.0367 40 -52.0367 -32.0367 37.6676 -52.0367 -37.6676 32.0367 -52.0367 -40 -32.0367 -52.0367 -40 -37.6676 -52.0367 -37.6676 -40 -52.0367 -32.0367 -40 -52.0367 32.0367 -37.6676 -52.0367 37.6676 -32.0367 52.0367 40 32.0367 52.0367 40 37.6676 52.0367 37.6676 40 52.0367 32.0367 40 52.0367 -32.0367 37.6676 52.0367 -37.6676 32.0367 52.0367 -40 -32.0367 52.0367 -40 -37.6676 52.0367 -37.6676 -40 52.0367 -32.0367 -40 52.0367 32.0367 -37.6676 52.0367 37.6676 -32.0367 57.6676 37.6676 32.0367 57.6676 37.6676 36.0183 57.6676 36.0183 37.6676 57.6676 32.0367 37.6676 57.6676 -32.0367 36.0183 57.6676 -36.0183 32.0367 57.6676 -37.6676 -32.0367 57.6676 -37.6676 -36.0183 57.6676 -36.0183 -37.6676 57.6676 -32.0367 -37.6676 57.6676 32.0367 -36.0183 57.6676 36.0183 -32.0367 60 32.0367 32.0367 60 32.0367 32.0367 60 -32.0367 -32.0367 60 -32.0367
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 | -0.0785213 -0.921027 0.381502 0.0785213 -0.921027 0.381502 0 -0.382683 0.92388 0 0 1 0 0.382683 0.92388 -0.0785213 0.921027 0.381502 0.0785213 0.921027 0.381502 0.156558 -0.912487 0.377964 0.357407 -0.357407 0.862856 0.382683 0 0.92388 0.357406 0.357407 0.862856 0.156558 0.912487 0.377964 0.381502 -0.921027 0.0785213 0.377964 -0.912487 0.156558 0.862856 -0.357407 0.357407 0.92388 0 0.382683 0.862856 0.357407 0.357407 0.377964 0.912487 0.156558 0.381502 0.921027 0.0785213 0.381502 -0.921027 -0.0785213 0.92388 -0.382683 -0 1 0 -0 0.92388 0.382683 -0 0.381502 0.921027 -0.0785213 0.377964 -0.912487 -0.156558 0.862856 -0.357407 -0.357407 0.92388 0 -0.382683 0.862856 0.357407 -0.357407 0.377964 0.912487 -0.156558 0.0785213 -0.921027 -0.381502 0.156558 -0.912487 -0.377964 0.357406 -0.357407 -0.862856 0.382683 0 -0.92388 0.357407 0.357407 -0.862856 0.156558 0.912487 -0.377964 0.0785213 0.921027 -0.381502 -0.0785213 -0.921027 -0.381502 0 -0.382683 -0.92388 0 0 -1 0 0.382683 -0.92388 -0.0785213 0.921027 -0.381502 -0.156558 -0.912487 -0.377964 -0.357407 -0.357407 -0.862856 -0.382683 0 -0.92388 -0.357406 0.357407 -0.862856 -0.156558 0.912487 -0.377964 -0.381502 -0.921027 -0.0785213 -0.377964 -0.912487 -0.156558 -0.862856 -0.357407 -0.357407 -0.92388 0 -0.382683 -0.862856 0.357407 -0.357407 -0.377964 0.912487 -0.156558 -0.381502 0.921027 -0.0785213 -0.381502 -0.921027 0.0785213 -0.92388 -0.382683 -0 -1 0 -0 -0.92388 0.382683 -0 -0.381502 0.921027 0.0785213 -0.377964 -0.912487 0.156558 -0.862856 -0.357407 0.357407 -0.92388 0 0.382683 -0.862856 0.357407 0.357407 -0.377964 0.912487 0.156558 -0.156558 -0.912487 0.377964 -0.357406 -0.357407 0.862856 -0.382683 0 0.92388 -0.357407 0.357407 0.862856 -0.156558 0.912487 0.377964 0 1 -0 0 -1 -0
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 | 0 0 0 0.0484494 0.836663 0.0484494 0.836663 0 0 0.0968987 0.836663 0.0968987 0 0.903101 0.836663 0.903101 0 0.951551 0.836663 0.951551 0 1 0.836663 1 0.877497 0 0.918331 0.0484494 0.918331 0.0968987 0.918331 0.903101 0.918331 0.951551 0.877497 1 0.959166 0 1 0.0484494 1 0.0968987 1 0.903101 1 0.951551 0.959166 1 1 1 1 0
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 | 4 4 4 4 4 3 4 4 4 3 3 4 4 4 3 4 4 4 4 4 3 4 4 4 3 3 4 4 4 3 4 4 4 4 4 3 4 4 4 3 3 4 4 4 3 4 4 4 4 4 3 4 4 4 3 3 4 4 4 3 4 4
194 | 1 1 3 5 1 2 4 0 1 0 0 0 5 2 2 17 2 5 16 2 4 4 2 1 17 3 5 29 3 7 28 3 6 16 3 4 29 4 7 41 4 9 40 4 8 28 4 6 41 6 9 53 6 11 52 5 10 40 5 8 6 7 13 5 1 2 1 1 12 6 8 13 18 8 14 17 8 5 5 8 2 18 9 14 30 9 15 29 9 7 17 9 5 30 10 15 42 10 16 41 10 9 29 10 7 42 11 16 53 6 17 41 6 9 7 12 19 6 13 13 1 12 18 7 14 19 19 14 20 18 14 14 6 14 13 19 15 20 31 15 21 30 15 15 18 15 14 31 16 21 43 16 22 42 16 16 30 16 15 43 18 22 53 18 23 42 17 16 2 19 3 8 19 2 7 12 1 1 12 0 8 20 2 20 20 5 19 20 4 7 20 1 20 21 5 32 21 7 31 21 6 19 21 4 32 22 7 44 22 9 43 22 8 31 22 6 44 23 9 54 23 11 53 18 10 43 18 8 9 24 13 8 19 2 2 19 12 9 25 13 21 25 14 20 25 5 8 25 2 21 26 14 33 26 15 32 26 7 20 26 5 33 27 15 45 27 16 44 27 9 32 27 7 45 28 16 54 23 17 44 23 9 10 29 19 9 30 13 2 29 18 10 31 19 22 31 20 21 31 14 9 31 13 22 32 20 34 32 21 33 32 15 21 32 14 34 33 21 46 33 22 45 33 16 33 33 15 46 35 22 54 35 23 45 34 16 3 36 3 11 36 2 10 29 1 2 29 0 11 37 2 23 37 5 22 37 4 10 37 1 23 38 5 35 38 7 34 38 6 22 38 4 35 39 7 47 39 9 46 39 8 34 39 6 47 40 9 55 40 11 54 35 10 46 35 8 12 41 13 11 36 2 3 36 12 12 42 13 24 42 14 23 42 5 11 42 2 24 43 14 36 43 15 35 43 7 23 43 5 36 44 15 48 44 16 47 44 9 35 44 7 48 45 16 55 40 17 47 40 9 13 46 19 12 47 13 3 46 18 13 48 19 25 48 20 24 48 14 12 48 13 25 49 20 37 49 21 36 49 15 24 49 14 37 50 21 49 50 22 48 50 16 36 50 15 49 52 22 55 52 23 48 51 16 0 53 3 14 53 2 13 46 1 3 46 0 14 54 2 26 54 5 25 54 4 13 54 1 26 55 5 38 55 7 37 55 6 25 55 4 38 56 7 50 56 9 49 56 8 37 56 6 50 57 9 52 57 11 55 52 10 49 52 8 15 58 13 14 53 2 0 53 12 15 59 13 27 59 14 26 59 5 14 59 2 27 60 14 39 60 15 38 60 7 26 60 5 39 61 15 51 61 16 50 61 9 38 61 7 51 62 16 52 57 17 50 57 9 4 0 19 15 63 13 0 0 18 4 64 19 16 64 20 27 64 14 15 64 13 16 65 20 28 65 21 39 65 15 27 65 14 28 66 21 40 66 22 51 66 16 39 66 15 40 5 22 52 5 23 51 67 16 53 68 25 54 68 24 55 68 10 52 68 0 3 69 0 2 69 25 1 69 24 0 69 10
195 |
196 |
197 |
198 |
199 |
200 |
201 | -18.4211 -34.5 62.1092 18.4211 -34.5 62.1092 18.4211 -34.5 -62.1092 -18.4211 -34.5 -62.1092 -18.4211 -33.1589 65.3469 18.4211 -33.1589 65.3469 20.7105 -33.1589 64.3986 21.6589 -33.1589 62.1092 21.6589 -33.1589 -62.1092 20.7105 -33.1589 -64.3986 18.4211 -33.1589 -65.3469 -18.4211 -33.1589 -65.3469 -20.7105 -33.1589 -64.3986 -21.6589 -33.1589 -62.1092 -21.6589 -33.1589 62.1092 -20.7105 -33.1589 64.3986 -18.4211 -29.9211 66.6881 18.4211 -29.9211 66.6881 21.6589 -29.9211 65.3469 23 -29.9211 62.1092 23 -29.9211 -62.1092 21.6589 -29.9211 -65.3469 18.4211 -29.9211 -66.6881 -18.4211 -29.9211 -66.6881 -21.6589 -29.9211 -65.3469 -23 -29.9211 -62.1092 -23 -29.9211 62.1092 -21.6589 -29.9211 65.3469 -18.4211 29.9211 66.6881 18.4211 29.9211 66.6881 21.6589 29.9211 65.3469 23 29.9211 62.1092 23 29.9211 -62.1092 21.6589 29.9211 -65.3469 18.4211 29.9211 -66.6881 -18.4211 29.9211 -66.6881 -21.6589 29.9211 -65.3469 -23 29.9211 -62.1092 -23 29.9211 62.1092 -21.6589 29.9211 65.3469 -18.4211 33.1589 65.3469 18.4211 33.1589 65.3469 20.7105 33.1589 64.3986 21.6589 33.1589 62.1092 21.6589 33.1589 -62.1092 20.7105 33.1589 -64.3986 18.4211 33.1589 -65.3469 -18.4211 33.1589 -65.3469 -20.7105 33.1589 -64.3986 -21.6589 33.1589 -62.1092 -21.6589 33.1589 62.1092 -20.7105 33.1589 64.3986 -18.4211 34.5 62.1092 18.4211 34.5 62.1092 18.4211 34.5 -62.1092 -18.4211 34.5 -62.1092
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 | -0.0785212 -0.921027 0.381502 0.0785212 -0.921027 0.381502 0 -0.382683 0.92388 0 0 1 0 0.382683 0.92388 -0.0785212 0.921027 0.381502 0.0785212 0.921027 0.381502 0.156558 -0.912487 0.377965 0.357406 -0.357406 0.862857 0.382683 0 0.92388 0.357406 0.357406 0.862857 0.156558 0.912487 0.377965 0.381502 -0.921027 0.0785213 0.377965 -0.912487 0.156558 0.862856 -0.357407 0.357407 0.923879 0 0.382684 0.862856 0.357406 0.357407 0.377965 0.912487 0.156558 0.381502 0.921027 0.0785213 0.381502 -0.921027 -0.0785213 0.92388 -0.382683 -0 1 0 -0 0.92388 0.382683 -0 0.381502 0.921027 -0.0785213 0.377965 -0.912487 -0.156558 0.862856 -0.357406 -0.357407 0.923879 0 -0.382684 0.862856 0.357407 -0.357407 0.377965 0.912487 -0.156558 0.0785212 -0.921027 -0.381502 0.156558 -0.912487 -0.377965 0.357406 -0.357406 -0.862857 0.382683 0 -0.92388 0.357406 0.357406 -0.862857 0.156558 0.912487 -0.377965 0.0785212 0.921027 -0.381502 -0.0785212 -0.921027 -0.381502 0 -0.382683 -0.92388 0 0 -1 0 0.382683 -0.92388 -0.0785212 0.921027 -0.381502 -0.156558 -0.912487 -0.377965 -0.357406 -0.357406 -0.862857 -0.382683 0 -0.92388 -0.357406 0.357406 -0.862857 -0.156558 0.912487 -0.377965 -0.381502 -0.921027 -0.0785213 -0.377965 -0.912487 -0.156558 -0.862856 -0.357407 -0.357407 -0.923879 0 -0.382684 -0.862856 0.357406 -0.357407 -0.377965 0.912487 -0.156558 -0.381502 0.921027 -0.0785213 -0.381502 -0.921027 0.0785213 -0.92388 -0.382683 -0 -1 0 -0 -0.92388 0.382683 -0 -0.381502 0.921027 0.0785213 -0.377965 -0.912487 0.156558 -0.862856 -0.357406 0.357407 -0.923879 0 0.382684 -0.862856 0.357407 0.357407 -0.377965 0.912487 0.156558 -0.156558 -0.912487 0.377965 -0.357406 -0.357406 0.862857 -0.382683 0 0.92388 -0.357406 0.357406 0.862857 -0.156558 0.912487 0.377965 0 1 -0 0 -1 -0
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 | 0 0 0 0.0484494 0.836663 0.0484494 0.836663 0 0 0.0968987 0.836663 0.0968987 0 0.903101 0.836663 0.903101 0 0.951551 0.836663 0.951551 0 1 0.836663 1 0.877497 0 0.918331 0.0484494 0.918331 0.0968987 0.918331 0.903101 0.918331 0.951551 0.877497 1 0.959166 0 1 0.0484494 1 0.0968987 1 0.903101 1 0.951551 0.959166 1 0.945267 0.0484494 0.945267 0 0.945267 0.0968987 0.945267 0.903101 0.945267 0.951551 0.945267 1 0.95895 0 0.972633 0.0484494 0.972633 0.0968987 0.972633 0.903101 0.972633 0.951551 0.95895 1 0.986317 0 0.986317 1 1 1 1 0
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 | 4 4 4 4 4 3 4 4 4 3 3 4 4 4 3 4 4 4 4 4 3 4 4 4 3 3 4 4 4 3 4 4 4 4 4 3 4 4 4 3 3 4 4 4 3 4 4 4 4 4 3 4 4 4 3 3 4 4 4 3 4 4
237 | 1 1 3 5 1 2 4 0 1 0 0 0 5 2 2 17 2 5 16 2 4 4 2 1 17 3 5 29 3 7 28 3 6 16 3 4 29 4 7 41 4 9 40 4 8 28 4 6 41 6 9 53 6 11 52 5 10 40 5 8 6 7 13 5 1 2 1 1 12 6 8 13 18 8 14 17 8 5 5 8 2 18 9 14 30 9 15 29 9 7 17 9 5 30 10 15 42 10 16 41 10 9 29 10 7 42 11 16 53 6 17 41 6 9 7 12 19 6 13 13 1 12 18 7 14 19 19 14 20 18 14 14 6 14 13 19 15 20 31 15 21 30 15 15 18 15 14 31 16 21 43 16 22 42 16 16 30 16 15 43 18 22 53 18 23 42 17 16 2 19 25 8 19 24 7 12 1 1 12 0 8 20 24 20 20 26 19 20 4 7 20 1 20 21 26 32 21 27 31 21 6 19 21 4 32 22 27 44 22 28 43 22 8 31 22 6 44 23 28 54 23 29 53 18 10 43 18 8 9 24 31 8 19 24 2 19 30 9 25 31 21 25 32 20 25 26 8 25 24 21 26 32 33 26 33 32 26 27 20 26 26 33 27 33 45 27 34 44 27 28 32 27 27 45 28 34 54 23 35 44 23 28 10 29 19 9 30 31 2 29 36 10 31 19 22 31 20 21 31 32 9 31 31 22 32 20 34 32 21 33 32 33 21 32 32 34 33 21 46 33 22 45 33 34 33 33 33 46 35 22 54 35 37 45 34 34 3 36 3 11 36 2 10 29 1 2 29 0 11 37 2 23 37 5 22 37 4 10 37 1 23 38 5 35 38 7 34 38 6 22 38 4 35 39 7 47 39 9 46 39 8 34 39 6 47 40 9 55 40 11 54 35 10 46 35 8 12 41 13 11 36 2 3 36 12 12 42 13 24 42 14 23 42 5 11 42 2 24 43 14 36 43 15 35 43 7 23 43 5 36 44 15 48 44 16 47 44 9 35 44 7 48 45 16 55 40 17 47 40 9 13 46 19 12 47 13 3 46 18 13 48 19 25 48 20 24 48 14 12 48 13 25 49 20 37 49 21 36 49 15 24 49 14 37 50 21 49 50 22 48 50 16 36 50 15 49 52 22 55 52 23 48 51 16 0 53 25 14 53 24 13 46 1 3 46 0 14 54 24 26 54 26 25 54 4 13 54 1 26 55 26 38 55 27 37 55 6 25 55 4 38 56 27 50 56 28 49 56 8 37 56 6 50 57 28 52 57 29 55 52 10 49 52 8 15 58 31 14 53 24 0 53 30 15 59 31 27 59 32 26 59 26 14 59 24 27 60 32 39 60 33 38 60 27 26 60 26 39 61 33 51 61 34 50 61 28 38 61 27 51 62 34 52 57 35 50 57 28 4 0 19 15 63 31 0 0 36 4 64 19 16 64 20 27 64 32 15 64 31 16 65 20 28 65 21 39 65 33 27 65 32 28 66 21 40 66 22 51 66 34 39 66 33 40 5 22 52 5 37 51 67 34 53 68 39 54 68 38 55 68 10 52 68 0 3 69 0 2 69 39 1 69 38 0 69 10
238 |
239 |
240 |
241 |
242 |
243 |
244 | -40 -80.5 40 40 -80.5 40 40 -80.5 -40 -40 -80.5 -40 -40 -77.5711 47.0711 40 -77.5711 47.0711 45 -77.5711 45 47.0711 -77.5711 40 47.0711 -77.5711 -40 45 -77.5711 -45 40 -77.5711 -47.0711 -40 -77.5711 -47.0711 -45 -77.5711 -45 -47.0711 -77.5711 -40 -47.0711 -77.5711 40 -45 -77.5711 45 -40 -70.5 50 40 -70.5 50 47.0711 -70.5 47.0711 50 -70.5 40 50 -70.5 -40 47.0711 -70.5 -47.0711 40 -70.5 -50 -40 -70.5 -50 -47.0711 -70.5 -47.0711 -50 -70.5 -40 -50 -70.5 40 -47.0711 -70.5 47.0711 -40 70.5 50 40 70.5 50 47.0711 70.5 47.0711 50 70.5 40 50 70.5 -40 47.0711 70.5 -47.0711 40 70.5 -50 -40 70.5 -50 -47.0711 70.5 -47.0711 -50 70.5 -40 -50 70.5 40 -47.0711 70.5 47.0711 -40 77.5711 47.0711 40 77.5711 47.0711 45 77.5711 45 47.0711 77.5711 40 47.0711 77.5711 -40 45 77.5711 -45 40 77.5711 -47.0711 -40 77.5711 -47.0711 -45 77.5711 -45 -47.0711 77.5711 -40 -47.0711 77.5711 40 -45 77.5711 45 -40 80.5 40 40 80.5 40 40 80.5 -40 -40 80.5 -40
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 | -0.0785214 -0.921027 0.381502 0.0785214 -0.921027 0.381502 0 -0.382683 0.92388 0 0 1 0 0.382683 0.92388 -0.0785214 0.921027 0.381502 0.0785214 0.921027 0.381502 0.156558 -0.912487 0.377964 0.357407 -0.357407 0.862856 0.382683 0 0.92388 0.357407 0.357407 0.862856 0.156558 0.912487 0.377964 0.381502 -0.921027 0.0785214 0.377964 -0.912487 0.156558 0.862856 -0.357407 0.357407 0.92388 0 0.382683 0.862856 0.357407 0.357407 0.377964 0.912487 0.156558 0.381502 0.921027 0.0785214 0.381502 -0.921027 -0.0785214 0.92388 -0.382683 -0 1 0 -0 0.92388 0.382683 -0 0.381502 0.921027 -0.0785214 0.377964 -0.912487 -0.156558 0.862856 -0.357407 -0.357407 0.92388 0 -0.382683 0.862856 0.357407 -0.357407 0.377964 0.912487 -0.156558 0.0785214 -0.921027 -0.381502 0.156558 -0.912487 -0.377964 0.357407 -0.357407 -0.862856 0.382683 0 -0.92388 0.357407 0.357407 -0.862856 0.156558 0.912487 -0.377964 0.0785214 0.921027 -0.381502 -0.0785214 -0.921027 -0.381502 0 -0.382683 -0.92388 0 0 -1 0 0.382683 -0.92388 -0.0785214 0.921027 -0.381502 -0.156558 -0.912487 -0.377964 -0.357407 -0.357407 -0.862856 -0.382683 0 -0.92388 -0.357407 0.357407 -0.862856 -0.156558 0.912487 -0.377964 -0.381502 -0.921027 -0.0785214 -0.377964 -0.912487 -0.156558 -0.862856 -0.357407 -0.357407 -0.92388 0 -0.382683 -0.862856 0.357407 -0.357407 -0.377964 0.912487 -0.156558 -0.381502 0.921027 -0.0785214 -0.381502 -0.921027 0.0785214 -0.92388 -0.382683 -0 -1 0 -0 -0.92388 0.382683 -0 -0.381502 0.921027 0.0785214 -0.377964 -0.912487 0.156558 -0.862856 -0.357407 0.357407 -0.92388 0 0.382683 -0.862856 0.357407 0.357407 -0.377964 0.912487 0.156558 -0.156558 -0.912487 0.377964 -0.357407 -0.357407 0.862856 -0.382683 0 0.92388 -0.357407 0.357407 0.862856 -0.156558 0.912487 0.377964 0 1 -0 0 -1 -0
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 | 0 0 0 0.0455526 0.835876 0.0455526 0.835876 0 0 0.0911051 0.835876 0.0911051 0 0.908895 0.835876 0.908895 0 0.954447 0.835876 0.954447 0 1 0.835876 1 0.876907 0 0.917938 0.0455526 0.917938 0.0911051 0.917938 0.908895 0.917938 0.954447 0.876907 1 0.958969 0 1 0.0455526 1 0.0911051 1 0.908895 1 0.954447 0.958969 1 1 1 1 0
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 | 4 4 4 4 4 3 4 4 4 3 3 4 4 4 3 4 4 4 4 4 3 4 4 4 3 3 4 4 4 3 4 4 4 4 4 3 4 4 4 3 3 4 4 4 3 4 4 4 4 4 3 4 4 4 3 3 4 4 4 3 4 4
280 | 1 1 3 5 1 2 4 0 1 0 0 0 5 2 2 17 2 5 16 2 4 4 2 1 17 3 5 29 3 7 28 3 6 16 3 4 29 4 7 41 4 9 40 4 8 28 4 6 41 6 9 53 6 11 52 5 10 40 5 8 6 7 13 5 1 2 1 1 12 6 8 13 18 8 14 17 8 5 5 8 2 18 9 14 30 9 15 29 9 7 17 9 5 30 10 15 42 10 16 41 10 9 29 10 7 42 11 16 53 6 17 41 6 9 7 12 19 6 13 13 1 12 18 7 14 19 19 14 20 18 14 14 6 14 13 19 15 20 31 15 21 30 15 15 18 15 14 31 16 21 43 16 22 42 16 16 30 16 15 43 18 22 53 18 23 42 17 16 2 19 3 8 19 2 7 12 1 1 12 0 8 20 2 20 20 5 19 20 4 7 20 1 20 21 5 32 21 7 31 21 6 19 21 4 32 22 7 44 22 9 43 22 8 31 22 6 44 23 9 54 23 11 53 18 10 43 18 8 9 24 13 8 19 2 2 19 12 9 25 13 21 25 14 20 25 5 8 25 2 21 26 14 33 26 15 32 26 7 20 26 5 33 27 15 45 27 16 44 27 9 32 27 7 45 28 16 54 23 17 44 23 9 10 29 19 9 30 13 2 29 18 10 31 19 22 31 20 21 31 14 9 31 13 22 32 20 34 32 21 33 32 15 21 32 14 34 33 21 46 33 22 45 33 16 33 33 15 46 35 22 54 35 23 45 34 16 3 36 3 11 36 2 10 29 1 2 29 0 11 37 2 23 37 5 22 37 4 10 37 1 23 38 5 35 38 7 34 38 6 22 38 4 35 39 7 47 39 9 46 39 8 34 39 6 47 40 9 55 40 11 54 35 10 46 35 8 12 41 13 11 36 2 3 36 12 12 42 13 24 42 14 23 42 5 11 42 2 24 43 14 36 43 15 35 43 7 23 43 5 36 44 15 48 44 16 47 44 9 35 44 7 48 45 16 55 40 17 47 40 9 13 46 19 12 47 13 3 46 18 13 48 19 25 48 20 24 48 14 12 48 13 25 49 20 37 49 21 36 49 15 24 49 14 37 50 21 49 50 22 48 50 16 36 50 15 49 52 22 55 52 23 48 51 16 0 53 3 14 53 2 13 46 1 3 46 0 14 54 2 26 54 5 25 54 4 13 54 1 26 55 5 38 55 7 37 55 6 25 55 4 38 56 7 50 56 9 49 56 8 37 56 6 50 57 9 52 57 11 55 52 10 49 52 8 15 58 13 14 53 2 0 53 12 15 59 13 27 59 14 26 59 5 14 59 2 27 60 14 39 60 15 38 60 7 26 60 5 39 61 15 51 61 16 50 61 9 38 61 7 51 62 16 52 57 17 50 57 9 4 0 19 15 63 13 0 0 18 4 64 19 16 64 20 27 64 14 15 64 13 16 65 20 28 65 21 39 65 15 27 65 14 28 66 21 40 66 22 51 66 16 39 66 15 40 5 22 52 5 23 51 67 16 53 68 25 54 68 24 55 68 10 52 68 0 3 69 0 2 69 25 1 69 24 0 69 10
281 |
282 |
283 |
284 |
285 |
286 |
287 | 0 -50 0 0 50 0 10 -50 -0 10 -50 -0 10 50 -0 10 50 -0 7.07107 -50 -7.07107 7.07107 -50 -7.07107 7.07107 50 -7.07107 7.07107 50 -7.07107 6.12323e-16 -50 -10 6.12323e-16 -50 -10 6.12323e-16 50 -10 6.12323e-16 50 -10 -7.07107 -50 -7.07107 -7.07107 -50 -7.07107 -7.07107 50 -7.07107 -7.07107 50 -7.07107 -10 -50 -1.22465e-15 -10 -50 -1.22465e-15 -10 50 -1.22465e-15 -10 50 -1.22465e-15 -7.07107 -50 7.07107 -7.07107 -50 7.07107 -7.07107 50 7.07107 -7.07107 50 7.07107 -1.83697e-15 -50 10 -1.83697e-15 -50 10 -1.83697e-15 50 10 -1.83697e-15 50 10 7.07107 -50 7.07107 7.07107 -50 7.07107 7.07107 50 7.07107 7.07107 50 7.07107
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 | 0 -1 -0 1 0 -0 0.707107 0 -0.707107 0 1 -0 0 0 -1 -0.707107 0 -0.707107 -1 0 -0 -0.707107 0 0.707107 0 0 1 0.707107 0 0.707107
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 | 0.5 0.5 0 0.5 0.146447 0.853553 0 0 0 1 0.125 1 0.125 0 1 0.5 0.853553 0.853553 0.5 1 0.25 1 0.25 0 0.375 1 0.375 0 0.5 0 0.853553 0.146447 0.625 1 0.625 0 0.146447 0.146447 0.75 1 0.75 0 0.875 1 0.875 0 1 1 1 0
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 | 3 4 3 3 4 3 3 4 3 3 4 3 3 4 3 3 4 3 3 4 3 3 4 3
323 | 6 0 2 2 0 1 0 0 0 7 2 6 8 2 5 4 1 4 3 1 3 9 3 8 1 3 0 5 3 7 10 0 9 6 0 2 0 0 0 11 4 11 12 4 10 8 2 5 7 2 6 13 3 9 1 3 0 9 3 8 14 0 8 10 0 9 0 0 0 15 5 13 16 5 12 12 4 10 11 4 11 17 3 2 1 3 0 13 3 9 18 0 7 14 0 8 0 0 0 19 6 14 20 6 9 16 5 12 15 5 13 21 3 1 1 3 0 17 3 2 22 0 15 18 0 7 0 0 0 23 7 17 24 7 16 20 6 9 19 6 14 25 3 18 1 3 0 21 3 1 26 0 14 22 0 15 0 0 0 27 8 20 28 8 19 24 7 16 23 7 17 29 3 14 1 3 0 25 3 18 30 0 18 26 0 14 0 0 0 31 9 22 32 9 21 28 8 19 27 8 20 33 3 15 1 3 0 29 3 14 2 0 1 30 0 18 0 0 0 3 1 24 4 1 23 32 9 21 31 9 22 5 3 7 1 3 0 33 3 15
324 |
325 |
326 |
327 |
328 |
329 |
330 | -50 0 50 50 0 50 -50 0 -50 50 0 -50
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 | 0 1 -0
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 | 0 0 0 1 1 1 1 0
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 | 4
366 | 1 0 3 3 0 2 2 0 1 0 0 0
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 | -23.7123 210.709 24.9106
375 | 0 1 0 -0
376 | 1 0 0 0
377 | 0 0 1 -0
378 | 1 1 1
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 | 3.20043 174.503 2.82738
391 | 0 1 0 -0
392 | 1 0 0 0
393 | 0 0 1 -0
394 | 1 1 1
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 | 0 180 -0
407 | 0 1 0 -0
408 | 1 0 0 0
409 | 0 0 1 -0
410 | 1 1 1
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 | 0 50 -0
423 | 0 1 0 -0
424 | 1 0 0 0
425 | 0 0 1 -0
426 | 1 1 1
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 | 0 0 -0
439 | 0 1 0 -0
440 | 1 0 0 0
441 | 0 0 1 -0
442 | 1 1 1
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 |
--------------------------------------------------------------------------------
/examples/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
11 |
17 |
18 |
19 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
38 |
39 |
40 |
43 |
44 |
45 |
46 |
47 |
57 | Mode: Default. (<Space> to change)
58 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/keyboard-controls.js:
--------------------------------------------------------------------------------
1 | require('./lib/keyboard.polyfill');
2 |
3 | var MAX_DELTA = 200,
4 | PROXY_FLAG = '__keyboard-controls-proxy';
5 |
6 | var KeyboardEvent = window.KeyboardEvent;
7 |
8 | /**
9 | * Keyboard Controls component.
10 | *
11 | * Bind keyboard events to components, or control your entities with the WASD keys.
12 | *
13 | * @namespace wasd-controls
14 | * @param {number} [easing=20] - How fast the movement decelerates. If you hold the
15 | * keys the entity moves and if you release it will stop. Easing simulates friction.
16 | * @param {number} [acceleration=65] - Determines the acceleration given
17 | * to the entity when pressing the keys.
18 | * @param {number} [angularAcceleration=Math.PI*0.25] - Determines the angular
19 | * acceleration given to the entity when pressing the keys. Only applied when
20 | * mode == 'fps'. Measured in Radians.
21 | * @param {bool} [enabled=true] - To completely enable or disable the controls
22 | * @param {string} [mode='default'] -
23 | * 'default' enforces the direction of the movement to stick to the plane
24 | * where the entity started off.
25 | * 'fps' extends 'default' by enabling "First Person Shooter" controls: W/S =
26 | * Forward/Back, Q/E = Strafe left/right, A/D = Rotate left/right
27 | * 'fly' enables 6 degrees of freedom as a diver underwater or a plane flying.
28 | * @param {string} [rollAxis='z'] - The front-to-back axis.
29 | * @param {string} [pitchAxis='x'] - The left-to-right axis.
30 | * @param {bool} [rollAxisInverted=false] - Roll axis is inverted
31 | * @param {bool} [pitchAxisInverted=false] - Pitch axis is inverted
32 | * @param {bool} [yawAxisInverted=false] - Yaw axis is inverted. Used when
33 | * mode == 'fps'
34 | */
35 | module.exports = {
36 | schema: {
37 | easing: { default: 20 },
38 | acceleration: { default: 65 },
39 | angularAcceleration: { default: Math.PI / 4 },
40 | enabled: { default: true },
41 | mode: { default: 'default', oneOf: ['default', 'fly', 'fps']},
42 | rollAxis: { default: 'z', oneOf: [ 'x', 'y', 'z' ] },
43 | pitchAxis: { default: 'x', oneOf: [ 'x', 'y', 'z' ] },
44 | rollAxisInverted: { default: false },
45 | rollAxisEnabled: { default: true },
46 | pitchAxisInverted: { default: false },
47 | pitchAxisEnabled: { default: true },
48 | yawAxisInverted: { default: false },
49 | debug: { default: false }
50 | },
51 |
52 | init: function () {
53 | this.velocity = new THREE.Vector3();
54 | this.angularVelocity = 0;
55 | this.localKeys = {};
56 | this.listeners = {
57 | keydown: this.onKeyDown.bind(this),
58 | keyup: this.onKeyUp.bind(this),
59 | blur: this.onBlur.bind(this)
60 | };
61 | this.attachEventListeners();
62 | },
63 |
64 | /*******************************************************************
65 | * Movement
66 | */
67 |
68 | tick: (function () {
69 | var upVector = new THREE.Vector3(0, 1, 0);
70 | var rotation = new THREE.Euler(0, 0, 0, 'YXZ');
71 | return function (t, dt) {
72 | var data = this.data;
73 | var acceleration = data.acceleration;
74 | var angularAcceleration = data.angularAcceleration;
75 | var easing = data.easing;
76 | var velocity = this.velocity;
77 | var keys = this.getKeys();
78 | var movementVector;
79 | var pitchAxis = data.pitchAxis;
80 | var rollAxis = data.rollAxis;
81 | var pitchSign = data.pitchAxisInverted ? -1 : 1;
82 | var rollSign = data.rollAxisInverted ? -1 : 1;
83 | var yawSign = data.yawAxisInverted ? 1 : -1;
84 | var el = this.el;
85 | var strafeLeft = data.mode === 'fps' ? ['KeyQ', 'ArrowLeft'] : ['KeyA', 'ArrowLeft'];
86 | var strafeRight = data.mode === 'fps' ? ['KeyE', 'ArrowRight'] : ['KeyD', 'ArrowRight'];
87 |
88 | // If data changed or FPS too low, reset velocity.
89 | if (isNaN(dt) || dt > MAX_DELTA) {
90 | velocity[pitchAxis] = 0;
91 | velocity[rollAxis] = 0;
92 | this.angularVelocity = 0;
93 | return;
94 | }
95 |
96 | velocity[pitchAxis] -= velocity[pitchAxis] * easing * dt / 1000;
97 | velocity[rollAxis] -= velocity[rollAxis] * easing * dt / 1000;
98 | this.angularVelocity -= this.angularVelocity * easing * dt / 1000;
99 |
100 | var position = el.getAttribute('position');
101 |
102 | if (data.enabled) {
103 | if (data.pitchAxisEnabled) {
104 | if (keys[strafeLeft[0]] || keys[strafeLeft[1]]) {
105 | velocity[pitchAxis] -= pitchSign * acceleration * dt / 1000;
106 | }
107 | if (keys[strafeRight[0]] || keys[strafeRight[1]]) {
108 | velocity[pitchAxis] += pitchSign * acceleration * dt / 1000;
109 | }
110 | }
111 | if (data.rollAxisEnabled) {
112 | if (keys.KeyW || keys.ArrowUp) {
113 | velocity[rollAxis] -= rollSign * acceleration * dt / 1000;
114 | }
115 | if (keys.KeyS || keys.ArrowDown) {
116 | velocity[rollAxis] += rollSign * acceleration * dt / 1000;
117 | }
118 | }
119 | if (data.mode === 'fps') {
120 | if (keys.KeyA) {
121 | this.angularVelocity -= yawSign * angularAcceleration * dt / 1000;
122 | }
123 | if (keys.KeyD) {
124 | this.angularVelocity += yawSign * angularAcceleration * dt / 1000;
125 | }
126 | }
127 | }
128 |
129 | if (data.mode === 'fps') {
130 | this.rotateOnAxis(rotation, upVector, this.angularVelocity);
131 |
132 | el.setAttribute('rotation', {
133 | x: THREE.Math.radToDeg(rotation.x),
134 | y: THREE.Math.radToDeg(rotation.y),
135 | z: THREE.Math.radToDeg(rotation.z)
136 | });
137 | }
138 |
139 | movementVector = this.getMovementVector(dt);
140 | el.object3D.translateX(movementVector.x);
141 | el.object3D.translateY(movementVector.y);
142 | el.object3D.translateZ(movementVector.z);
143 |
144 | el.setAttribute('position', {
145 | x: position.x + movementVector.x,
146 | y: position.y + movementVector.y,
147 | z: position.z + movementVector.z
148 | });
149 | };
150 | })(),
151 |
152 |
153 | rotateOnAxis: (function () {
154 |
155 | var quaternion = new THREE.Quaternion();
156 | var eulerAsQuaternion = new THREE.Quaternion();
157 |
158 | return function (euler, axis, angle) {
159 | quaternion.setFromAxisAngle(axis, angle);
160 | eulerAsQuaternion.setFromEuler(euler);
161 | eulerAsQuaternion.multiply(quaternion);
162 | euler.setFromQuaternion(eulerAsQuaternion, euler.order);
163 | };
164 |
165 | })(),
166 |
167 | getMovementVector: (function () {
168 | var direction = new THREE.Vector3(0, 0, 0);
169 | var rotation = new THREE.Euler(0, 0, 0, 'YXZ');
170 | return function (dt) {
171 | var velocity = this.velocity;
172 | var elRotation = this.el.getAttribute('rotation');
173 | direction.copy(velocity);
174 | direction.multiplyScalar(dt / 1000);
175 | if (!elRotation) { return direction; }
176 | if (this.data.mode !== 'fly') { elRotation.x = 0; }
177 | rotation.set(THREE.Math.degToRad(elRotation.x),
178 | THREE.Math.degToRad(elRotation.y), 0);
179 | direction.applyEuler(rotation);
180 | return direction;
181 | };
182 | })(),
183 |
184 | /*******************************************************************
185 | * Events
186 | */
187 |
188 | play: function () {
189 | this.attachEventListeners();
190 | },
191 |
192 | pause: function () {
193 | this.removeEventListeners();
194 | },
195 |
196 | remove: function () {
197 | this.pause();
198 | },
199 |
200 | attachEventListeners: function () {
201 | window.addEventListener('keydown', this.listeners.keydown, false);
202 | window.addEventListener('keyup', this.listeners.keyup, false);
203 | window.addEventListener('blur', this.listeners.blur, false);
204 | },
205 |
206 | removeEventListeners: function () {
207 | window.removeEventListener('keydown', this.listeners.keydown);
208 | window.removeEventListener('keyup', this.listeners.keyup);
209 | window.removeEventListener('blur', this.listeners.blur);
210 | },
211 |
212 | onKeyDown: function (event) {
213 | this.localKeys[event.code] = true;
214 | this.emit(event);
215 | },
216 |
217 | onKeyUp: function (event) {
218 | delete this.localKeys[event.code];
219 | this.emit(event);
220 | },
221 |
222 | onBlur: function () {
223 | for (var code in this.localKeys) {
224 | if (this.localKeys.hasOwnProperty(code)) {
225 | delete this.localKeys[code];
226 | }
227 | }
228 | },
229 |
230 | emit: function (event) {
231 | // TODO - keydown only initially?
232 | // TODO - where the f is the spacebar
233 |
234 | // Emit original event.
235 | if (PROXY_FLAG in event) {
236 | // TODO - Method never triggered.
237 | this.el.emit(event.type, event);
238 | }
239 |
240 | // Emit convenience event, identifying key.
241 | this.el.emit(event.type + ':' + event.code, new KeyboardEvent(event.type, event));
242 | if (this.data.debug) console.log(event.type + ':' + event.code);
243 | },
244 |
245 | /*******************************************************************
246 | * Accessors
247 | */
248 |
249 | isPressed: function (code) {
250 | return code in this.getKeys();
251 | },
252 |
253 | getKeys: function () {
254 | if (this.isProxied()) {
255 | return this.el.sceneEl.components['proxy-controls'].getKeyboard();
256 | }
257 | return this.localKeys;
258 | },
259 |
260 | isProxied: function () {
261 | var proxyControls = this.el.sceneEl.components['proxy-controls'];
262 | return proxyControls && proxyControls.isConnected();
263 | }
264 |
265 | };
266 |
--------------------------------------------------------------------------------
/lib/keyboard.polyfill.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Polyfill for the additional KeyboardEvent properties defined in the D3E and
3 | * D4E draft specifications, by @inexorabletash.
4 | *
5 | * See: https://github.com/inexorabletash/polyfill
6 | */
7 | (function(global) {
8 | var nativeKeyboardEvent = ('KeyboardEvent' in global);
9 | if (!nativeKeyboardEvent)
10 | global.KeyboardEvent = function KeyboardEvent() { throw TypeError('Illegal constructor'); };
11 |
12 | global.KeyboardEvent.DOM_KEY_LOCATION_STANDARD = 0x00; // Default or unknown location
13 | global.KeyboardEvent.DOM_KEY_LOCATION_LEFT = 0x01; // e.g. Left Alt key
14 | global.KeyboardEvent.DOM_KEY_LOCATION_RIGHT = 0x02; // e.g. Right Alt key
15 | global.KeyboardEvent.DOM_KEY_LOCATION_NUMPAD = 0x03; // e.g. Numpad 0 or +
16 |
17 | var STANDARD = window.KeyboardEvent.DOM_KEY_LOCATION_STANDARD,
18 | LEFT = window.KeyboardEvent.DOM_KEY_LOCATION_LEFT,
19 | RIGHT = window.KeyboardEvent.DOM_KEY_LOCATION_RIGHT,
20 | NUMPAD = window.KeyboardEvent.DOM_KEY_LOCATION_NUMPAD;
21 |
22 | //--------------------------------------------------------------------
23 | //
24 | // Utilities
25 | //
26 | //--------------------------------------------------------------------
27 |
28 | function contains(s, ss) { return String(s).indexOf(ss) !== -1; }
29 |
30 | var os = (function() {
31 | if (contains(navigator.platform, 'Win')) { return 'win'; }
32 | if (contains(navigator.platform, 'Mac')) { return 'mac'; }
33 | if (contains(navigator.platform, 'CrOS')) { return 'cros'; }
34 | if (contains(navigator.platform, 'Linux')) { return 'linux'; }
35 | if (contains(navigator.userAgent, 'iPad') || contains(navigator.platform, 'iPod') || contains(navigator.platform, 'iPhone')) { return 'ios'; }
36 | return '';
37 | } ());
38 |
39 | var browser = (function() {
40 | if (contains(navigator.userAgent, 'Chrome/')) { return 'chrome'; }
41 | if (contains(navigator.vendor, 'Apple')) { return 'safari'; }
42 | if (contains(navigator.userAgent, 'MSIE')) { return 'ie'; }
43 | if (contains(navigator.userAgent, 'Gecko/')) { return 'moz'; }
44 | if (contains(navigator.userAgent, 'Opera/')) { return 'opera'; }
45 | return '';
46 | } ());
47 |
48 | var browser_os = browser + '-' + os;
49 |
50 | function mergeIf(baseTable, select, table) {
51 | if (browser_os === select || browser === select || os === select) {
52 | Object.keys(table).forEach(function(keyCode) {
53 | baseTable[keyCode] = table[keyCode];
54 | });
55 | }
56 | }
57 |
58 | function remap(o, key) {
59 | var r = {};
60 | Object.keys(o).forEach(function(k) {
61 | var item = o[k];
62 | if (key in item) {
63 | r[item[key]] = item;
64 | }
65 | });
66 | return r;
67 | }
68 |
69 | function invert(o) {
70 | var r = {};
71 | Object.keys(o).forEach(function(k) {
72 | r[o[k]] = k;
73 | });
74 | return r;
75 | }
76 |
77 | //--------------------------------------------------------------------
78 | //
79 | // Generic Mappings
80 | //
81 | //--------------------------------------------------------------------
82 |
83 | // "keyInfo" is a dictionary:
84 | // code: string - name from DOM Level 3 KeyboardEvent code Values
85 | // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3Events-code.html
86 | // location (optional): number - one of the DOM_KEY_LOCATION values
87 | // keyCap (optional): string - keyboard label in en-US locale
88 | // USB code Usage ID from page 0x07 unless otherwise noted (Informative)
89 |
90 | // Map of keyCode to keyInfo
91 | var keyCodeToInfoTable = {
92 | // 0x01 - VK_LBUTTON
93 | // 0x02 - VK_RBUTTON
94 | 0x03: { code: 'Cancel' }, // [USB: 0x9b] char \x0018 ??? (Not in D3E)
95 | // 0x04 - VK_MBUTTON
96 | // 0x05 - VK_XBUTTON1
97 | // 0x06 - VK_XBUTTON2
98 | 0x06: { code: 'Help' }, // [USB: 0x75] ???
99 | // 0x07 - undefined
100 | 0x08: { code: 'Backspace' }, // [USB: 0x2a] Labelled Delete on Macintosh keyboards.
101 | 0x09: { code: 'Tab' }, // [USB: 0x2b]
102 | // 0x0A-0x0B - reserved
103 | 0X0C: { code: 'Clear' }, // [USB: 0x9c] NumPad Center (Not in D3E)
104 | 0X0D: { code: 'Enter' }, // [USB: 0x28]
105 | // 0x0E-0x0F - undefined
106 |
107 | 0x10: { code: 'Shift' },
108 | 0x11: { code: 'Control' },
109 | 0x12: { code: 'Alt' },
110 | 0x13: { code: 'Pause' }, // [USB: 0x48]
111 | 0x14: { code: 'CapsLock' }, // [USB: 0x39]
112 | 0x15: { code: 'KanaMode' }, // [USB: 0x88] - "HangulMode" for Korean layout
113 | 0x16: { code: 'HangulMode' }, // [USB: 0x90] 0x15 as well in MSDN VK table ???
114 | 0x17: { code: 'JunjaMode' }, // (Not in D3E)
115 | 0x18: { code: 'FinalMode' }, // (Not in D3E)
116 | 0x19: { code: 'KanjiMode' }, // [USB: 0x91] - "HanjaMode" for Korean layout
117 | // 0x1A - undefined
118 | 0x1B: { code: 'Escape' }, // [USB: 0x29]
119 | 0x1C: { code: 'Convert' }, // [USB: 0x8a]
120 | 0x1D: { code: 'NonConvert' }, // [USB: 0x8b]
121 | 0x1E: { code: 'Accept' }, // (Not in D3E)
122 | 0x1F: { code: 'ModeChange' }, // (Not in D3E)
123 |
124 | 0x20: { code: 'Space' }, // [USB: 0x2c]
125 | 0x21: { code: 'PageUp' }, // [USB: 0x4b]
126 | 0x22: { code: 'PageDown' }, // [USB: 0x4e]
127 | 0x23: { code: 'End' }, // [USB: 0x4d]
128 | 0x24: { code: 'Home' }, // [USB: 0x4a]
129 | 0x25: { code: 'ArrowLeft' }, // [USB: 0x50]
130 | 0x26: { code: 'ArrowUp' }, // [USB: 0x52]
131 | 0x27: { code: 'ArrowRight' }, // [USB: 0x4f]
132 | 0x28: { code: 'ArrowDown' }, // [USB: 0x51]
133 | 0x29: { code: 'Select' }, // (Not in D3E)
134 | 0x2A: { code: 'Print' }, // (Not in D3E)
135 | 0x2B: { code: 'Execute' }, // [USB: 0x74] (Not in D3E)
136 | 0x2C: { code: 'PrintScreen' }, // [USB: 0x46]
137 | 0x2D: { code: 'Insert' }, // [USB: 0x49]
138 | 0x2E: { code: 'Delete' }, // [USB: 0x4c]
139 | 0x2F: { code: 'Help' }, // [USB: 0x75] ???
140 |
141 | 0x30: { code: 'Digit0', keyCap: '0' }, // [USB: 0x27] 0)
142 | 0x31: { code: 'Digit1', keyCap: '1' }, // [USB: 0x1e] 1!
143 | 0x32: { code: 'Digit2', keyCap: '2' }, // [USB: 0x1f] 2@
144 | 0x33: { code: 'Digit3', keyCap: '3' }, // [USB: 0x20] 3#
145 | 0x34: { code: 'Digit4', keyCap: '4' }, // [USB: 0x21] 4$
146 | 0x35: { code: 'Digit5', keyCap: '5' }, // [USB: 0x22] 5%
147 | 0x36: { code: 'Digit6', keyCap: '6' }, // [USB: 0x23] 6^
148 | 0x37: { code: 'Digit7', keyCap: '7' }, // [USB: 0x24] 7&
149 | 0x38: { code: 'Digit8', keyCap: '8' }, // [USB: 0x25] 8*
150 | 0x39: { code: 'Digit9', keyCap: '9' }, // [USB: 0x26] 9(
151 | // 0x3A-0x40 - undefined
152 |
153 | 0x41: { code: 'KeyA', keyCap: 'a' }, // [USB: 0x04]
154 | 0x42: { code: 'KeyB', keyCap: 'b' }, // [USB: 0x05]
155 | 0x43: { code: 'KeyC', keyCap: 'c' }, // [USB: 0x06]
156 | 0x44: { code: 'KeyD', keyCap: 'd' }, // [USB: 0x07]
157 | 0x45: { code: 'KeyE', keyCap: 'e' }, // [USB: 0x08]
158 | 0x46: { code: 'KeyF', keyCap: 'f' }, // [USB: 0x09]
159 | 0x47: { code: 'KeyG', keyCap: 'g' }, // [USB: 0x0a]
160 | 0x48: { code: 'KeyH', keyCap: 'h' }, // [USB: 0x0b]
161 | 0x49: { code: 'KeyI', keyCap: 'i' }, // [USB: 0x0c]
162 | 0x4A: { code: 'KeyJ', keyCap: 'j' }, // [USB: 0x0d]
163 | 0x4B: { code: 'KeyK', keyCap: 'k' }, // [USB: 0x0e]
164 | 0x4C: { code: 'KeyL', keyCap: 'l' }, // [USB: 0x0f]
165 | 0x4D: { code: 'KeyM', keyCap: 'm' }, // [USB: 0x10]
166 | 0x4E: { code: 'KeyN', keyCap: 'n' }, // [USB: 0x11]
167 | 0x4F: { code: 'KeyO', keyCap: 'o' }, // [USB: 0x12]
168 |
169 | 0x50: { code: 'KeyP', keyCap: 'p' }, // [USB: 0x13]
170 | 0x51: { code: 'KeyQ', keyCap: 'q' }, // [USB: 0x14]
171 | 0x52: { code: 'KeyR', keyCap: 'r' }, // [USB: 0x15]
172 | 0x53: { code: 'KeyS', keyCap: 's' }, // [USB: 0x16]
173 | 0x54: { code: 'KeyT', keyCap: 't' }, // [USB: 0x17]
174 | 0x55: { code: 'KeyU', keyCap: 'u' }, // [USB: 0x18]
175 | 0x56: { code: 'KeyV', keyCap: 'v' }, // [USB: 0x19]
176 | 0x57: { code: 'KeyW', keyCap: 'w' }, // [USB: 0x1a]
177 | 0x58: { code: 'KeyX', keyCap: 'x' }, // [USB: 0x1b]
178 | 0x59: { code: 'KeyY', keyCap: 'y' }, // [USB: 0x1c]
179 | 0x5A: { code: 'KeyZ', keyCap: 'z' }, // [USB: 0x1d]
180 | 0x5B: { code: 'OSLeft', location: LEFT }, // [USB: 0xe3]
181 | 0x5C: { code: 'OSRight', location: RIGHT }, // [USB: 0xe7]
182 | 0x5D: { code: 'ContextMenu' }, // [USB: 0x65] Context Menu
183 | // 0x5E - reserved
184 | 0x5F: { code: 'Standby' }, // [USB: 0x82] Sleep
185 |
186 | 0x60: { code: 'Numpad0', keyCap: '0', location: NUMPAD }, // [USB: 0x62]
187 | 0x61: { code: 'Numpad1', keyCap: '1', location: NUMPAD }, // [USB: 0x59]
188 | 0x62: { code: 'Numpad2', keyCap: '2', location: NUMPAD }, // [USB: 0x5a]
189 | 0x63: { code: 'Numpad3', keyCap: '3', location: NUMPAD }, // [USB: 0x5b]
190 | 0x64: { code: 'Numpad4', keyCap: '4', location: NUMPAD }, // [USB: 0x5c]
191 | 0x65: { code: 'Numpad5', keyCap: '5', location: NUMPAD }, // [USB: 0x5d]
192 | 0x66: { code: 'Numpad6', keyCap: '6', location: NUMPAD }, // [USB: 0x5e]
193 | 0x67: { code: 'Numpad7', keyCap: '7', location: NUMPAD }, // [USB: 0x5f]
194 | 0x68: { code: 'Numpad8', keyCap: '8', location: NUMPAD }, // [USB: 0x60]
195 | 0x69: { code: 'Numpad9', keyCap: '9', location: NUMPAD }, // [USB: 0x61]
196 | 0x6A: { code: 'NumpadMultiply', keyCap: '*', location: NUMPAD }, // [USB: 0x55]
197 | 0x6B: { code: 'NumpadAdd', keyCap: '+', location: NUMPAD }, // [USB: 0x57]
198 | 0x6C: { code: 'NumpadComma', keyCap: ',', location: NUMPAD }, // [USB: 0x85]
199 | 0x6D: { code: 'NumpadSubtract', keyCap: '-', location: NUMPAD }, // [USB: 0x56]
200 | 0x6E: { code: 'NumpadDecimal', keyCap: '.', location: NUMPAD }, // [USB: 0x63]
201 | 0x6F: { code: 'NumpadDivide', keyCap: '/', location: NUMPAD }, // [USB: 0x54]
202 |
203 | 0x70: { code: 'F1' }, // [USB: 0x3a]
204 | 0x71: { code: 'F2' }, // [USB: 0x3b]
205 | 0x72: { code: 'F3' }, // [USB: 0x3c]
206 | 0x73: { code: 'F4' }, // [USB: 0x3d]
207 | 0x74: { code: 'F5' }, // [USB: 0x3e]
208 | 0x75: { code: 'F6' }, // [USB: 0x3f]
209 | 0x76: { code: 'F7' }, // [USB: 0x40]
210 | 0x77: { code: 'F8' }, // [USB: 0x41]
211 | 0x78: { code: 'F9' }, // [USB: 0x42]
212 | 0x79: { code: 'F10' }, // [USB: 0x43]
213 | 0x7A: { code: 'F11' }, // [USB: 0x44]
214 | 0x7B: { code: 'F12' }, // [USB: 0x45]
215 | 0x7C: { code: 'F13' }, // [USB: 0x68]
216 | 0x7D: { code: 'F14' }, // [USB: 0x69]
217 | 0x7E: { code: 'F15' }, // [USB: 0x6a]
218 | 0x7F: { code: 'F16' }, // [USB: 0x6b]
219 |
220 | 0x80: { code: 'F17' }, // [USB: 0x6c]
221 | 0x81: { code: 'F18' }, // [USB: 0x6d]
222 | 0x82: { code: 'F19' }, // [USB: 0x6e]
223 | 0x83: { code: 'F20' }, // [USB: 0x6f]
224 | 0x84: { code: 'F21' }, // [USB: 0x70]
225 | 0x85: { code: 'F22' }, // [USB: 0x71]
226 | 0x86: { code: 'F23' }, // [USB: 0x72]
227 | 0x87: { code: 'F24' }, // [USB: 0x73]
228 | // 0x88-0x8F - unassigned
229 |
230 | 0x90: { code: 'NumLock', location: NUMPAD }, // [USB: 0x53]
231 | 0x91: { code: 'ScrollLock' }, // [USB: 0x47]
232 | // 0x92-0x96 - OEM specific
233 | // 0x97-0x9F - unassigned
234 |
235 | // NOTE: 0xA0-0xA5 usually mapped to 0x10-0x12 in browsers
236 | 0xA0: { code: 'ShiftLeft', location: LEFT }, // [USB: 0xe1]
237 | 0xA1: { code: 'ShiftRight', location: RIGHT }, // [USB: 0xe5]
238 | 0xA2: { code: 'ControlLeft', location: LEFT }, // [USB: 0xe0]
239 | 0xA3: { code: 'ControlRight', location: RIGHT }, // [USB: 0xe4]
240 | 0xA4: { code: 'AltLeft', location: LEFT }, // [USB: 0xe2]
241 | 0xA5: { code: 'AltRight', location: RIGHT }, // [USB: 0xe6]
242 |
243 | 0xA6: { code: 'BrowserBack' }, // [USB: 0x0c/0x0224]
244 | 0xA7: { code: 'BrowserForward' }, // [USB: 0x0c/0x0225]
245 | 0xA8: { code: 'BrowserRefresh' }, // [USB: 0x0c/0x0227]
246 | 0xA9: { code: 'BrowserStop' }, // [USB: 0x0c/0x0226]
247 | 0xAA: { code: 'BrowserSearch' }, // [USB: 0x0c/0x0221]
248 | 0xAB: { code: 'BrowserFavorites' }, // [USB: 0x0c/0x0228]
249 | 0xAC: { code: 'BrowserHome' }, // [USB: 0x0c/0x0222]
250 | 0xAD: { code: 'VolumeMute' }, // [USB: 0x7f]
251 | 0xAE: { code: 'VolumeDown' }, // [USB: 0x81]
252 | 0xAF: { code: 'VolumeUp' }, // [USB: 0x80]
253 |
254 | 0xB0: { code: 'MediaTrackNext' }, // [USB: 0x0c/0x00b5]
255 | 0xB1: { code: 'MediaTrackPrevious' }, // [USB: 0x0c/0x00b6]
256 | 0xB2: { code: 'MediaStop' }, // [USB: 0x0c/0x00b7]
257 | 0xB3: { code: 'MediaPlayPause' }, // [USB: 0x0c/0x00cd]
258 | 0xB4: { code: 'LaunchMail' }, // [USB: 0x0c/0x018a]
259 | 0xB5: { code: 'MediaSelect' },
260 | 0xB6: { code: 'LaunchApp1' },
261 | 0xB7: { code: 'LaunchApp2' },
262 | // 0xB8-0xB9 - reserved
263 | 0xBA: { code: 'Semicolon', keyCap: ';' }, // [USB: 0x33] ;: (US Standard 101)
264 | 0xBB: { code: 'Equal', keyCap: '=' }, // [USB: 0x2e] =+
265 | 0xBC: { code: 'Comma', keyCap: ',' }, // [USB: 0x36] ,<
266 | 0xBD: { code: 'Minus', keyCap: '-' }, // [USB: 0x2d] -_
267 | 0xBE: { code: 'Period', keyCap: '.' }, // [USB: 0x37] .>
268 | 0xBF: { code: 'Slash', keyCap: '/' }, // [USB: 0x38] /? (US Standard 101)
269 |
270 | 0xC0: { code: 'Backquote', keyCap: '`' }, // [USB: 0x35] `~ (US Standard 101)
271 | // 0xC1-0xCF - reserved
272 |
273 | // 0xD0-0xD7 - reserved
274 | // 0xD8-0xDA - unassigned
275 | 0xDB: { code: 'BracketLeft', keyCap: '[' }, // [USB: 0x2f] [{ (US Standard 101)
276 | 0xDC: { code: 'Backslash', keyCap: '\\' }, // [USB: 0x31] \| (US Standard 101)
277 | 0xDD: { code: 'BracketRight', keyCap: ']' }, // [USB: 0x30] ]} (US Standard 101)
278 | 0xDE: { code: 'Quote', keyCap: '\'' }, // [USB: 0x34] '" (US Standard 101)
279 | // 0xDF - miscellaneous/varies
280 |
281 | // 0xE0 - reserved
282 | // 0xE1 - OEM specific
283 | 0xE2: { code: 'IntlBackslash', keyCap: '\\' }, // [USB: 0x64] \| (UK Standard 102)
284 | // 0xE3-0xE4 - OEM specific
285 | 0xE5: { code: 'Process' }, // (Not in D3E)
286 | // 0xE6 - OEM specific
287 | // 0xE7 - VK_PACKET
288 | // 0xE8 - unassigned
289 | // 0xE9-0xEF - OEM specific
290 |
291 | // 0xF0-0xF5 - OEM specific
292 | 0xF6: { code: 'Attn' }, // [USB: 0x9a] (Not in D3E)
293 | 0xF7: { code: 'CrSel' }, // [USB: 0xa3] (Not in D3E)
294 | 0xF8: { code: 'ExSel' }, // [USB: 0xa4] (Not in D3E)
295 | 0xF9: { code: 'EraseEof' }, // (Not in D3E)
296 | 0xFA: { code: 'Play' }, // (Not in D3E)
297 | 0xFB: { code: 'ZoomToggle' }, // (Not in D3E)
298 | // 0xFC - VK_NONAME - reserved
299 | // 0xFD - VK_PA1
300 | 0xFE: { code: 'Clear' } // [USB: 0x9c] (Not in D3E)
301 | };
302 |
303 | // No legacy keyCode, but listed in D3E:
304 |
305 | // code: usb
306 | // 'IntlHash': 0x070032,
307 | // 'IntlRo': 0x070087,
308 | // 'IntlYen': 0x070089,
309 | // 'NumpadBackspace': 0x0700bb,
310 | // 'NumpadClear': 0x0700d8,
311 | // 'NumpadClearEntry': 0x0700d9,
312 | // 'NumpadMemoryAdd': 0x0700d3,
313 | // 'NumpadMemoryClear': 0x0700d2,
314 | // 'NumpadMemoryRecall': 0x0700d1,
315 | // 'NumpadMemoryStore': 0x0700d0,
316 | // 'NumpadMemorySubtract': 0x0700d4,
317 | // 'NumpadParenLeft': 0x0700b6,
318 | // 'NumpadParenRight': 0x0700b7,
319 |
320 | //--------------------------------------------------------------------
321 | //
322 | // Browser/OS Specific Mappings
323 | //
324 | //--------------------------------------------------------------------
325 |
326 | mergeIf(keyCodeToInfoTable,
327 | 'moz', {
328 | 0x3B: { code: 'Semicolon', keyCap: ';' }, // [USB: 0x33] ;: (US Standard 101)
329 | 0x3D: { code: 'Equal', keyCap: '=' }, // [USB: 0x2e] =+
330 | 0x6B: { code: 'Equal', keyCap: '=' }, // [USB: 0x2e] =+
331 | 0x6D: { code: 'Minus', keyCap: '-' }, // [USB: 0x2d] -_
332 | 0xBB: { code: 'NumpadAdd', keyCap: '+', location: NUMPAD }, // [USB: 0x57]
333 | 0xBD: { code: 'NumpadSubtract', keyCap: '-', location: NUMPAD } // [USB: 0x56]
334 | });
335 |
336 | mergeIf(keyCodeToInfoTable,
337 | 'moz-mac', {
338 | 0x0C: { code: 'NumLock', location: NUMPAD }, // [USB: 0x53]
339 | 0xAD: { code: 'Minus', keyCap: '-' } // [USB: 0x2d] -_
340 | });
341 |
342 | mergeIf(keyCodeToInfoTable,
343 | 'moz-win', {
344 | 0xAD: { code: 'Minus', keyCap: '-' } // [USB: 0x2d] -_
345 | });
346 |
347 | mergeIf(keyCodeToInfoTable,
348 | 'chrome-mac', {
349 | 0x5D: { code: 'OSRight', location: RIGHT } // [USB: 0xe7]
350 | });
351 |
352 | // Windows via Bootcamp (!)
353 | if (0) {
354 | mergeIf(keyCodeToInfoTable,
355 | 'chrome-win', {
356 | 0xC0: { code: 'Quote', keyCap: '\'' }, // [USB: 0x34] '" (US Standard 101)
357 | 0xDE: { code: 'Backslash', keyCap: '\\' }, // [USB: 0x31] \| (US Standard 101)
358 | 0xDF: { code: 'Backquote', keyCap: '`' } // [USB: 0x35] `~ (US Standard 101)
359 | });
360 |
361 | mergeIf(keyCodeToInfoTable,
362 | 'ie', {
363 | 0xC0: { code: 'Quote', keyCap: '\'' }, // [USB: 0x34] '" (US Standard 101)
364 | 0xDE: { code: 'Backslash', keyCap: '\\' }, // [USB: 0x31] \| (US Standard 101)
365 | 0xDF: { code: 'Backquote', keyCap: '`' } // [USB: 0x35] `~ (US Standard 101)
366 | });
367 | }
368 |
369 | mergeIf(keyCodeToInfoTable,
370 | 'safari', {
371 | 0x03: { code: 'Enter' }, // [USB: 0x28] old Safari
372 | 0x19: { code: 'Tab' } // [USB: 0x2b] old Safari for Shift+Tab
373 | });
374 |
375 | mergeIf(keyCodeToInfoTable,
376 | 'ios', {
377 | 0x0A: { code: 'Enter', location: STANDARD } // [USB: 0x28]
378 | });
379 |
380 | mergeIf(keyCodeToInfoTable,
381 | 'safari-mac', {
382 | 0x5B: { code: 'OSLeft', location: LEFT }, // [USB: 0xe3]
383 | 0x5D: { code: 'OSRight', location: RIGHT }, // [USB: 0xe7]
384 | 0xE5: { code: 'KeyQ', keyCap: 'Q' } // [USB: 0x14] On alternate presses, Ctrl+Q sends this
385 | });
386 |
387 | //--------------------------------------------------------------------
388 | //
389 | // Identifier Mappings
390 | //
391 | //--------------------------------------------------------------------
392 |
393 | // Cases where newer-ish browsers send keyIdentifier which can be
394 | // used to disambiguate keys.
395 |
396 | // keyIdentifierTable[keyIdentifier] -> keyInfo
397 |
398 | var keyIdentifierTable = {};
399 | if ('cros' === os) {
400 | keyIdentifierTable['U+00A0'] = { code: 'ShiftLeft', location: LEFT };
401 | keyIdentifierTable['U+00A1'] = { code: 'ShiftRight', location: RIGHT };
402 | keyIdentifierTable['U+00A2'] = { code: 'ControlLeft', location: LEFT };
403 | keyIdentifierTable['U+00A3'] = { code: 'ControlRight', location: RIGHT };
404 | keyIdentifierTable['U+00A4'] = { code: 'AltLeft', location: LEFT };
405 | keyIdentifierTable['U+00A5'] = { code: 'AltRight', location: RIGHT };
406 | }
407 | if ('chrome-mac' === browser_os) {
408 | keyIdentifierTable['U+0010'] = { code: 'ContextMenu' };
409 | }
410 | if ('safari-mac' === browser_os) {
411 | keyIdentifierTable['U+0010'] = { code: 'ContextMenu' };
412 | }
413 | if ('ios' === os) {
414 | // These only generate keyup events
415 | keyIdentifierTable['U+0010'] = { code: 'Function' };
416 |
417 | keyIdentifierTable['U+001C'] = { code: 'ArrowLeft' };
418 | keyIdentifierTable['U+001D'] = { code: 'ArrowRight' };
419 | keyIdentifierTable['U+001E'] = { code: 'ArrowUp' };
420 | keyIdentifierTable['U+001F'] = { code: 'ArrowDown' };
421 |
422 | keyIdentifierTable['U+0001'] = { code: 'Home' }; // [USB: 0x4a] Fn + ArrowLeft
423 | keyIdentifierTable['U+0004'] = { code: 'End' }; // [USB: 0x4d] Fn + ArrowRight
424 | keyIdentifierTable['U+000B'] = { code: 'PageUp' }; // [USB: 0x4b] Fn + ArrowUp
425 | keyIdentifierTable['U+000C'] = { code: 'PageDown' }; // [USB: 0x4e] Fn + ArrowDown
426 | }
427 |
428 | //--------------------------------------------------------------------
429 | //
430 | // Location Mappings
431 | //
432 | //--------------------------------------------------------------------
433 |
434 | // Cases where newer-ish browsers send location/keyLocation which
435 | // can be used to disambiguate keys.
436 |
437 | // locationTable[location][keyCode] -> keyInfo
438 | var locationTable = [];
439 | locationTable[LEFT] = {
440 | 0x10: { code: 'ShiftLeft', location: LEFT }, // [USB: 0xe1]
441 | 0x11: { code: 'ControlLeft', location: LEFT }, // [USB: 0xe0]
442 | 0x12: { code: 'AltLeft', location: LEFT } // [USB: 0xe2]
443 | };
444 | locationTable[RIGHT] = {
445 | 0x10: { code: 'ShiftRight', location: RIGHT }, // [USB: 0xe5]
446 | 0x11: { code: 'ControlRight', location: RIGHT }, // [USB: 0xe4]
447 | 0x12: { code: 'AltRight', location: RIGHT } // [USB: 0xe6]
448 | };
449 | locationTable[NUMPAD] = {
450 | 0x0D: { code: 'NumpadEnter', location: NUMPAD } // [USB: 0x58]
451 | };
452 |
453 | mergeIf(locationTable[NUMPAD], 'moz', {
454 | 0x6D: { code: 'NumpadSubtract', location: NUMPAD }, // [USB: 0x56]
455 | 0x6B: { code: 'NumpadAdd', location: NUMPAD } // [USB: 0x57]
456 | });
457 | mergeIf(locationTable[LEFT], 'moz-mac', {
458 | 0xE0: { code: 'OSLeft', location: LEFT } // [USB: 0xe3]
459 | });
460 | mergeIf(locationTable[RIGHT], 'moz-mac', {
461 | 0xE0: { code: 'OSRight', location: RIGHT } // [USB: 0xe7]
462 | });
463 | mergeIf(locationTable[RIGHT], 'moz-win', {
464 | 0x5B: { code: 'OSRight', location: RIGHT } // [USB: 0xe7]
465 | });
466 |
467 |
468 | mergeIf(locationTable[RIGHT], 'mac', {
469 | 0x5D: { code: 'OSRight', location: RIGHT } // [USB: 0xe7]
470 | });
471 |
472 | mergeIf(locationTable[NUMPAD], 'chrome-mac', {
473 | 0x0C: { code: 'NumLock', location: NUMPAD } // [USB: 0x53]
474 | });
475 |
476 | mergeIf(locationTable[NUMPAD], 'safari-mac', {
477 | 0x0C: { code: 'NumLock', location: NUMPAD }, // [USB: 0x53]
478 | 0xBB: { code: 'NumpadAdd', location: NUMPAD }, // [USB: 0x57]
479 | 0xBD: { code: 'NumpadSubtract', location: NUMPAD }, // [USB: 0x56]
480 | 0xBE: { code: 'NumpadDecimal', location: NUMPAD }, // [USB: 0x63]
481 | 0xBF: { code: 'NumpadDivide', location: NUMPAD } // [USB: 0x54]
482 | });
483 |
484 |
485 | //--------------------------------------------------------------------
486 | //
487 | // Key Values
488 | //
489 | //--------------------------------------------------------------------
490 |
491 | // Mapping from `code` values to `key` values. Values defined at:
492 | // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3Events-key.html
493 | // Entries are only provided when `key` differs from `code`. If
494 | // printable, `shiftKey` has the shifted printable character. This
495 | // assumes US Standard 101 layout
496 |
497 | var codeToKeyTable = {
498 | // Modifier Keys
499 | ShiftLeft: { key: 'Shift' },
500 | ShiftRight: { key: 'Shift' },
501 | ControlLeft: { key: 'Control' },
502 | ControlRight: { key: 'Control' },
503 | AltLeft: { key: 'Alt' },
504 | AltRight: { key: 'Alt' },
505 | OSLeft: { key: 'OS' },
506 | OSRight: { key: 'OS' },
507 |
508 | // Whitespace Keys
509 | NumpadEnter: { key: 'Enter' },
510 | Space: { key: ' ' },
511 |
512 | // Printable Keys
513 | Digit0: { key: '0', shiftKey: ')' },
514 | Digit1: { key: '1', shiftKey: '!' },
515 | Digit2: { key: '2', shiftKey: '@' },
516 | Digit3: { key: '3', shiftKey: '#' },
517 | Digit4: { key: '4', shiftKey: '$' },
518 | Digit5: { key: '5', shiftKey: '%' },
519 | Digit6: { key: '6', shiftKey: '^' },
520 | Digit7: { key: '7', shiftKey: '&' },
521 | Digit8: { key: '8', shiftKey: '*' },
522 | Digit9: { key: '9', shiftKey: '(' },
523 | KeyA: { key: 'a', shiftKey: 'A' },
524 | KeyB: { key: 'b', shiftKey: 'B' },
525 | KeyC: { key: 'c', shiftKey: 'C' },
526 | KeyD: { key: 'd', shiftKey: 'D' },
527 | KeyE: { key: 'e', shiftKey: 'E' },
528 | KeyF: { key: 'f', shiftKey: 'F' },
529 | KeyG: { key: 'g', shiftKey: 'G' },
530 | KeyH: { key: 'h', shiftKey: 'H' },
531 | KeyI: { key: 'i', shiftKey: 'I' },
532 | KeyJ: { key: 'j', shiftKey: 'J' },
533 | KeyK: { key: 'k', shiftKey: 'K' },
534 | KeyL: { key: 'l', shiftKey: 'L' },
535 | KeyM: { key: 'm', shiftKey: 'M' },
536 | KeyN: { key: 'n', shiftKey: 'N' },
537 | KeyO: { key: 'o', shiftKey: 'O' },
538 | KeyP: { key: 'p', shiftKey: 'P' },
539 | KeyQ: { key: 'q', shiftKey: 'Q' },
540 | KeyR: { key: 'r', shiftKey: 'R' },
541 | KeyS: { key: 's', shiftKey: 'S' },
542 | KeyT: { key: 't', shiftKey: 'T' },
543 | KeyU: { key: 'u', shiftKey: 'U' },
544 | KeyV: { key: 'v', shiftKey: 'V' },
545 | KeyW: { key: 'w', shiftKey: 'W' },
546 | KeyX: { key: 'x', shiftKey: 'X' },
547 | KeyY: { key: 'y', shiftKey: 'Y' },
548 | KeyZ: { key: 'z', shiftKey: 'Z' },
549 | Numpad0: { key: '0' },
550 | Numpad1: { key: '1' },
551 | Numpad2: { key: '2' },
552 | Numpad3: { key: '3' },
553 | Numpad4: { key: '4' },
554 | Numpad5: { key: '5' },
555 | Numpad6: { key: '6' },
556 | Numpad7: { key: '7' },
557 | Numpad8: { key: '8' },
558 | Numpad9: { key: '9' },
559 | NumpadMultiply: { key: '*' },
560 | NumpadAdd: { key: '+' },
561 | NumpadComma: { key: ',' },
562 | NumpadSubtract: { key: '-' },
563 | NumpadDecimal: { key: '.' },
564 | NumpadDivide: { key: '/' },
565 | Semicolon: { key: ';', shiftKey: ':' },
566 | Equal: { key: '=', shiftKey: '+' },
567 | Comma: { key: ',', shiftKey: '<' },
568 | Minus: { key: '-', shiftKey: '_' },
569 | Period: { key: '.', shiftKey: '>' },
570 | Slash: { key: '/', shiftKey: '?' },
571 | Backquote: { key: '`', shiftKey: '~' },
572 | BracketLeft: { key: '[', shiftKey: '{' },
573 | Backslash: { key: '\\', shiftKey: '|' },
574 | BracketRight: { key: ']', shiftKey: '}' },
575 | Quote: { key: '\'', shiftKey: '"' },
576 | IntlBackslash: { key: '\\', shiftKey: '|' }
577 | };
578 |
579 | mergeIf(codeToKeyTable, 'mac', {
580 | OSLeft: { key: 'Meta' },
581 | OSRight: { key: 'Meta' }
582 | });
583 |
584 | // Corrections for 'key' names in older browsers (e.g. FF36-)
585 | // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key#Key_values
586 | var keyFixTable = {
587 | Esc: 'Escape',
588 | Nonconvert: 'NonConvert',
589 | Left: 'ArrowLeft',
590 | Up: 'ArrowUp',
591 | Right: 'ArrowRight',
592 | Down: 'ArrowDown',
593 | Del: 'Delete',
594 | Menu: 'ContextMenu',
595 | MediaNextTrack: 'MediaTrackNext',
596 | MediaPreviousTrack: 'MediaTrackPrevious',
597 | SelectMedia: 'MediaSelect',
598 | HalfWidth: 'Hankaku',
599 | FullWidth: 'Zenkaku',
600 | RomanCharacters: 'Romaji',
601 | Crsel: 'CrSel',
602 | Exsel: 'ExSel',
603 | Zoom: 'ZoomToggle'
604 | };
605 |
606 | //--------------------------------------------------------------------
607 | //
608 | // Exported Functions
609 | //
610 | //--------------------------------------------------------------------
611 |
612 |
613 | var codeTable = remap(keyCodeToInfoTable, 'code');
614 |
615 | try {
616 | var nativeLocation = nativeKeyboardEvent && ('location' in new KeyboardEvent(''));
617 | } catch (_) {}
618 |
619 | function keyInfoForEvent(event) {
620 | var keyCode = 'keyCode' in event ? event.keyCode : 'which' in event ? event.which : 0;
621 |
622 | var keyInfo = (function(){
623 | if (nativeLocation || 'keyLocation' in event) {
624 | var location = nativeLocation ? event.location : event.keyLocation;
625 | if (location && keyCode in locationTable[location]) {
626 | return locationTable[location][keyCode];
627 | }
628 | }
629 | if ('keyIdentifier' in event && event.keyIdentifier in keyIdentifierTable) {
630 | return keyIdentifierTable[event.keyIdentifier];
631 | }
632 | if (keyCode in keyCodeToInfoTable) {
633 | return keyCodeToInfoTable[keyCode];
634 | }
635 | return null;
636 | }());
637 |
638 | // TODO: Track these down and move to general tables
639 | if (0) {
640 | // TODO: Map these for newerish browsers?
641 | // TODO: iOS only?
642 | // TODO: Override with more common keyIdentifier name?
643 | switch (event.keyIdentifier) {
644 | case 'U+0010': keyInfo = { code: 'Function' }; break;
645 | case 'U+001C': keyInfo = { code: 'ArrowLeft' }; break;
646 | case 'U+001D': keyInfo = { code: 'ArrowRight' }; break;
647 | case 'U+001E': keyInfo = { code: 'ArrowUp' }; break;
648 | case 'U+001F': keyInfo = { code: 'ArrowDown' }; break;
649 | }
650 | }
651 |
652 | if (!keyInfo)
653 | return null;
654 |
655 | var key = (function() {
656 | var entry = codeToKeyTable[keyInfo.code];
657 | if (!entry) return keyInfo.code;
658 | return (event.shiftKey && 'shiftKey' in entry) ? entry.shiftKey : entry.key;
659 | }());
660 |
661 | return {
662 | code: keyInfo.code,
663 | key: key,
664 | location: keyInfo.location,
665 | keyCap: keyInfo.keyCap
666 | };
667 | }
668 |
669 | function queryKeyCap(code, locale) {
670 | code = String(code);
671 | if (!codeTable.hasOwnProperty(code)) return 'Undefined';
672 | if (locale && String(locale).toLowerCase() !== 'en-us') throw Error('Unsupported locale');
673 | var keyInfo = codeTable[code];
674 | return keyInfo.keyCap || keyInfo.code || 'Undefined';
675 | }
676 |
677 | if ('KeyboardEvent' in global && 'defineProperty' in Object) {
678 | (function() {
679 | function define(o, p, v) {
680 | if (p in o) return;
681 | Object.defineProperty(o, p, v);
682 | }
683 |
684 | define(KeyboardEvent.prototype, 'code', { get: function() {
685 | var keyInfo = keyInfoForEvent(this);
686 | return keyInfo ? keyInfo.code : '';
687 | }});
688 |
689 | // Fix for nonstandard `key` values (FF36-)
690 | if ('key' in KeyboardEvent.prototype) {
691 | var desc = Object.getOwnPropertyDescriptor(KeyboardEvent.prototype, 'key');
692 | Object.defineProperty(KeyboardEvent.prototype, 'key', { get: function() {
693 | var key = desc.get.call(this);
694 | return keyFixTable.hasOwnProperty(key) ? keyFixTable[key] : key;
695 | }});
696 | }
697 |
698 | define(KeyboardEvent.prototype, 'key', { get: function() {
699 | var keyInfo = keyInfoForEvent(this);
700 | return (keyInfo && 'key' in keyInfo) ? keyInfo.key : 'Unidentified';
701 | }});
702 |
703 | define(KeyboardEvent.prototype, 'location', { get: function() {
704 | var keyInfo = keyInfoForEvent(this);
705 | return (keyInfo && 'location' in keyInfo) ? keyInfo.location : STANDARD;
706 | }});
707 |
708 | define(KeyboardEvent.prototype, 'locale', { get: function() {
709 | return '';
710 | }});
711 | }());
712 | }
713 |
714 | if (!('queryKeyCap' in global.KeyboardEvent))
715 | global.KeyboardEvent.queryKeyCap = queryKeyCap;
716 |
717 | // Helper for IE8-
718 | global.identifyKey = function(event) {
719 | if ('code' in event)
720 | return;
721 |
722 | var keyInfo = keyInfoForEvent(event);
723 | event.code = keyInfo ? keyInfo.code : '';
724 | event.key = (keyInfo && 'key' in keyInfo) ? keyInfo.key : 'Unidentified';
725 | event.location = ('location' in event) ? event.location :
726 | ('keyLocation' in event) ? event.keyLocation :
727 | (keyInfo && 'location' in keyInfo) ? keyInfo.location : STANDARD;
728 | event.locale = '';
729 | };
730 |
731 | } (window));
732 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "aframe-keyboard-controls",
3 | "version": "1.3.0",
4 | "description": "Keyboard controls for A-Frame.",
5 | "main": "keyboard-controls.js",
6 | "config": {
7 | "demo_host": "localhost",
8 | "demo_port": 8000
9 | },
10 | "scripts": {
11 | "dev": "budo browser.js:bundle.js --dir examples --port $npm_package_config_demo_port --host $npm_package_config_demo_host --live",
12 | "dist": "webpack browser.js dist/aframe-keyboard-controls.js && webpack -p browser.js dist/aframe-keyboard-controls.min.js",
13 | "postpublish": "npm run dist",
14 | "test": "karma start ./tests/karma.conf.js",
15 | "preversion": "karma start ./tests/karma.conf.js --single-run",
16 | "version": "npm run dist && git add -A dist",
17 | "postversion": "git push && git push --tags && npm publish"
18 | },
19 | "repository": {
20 | "type": "git",
21 | "url": "git+https://github.com/donmccurdy/aframe-keyboard-controls.git"
22 | },
23 | "keywords": [
24 | "aframe",
25 | "aframe-component",
26 | "aframe-vr",
27 | "vr",
28 | "mozvr",
29 | "webvr",
30 | "keyboard",
31 | "controls",
32 | "wasd",
33 | "input"
34 | ],
35 | "author": "Don McCurdy ",
36 | "license": "MIT",
37 | "bugs": {
38 | "url": "https://github.com/donmccurdy/aframe-keyboard-controls/issues"
39 | },
40 | "homepage": "https://github.com/donmccurdy/aframe-keyboard-controls#readme",
41 | "dependencies": {},
42 | "devDependencies": {
43 | "aframe-core": "^0.1.3",
44 | "browserify": "^12.0.1",
45 | "browserify-css": "^0.8.3",
46 | "budo": "^7.1.0",
47 | "chai": "^3.4.1",
48 | "chai-shallow-deep-equal": "^1.3.0",
49 | "karma": "^0.13.15",
50 | "karma-browserify": "^4.4.2",
51 | "karma-chai-shallow-deep-equal": "0.0.4",
52 | "karma-firefox-launcher": "^0.1.7",
53 | "karma-mocha": "^0.2.1",
54 | "karma-mocha-reporter": "^1.1.3",
55 | "karma-sinon-chai": "^1.1.0",
56 | "mocha": "^2.3.4",
57 | "webpack": "^1.12.9"
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/tests/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../.jshintrc",
3 | "globals" : {
4 | "it" : false,
5 | "describe" : false,
6 | "expect" : false,
7 | "beforeEach" : false,
8 | "afterEach" : false,
9 | "assert" : false,
10 | "spyOn" : false
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/tests/__init.test.js:
--------------------------------------------------------------------------------
1 | /* global sinon, setup, teardown */
2 |
3 | /**
4 | * __init.test.js is run before every test case.
5 | */
6 | window.debug = true;
7 |
8 | var AScene = require('aframe-core').AScene;
9 |
10 | beforeEach(function () {
11 | this.sinon = sinon.sandbox.create();
12 | // Stub to not create a WebGL context since Travis CI runs headless.
13 | this.sinon.stub(AScene.prototype, 'attachedCallback');
14 | });
15 |
16 | afterEach(function () {
17 | // Clean up any attached elements.
18 | ['canvas', 'a-assets', 'a-scene'].forEach(function (tagName) {
19 | var els = document.querySelectorAll(tagName);
20 | for (var i = 0; i < els.length; i++) {
21 | els[i].parentNode.removeChild(els[i]);
22 | }
23 | });
24 | AScene.scene = null;
25 |
26 | this.sinon.restore();
27 | });
28 |
--------------------------------------------------------------------------------
/tests/helpers.js:
--------------------------------------------------------------------------------
1 | /* global suite */
2 |
3 | /**
4 | * Helper method to create a scene, create an entity, add entity to scene,
5 | * add scene to document.
6 | *
7 | * @returns {object} An `` element.
8 | */
9 | module.exports.entityFactory = function () {
10 | var scene = document.createElement('a-scene');
11 | var entity = document.createElement('a-entity');
12 | scene.appendChild(entity);
13 | document.body.appendChild(scene);
14 | return entity;
15 | };
16 |
17 | /**
18 | * Creates and attaches a mixin element (and an `` element if necessary).
19 | *
20 | * @param {string} id - ID of mixin.
21 | * @param {object} obj - Map of component names to attribute values.
22 | * @returns {object} An attached `` element.
23 | */
24 | module.exports.mixinFactory = function (id, obj) {
25 | var mixinEl = document.createElement('a-mixin');
26 | mixinEl.setAttribute('id', id);
27 | Object.keys(obj).forEach(function (componentName) {
28 | mixinEl.setAttribute(componentName, obj[componentName]);
29 | });
30 |
31 | var assetsEl = document.querySelector('a-assets');
32 | if (!assetsEl) {
33 | assetsEl = document.createElement('a-assets');
34 | document.body.appendChild(assetsEl);
35 | }
36 | assetsEl.appendChild(mixinEl);
37 |
38 | return mixinEl;
39 | };
40 |
41 | /**
42 | * Test that is only run locally and is skipped on CI.
43 | */
44 | module.exports.getSkipCISuite = function () {
45 | if (window.__env__.TEST_ENV === 'ci') {
46 | return suite.skip;
47 | } else {
48 | return suite;
49 | }
50 | };
51 |
--------------------------------------------------------------------------------
/tests/karma.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = function (config) {
3 | config.set({
4 | basePath: '../',
5 | browserify: {
6 | paths: ['./']
7 | },
8 | browsers: ['firefox_latest'],
9 | customLaunchers: {
10 | firefox_latest: {
11 | base: 'FirefoxNightly',
12 | prefs: { /* empty */ }
13 | }
14 | },
15 | client: {
16 | captureConsole: true,
17 | mocha: {ui: 'bdd'}
18 | },
19 | envPreprocessor: [
20 | 'TEST_ENV'
21 | ],
22 | files: [
23 | 'tests/**/*.test.js',
24 | ],
25 | frameworks: ['mocha', 'sinon-chai', 'chai-shallow-deep-equal', 'browserify'],
26 | preprocessors: {
27 | 'tests/**/*.js': ['browserify']
28 | },
29 | reporters: ['mocha']
30 | });
31 | };
32 |
--------------------------------------------------------------------------------
/tests/keyboard-controls.test.js:
--------------------------------------------------------------------------------
1 | var Aframe = require('aframe-core');
2 | var component = require('../keyboard-controls');
3 | var entityFactory = require('./helpers').entityFactory;
4 |
5 | Aframe.registerComponent('keyboard-controls', component);
6 |
7 | describe('Keyboard Controls', function () {
8 |
9 | /*******************************************************************
10 | * Setup
11 | */
12 |
13 | var EPS = 1e-6;
14 |
15 | var ctrl,
16 | currentTime = 0;
17 |
18 | beforeEach(function () {
19 | // Mock time
20 | currentTime = 0;
21 | this.sinon.stub(window.performance, 'now', function () { return currentTime; });
22 | });
23 |
24 | beforeEach(function (done) {
25 | this.el = entityFactory();
26 | this.el.setAttribute('keyboard-controls', '');
27 | this.el.addEventListener('loaded', function () {
28 | ctrl = this.el.components['keyboard-controls'];
29 | done();
30 | }.bind(this));
31 | });
32 |
33 | /*******************************************************************
34 | * Tests
35 | */
36 |
37 | describe('Accessors', function () {
38 | it('is attached to component', function () {
39 | expect(ctrl).to.be.ok;
40 | });
41 | });
42 |
43 | });
44 |
--------------------------------------------------------------------------------