aframe-keyboard-controls
Advanced tools
Comparing version 1.0.0 to 1.1.0
@@ -81,6 +81,12 @@ /******/ (function(modules) { // webpackBootstrap | ||
* to the entity when pressing the keys. | ||
* @param {number} [angularAcceleration=Math.PI*0.25] - Determines the angular | ||
* acceleration given to the entity when pressing the keys. Only applied when | ||
* mode == 'fps'. Measured in Radians. | ||
* @param {bool} [enabled=true] - To completely enable or disable the controls | ||
* @param {bool} [fly=false] - Determines if the direction of the movement sticks | ||
* to the plane where the entity started off or if there are 6 degrees of | ||
* freedom as a diver underwater or a plane flying. | ||
* @param {string} [mode='default'] - | ||
* 'default' enforces the direction of the movement to stick to the plane | ||
* where the entity started off. | ||
* 'fps' extends 'default' by enabling "First Person Shooter" controls: W/S = | ||
* Forward/Back, Q/E = Strafe left/right, A/D = Rotate left/right | ||
* 'fly' enables 6 degrees of freedom as a diver underwater or a plane flying. | ||
* @param {string} [rollAxis='z'] - The front-to-back axis. | ||
@@ -90,16 +96,20 @@ * @param {string} [pitchAxis='x'] - The left-to-right axis. | ||
* @param {bool} [pitchAxisInverted=false] - Pitch axis is inverted | ||
* @param {bool} [yawAxisInverted=false] - Yaw axis is inverted. Used when | ||
* mode == 'fps' | ||
*/ | ||
module.exports = { | ||
schema: { | ||
easing: { default: 20 }, | ||
acceleration: { default: 65 }, | ||
enabled: { default: true }, | ||
fly: { default: false }, | ||
rollAxis: { default: 'z', oneOf: [ 'x', 'y', 'z' ] }, | ||
pitchAxis: { default: 'x', oneOf: [ 'x', 'y', 'z' ] }, | ||
rollAxisInverted: { default: false }, | ||
rollAxisEnabled: { default: true }, | ||
pitchAxisInverted: { default: false }, | ||
pitchAxisEnabled: { default: true }, | ||
debug: { default: false } | ||
easing: { default: 20 }, | ||
acceleration: { default: 65 }, | ||
angularAcceleration: { default: Math.PI / 4 }, | ||
enabled: { default: true }, | ||
mode: { default: 'default', oneOf: ['default', 'fly', 'fps']}, | ||
rollAxis: { default: 'z', oneOf: [ 'x', 'y', 'z' ] }, | ||
pitchAxis: { default: 'x', oneOf: [ 'x', 'y', 'z' ] }, | ||
rollAxisInverted: { default: false }, | ||
rollAxisEnabled: { default: true }, | ||
pitchAxisInverted: { default: false }, | ||
pitchAxisEnabled: { default: true }, | ||
yawAxisInverted: { default: false }, | ||
debug: { default: false } | ||
}, | ||
@@ -109,2 +119,3 @@ | ||
this.velocity = new THREE.Vector3(); | ||
this.angularVelocity = 0; | ||
this.localKeys = {}; | ||
@@ -123,58 +134,102 @@ this.listeners = { | ||
tick: function (t, dt) { | ||
var data = this.data; | ||
var acceleration = data.acceleration; | ||
var easing = data.easing; | ||
var velocity = this.velocity; | ||
var keys = this.getKeys(); | ||
var movementVector; | ||
var pitchAxis = data.pitchAxis; | ||
var rollAxis = data.rollAxis; | ||
var pitchSign = data.pitchAxisInverted ? -1 : 1; | ||
var rollSign = data.rollAxisInverted ? -1 : 1; | ||
var el = this.el; | ||
tick: (function () { | ||
var upVector = new THREE.Vector3(0, 1, 0); | ||
var rotation = new THREE.Euler(0, 0, 0, 'YXZ'); | ||
return function (t, dt) { | ||
var data = this.data; | ||
var acceleration = data.acceleration; | ||
var angularAcceleration = data.angularAcceleration; | ||
var easing = data.easing; | ||
var velocity = this.velocity; | ||
var keys = this.getKeys(); | ||
var movementVector; | ||
var pitchAxis = data.pitchAxis; | ||
var rollAxis = data.rollAxis; | ||
var pitchSign = data.pitchAxisInverted ? -1 : 1; | ||
var rollSign = data.rollAxisInverted ? -1 : 1; | ||
var yawSign = data.yawAxisInverted ? 1 : -1; | ||
var el = this.el; | ||
var strafeLeft = data.mode === 'fps' ? ['KeyQ', 'ArrowLeft'] : ['KeyA', 'ArrowLeft']; | ||
var strafeRight = data.mode === 'fps' ? ['KeyE', 'ArrowRight'] : ['KeyD', 'ArrowRight']; | ||
// If data changed or FPS too low, reset velocity. | ||
if (isNaN(dt) || dt > MAX_DELTA) { | ||
velocity[pitchAxis] = 0; | ||
velocity[rollAxis] = 0; | ||
return; | ||
} | ||
// If data changed or FPS too low, reset velocity. | ||
if (isNaN(dt) || dt > MAX_DELTA) { | ||
velocity[pitchAxis] = 0; | ||
velocity[rollAxis] = 0; | ||
this.angularVelocity = 0; | ||
return; | ||
} | ||
velocity[pitchAxis] -= velocity[pitchAxis] * easing * dt / 1000; | ||
velocity[rollAxis] -= velocity[rollAxis] * easing * dt / 1000; | ||
velocity[pitchAxis] -= velocity[pitchAxis] * easing * dt / 1000; | ||
velocity[rollAxis] -= velocity[rollAxis] * easing * dt / 1000; | ||
this.angularVelocity -= this.angularVelocity * easing * dt / 1000; | ||
var position = el.getComputedAttribute('position'); | ||
var position = el.getComputedAttribute('position'); | ||
if (data.enabled) { | ||
if (data.pitchAxisEnabled) { | ||
if (keys.KeyA || keys.ArrowLeft) { | ||
velocity[pitchAxis] -= pitchSign * acceleration * dt / 1000; | ||
if (data.enabled) { | ||
if (data.pitchAxisEnabled) { | ||
if (strafeLeft.some(key => keys[key])) { | ||
velocity[pitchAxis] -= pitchSign * acceleration * dt / 1000; | ||
} | ||
if (strafeRight.some(key => keys[key])) { | ||
velocity[pitchAxis] += pitchSign * acceleration * dt / 1000; | ||
} | ||
} | ||
if (keys.KeyD || keys.ArrowRight) { | ||
velocity[pitchAxis] += pitchSign * acceleration * dt / 1000; | ||
if (data.rollAxisEnabled) { | ||
if (keys.KeyW || keys.ArrowUp) { | ||
velocity[rollAxis] -= rollSign * acceleration * dt / 1000; | ||
} | ||
if (keys.KeyS || keys.ArrowDown) { | ||
velocity[rollAxis] += rollSign * acceleration * dt / 1000; | ||
} | ||
} | ||
} | ||
if (data.rollAxisEnabled) { | ||
if (keys.KeyW || keys.ArrowUp) { | ||
velocity[rollAxis] -= rollSign * acceleration * dt / 1000; | ||
if (data.mode === 'fps') { | ||
if (keys.KeyA) { | ||
this.angularVelocity -= yawSign * angularAcceleration * dt / 1000; | ||
} | ||
if (keys.KeyD) { | ||
this.angularVelocity += yawSign * angularAcceleration * dt / 1000; | ||
} | ||
} | ||
if (keys.KeyS || keys.ArrowDown) { | ||
velocity[rollAxis] += rollSign * acceleration * dt / 1000; | ||
} | ||
} | ||
} | ||
movementVector = this.getMovementVector(dt); | ||
el.object3D.translateX(movementVector.x); | ||
el.object3D.translateY(movementVector.y); | ||
el.object3D.translateZ(movementVector.z); | ||
if (data.mode === 'fps') { | ||
var elRotation = this.el.getAttribute('rotation'); | ||
this.rotateOnAxis(rotation, upVector, this.angularVelocity); | ||
el.setAttribute('position', { | ||
x: position.x + movementVector.x, | ||
y: position.y + movementVector.y, | ||
z: position.z + movementVector.z | ||
}); | ||
}, | ||
el.setAttribute('rotation', { | ||
x: THREE.Math.radToDeg(rotation.x), | ||
y: THREE.Math.radToDeg(rotation.y), | ||
z: THREE.Math.radToDeg(rotation.z) | ||
}); | ||
} | ||
movementVector = this.getMovementVector(dt); | ||
el.object3D.translateX(movementVector.x); | ||
el.object3D.translateY(movementVector.y); | ||
el.object3D.translateZ(movementVector.z); | ||
el.setAttribute('position', { | ||
x: position.x + movementVector.x, | ||
y: position.y + movementVector.y, | ||
z: position.z + movementVector.z | ||
}); | ||
}; | ||
})(), | ||
rotateOnAxis: (function () { | ||
var quaternion = new THREE.Quaternion(); | ||
var eulerAsQuaternion = new THREE.Quaternion(); | ||
return function (euler, axis, angle) { | ||
quaternion.setFromAxisAngle(axis, angle); | ||
eulerAsQuaternion.setFromEuler(euler); | ||
eulerAsQuaternion.multiply(quaternion); | ||
euler.setFromQuaternion(eulerAsQuaternion, euler.order); | ||
}; | ||
})(), | ||
getMovementVector: (function () { | ||
@@ -189,3 +244,3 @@ var direction = new THREE.Vector3(0, 0, 0); | ||
if (!elRotation) { return direction; } | ||
if (!this.data.fly) { elRotation.x = 0; } | ||
if (this.data.mode !== 'fly') { elRotation.x = 0; } | ||
rotation.set(THREE.Math.degToRad(elRotation.x), | ||
@@ -192,0 +247,0 @@ THREE.Math.degToRad(elRotation.y), 0); |
@@ -1,1 +0,1070 @@ | ||
!function(e){function o(i){if(t[i])return t[i].exports;var c=t[i]={exports:{},id:i,loaded:!1};return e[i].call(c.exports,c,c.exports,o),c.loaded=!0,c.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,c="__keyboard-controls-proxy",a=window.KeyboardEvent;e.exports={schema:{easing:{"default":20},acceleration:{"default":65},enabled:{"default":!0},fly:{"default":!1},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},debug:{"default":!1}},init:function(){this.velocity=new THREE.Vector3,this.localKeys={},this.listeners={keydown:this.onKeyDown.bind(this),keyup:this.onKeyUp.bind(this),blur:this.onBlur.bind(this)},this.attachEventListeners()},tick:function(e,o){var t,c=this.data,a=c.acceleration,n=c.easing,d=this.velocity,r=this.getKeys(),y=c.pitchAxis,l=c.rollAxis,s=c.pitchAxisInverted?-1:1,p=c.rollAxisInverted?-1:1,k=this.el;if(isNaN(o)||o>i)return d[y]=0,void(d[l]=0);d[y]-=d[y]*n*o/1e3,d[l]-=d[l]*n*o/1e3;var u=k.getComputedAttribute("position");c.enabled&&(c.pitchAxisEnabled&&((r.KeyA||r.ArrowLeft)&&(d[y]-=s*a*o/1e3),(r.KeyD||r.ArrowRight)&&(d[y]+=s*a*o/1e3)),c.rollAxisEnabled&&((r.KeyW||r.ArrowUp)&&(d[l]-=p*a*o/1e3),(r.KeyS||r.ArrowDown)&&(d[l]+=p*a*o/1e3))),t=this.getMovementVector(o),k.object3D.translateX(t.x),k.object3D.translateY(t.y),k.object3D.translateZ(t.z),k.setAttribute("position",{x:u.x+t.x,y:u.y+t.y,z:u.z+t.z})},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,c=this.el.getAttribute("rotation");return e.copy(i),e.multiplyScalar(t/1e3),c?(this.data.fly||(c.x=0),o.set(THREE.Math.degToRad(c.x),THREE.Math.degToRad(c.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){c in e&&this.el.emit(e.type,e),this.el.emit(e.type+":"+e.code,new a(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){(k===o||p===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 c=e[i];o in c&&(t[c[o]]=c)}),t}function c(e){var o="keyCode"in e?e.keyCode:"which"in e?e.which:0,t=function(){if(v||"keyLocation"in e){var t=v?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 u?u[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 a(e,o){if(e=String(e),!m.hasOwnProperty(e))return"Undefined";if(o&&"en-us"!==String(o).toLowerCase())throw Error("Unsupported locale");var t=m[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":""}(),p=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":""}(),k=p+"-"+s,u={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(u,"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(u,"moz-mac",{12:{code:"NumLock",location:l},173:{code:"Minus",keyCap:"-"}}),t(u,"moz-win",{173:{code:"Minus",keyCap:"-"}}),t(u,"chrome-mac",{93:{code:"OSRight",location:y}}),t(u,"safari",{3:{code:"Enter"},25:{code:"Tab"}}),t(u,"ios",{10:{code:"Enter",location:d}}),t(u,"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"===k&&(f["U+0010"]={code:"ContextMenu"}),"safari-mac"===k&&(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 C={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"},m=i(u,"code");try{var v=n&&"location"in new KeyboardEvent("")}catch(g){}"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=c(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 C.hasOwnProperty(e)?C[e]:e}})}e(KeyboardEvent.prototype,"key",{get:function(){var e=c(this);return e&&"key"in e?e.key:"Unidentified"}}),e(KeyboardEvent.prototype,"location",{get:function(){var e=c(this);return e&&"location"in e?e.location:d}}),e(KeyboardEvent.prototype,"locale",{get:function(){return""}})}(),"queryKeyCap"in e.KeyboardEvent||(e.KeyboardEvent.queryKeyCap=a),e.identifyKey=function(e){if(!("code"in e)){var o=c(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)}]); | ||
/******/ (function(modules) { // webpackBootstrap | ||
/******/ // The module cache | ||
/******/ var installedModules = {}; | ||
/******/ | ||
/******/ // The require function | ||
/******/ function __webpack_require__(moduleId) { | ||
/******/ | ||
/******/ // Check if module is in cache | ||
/******/ if(installedModules[moduleId]) | ||
/******/ return installedModules[moduleId].exports; | ||
/******/ | ||
/******/ // Create a new module (and put it into the cache) | ||
/******/ var module = installedModules[moduleId] = { | ||
/******/ exports: {}, | ||
/******/ id: moduleId, | ||
/******/ loaded: false | ||
/******/ }; | ||
/******/ | ||
/******/ // Execute the module function | ||
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); | ||
/******/ | ||
/******/ // Flag the module as loaded | ||
/******/ module.loaded = true; | ||
/******/ | ||
/******/ // Return the exports of the module | ||
/******/ return module.exports; | ||
/******/ } | ||
/******/ | ||
/******/ | ||
/******/ // expose the modules object (__webpack_modules__) | ||
/******/ __webpack_require__.m = modules; | ||
/******/ | ||
/******/ // expose the module cache | ||
/******/ __webpack_require__.c = installedModules; | ||
/******/ | ||
/******/ // __webpack_public_path__ | ||
/******/ __webpack_require__.p = ""; | ||
/******/ | ||
/******/ // Load entry module and return exports | ||
/******/ return __webpack_require__(0); | ||
/******/ }) | ||
/************************************************************************/ | ||
/******/ ([ | ||
/* 0 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
// Browser distrubution of the A-Frame component. | ||
(function (AFRAME) { | ||
if (!AFRAME) { | ||
console.error('Component attempted to register before AFRAME was available.'); | ||
return; | ||
} | ||
(AFRAME.aframeCore || AFRAME).registerComponent('keyboard-controls', __webpack_require__(1)); | ||
}(window.AFRAME)); | ||
/***/ }, | ||
/* 1 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
__webpack_require__(2); | ||
var MAX_DELTA = 200, | ||
PROXY_FLAG = '__keyboard-controls-proxy'; | ||
var KeyboardEvent = window.KeyboardEvent; | ||
/** | ||
* Keyboard Controls component. | ||
* | ||
* Bind keyboard events to components, or control your entities with the WASD keys. | ||
* | ||
* @namespace wasd-controls | ||
* @param {number} [easing=20] - How fast the movement decelerates. If you hold the | ||
* keys the entity moves and if you release it will stop. Easing simulates friction. | ||
* @param {number} [acceleration=65] - Determines the acceleration given | ||
* to the entity when pressing the keys. | ||
* @param {number} [angularAcceleration=Math.PI*0.25] - Determines the angular | ||
* acceleration given to the entity when pressing the keys. Only applied when | ||
* mode == 'fps'. Measured in Radians. | ||
* @param {bool} [enabled=true] - To completely enable or disable the controls | ||
* @param {string} [mode='default'] - | ||
* 'default' enforces the direction of the movement to stick to the plane | ||
* where the entity started off. | ||
* 'fps' extends 'default' by enabling "First Person Shooter" controls: W/S = | ||
* Forward/Back, Q/E = Strafe left/right, A/D = Rotate left/right | ||
* 'fly' enables 6 degrees of freedom as a diver underwater or a plane flying. | ||
* @param {string} [rollAxis='z'] - The front-to-back axis. | ||
* @param {string} [pitchAxis='x'] - The left-to-right axis. | ||
* @param {bool} [rollAxisInverted=false] - Roll axis is inverted | ||
* @param {bool} [pitchAxisInverted=false] - Pitch axis is inverted | ||
* @param {bool} [yawAxisInverted=false] - Yaw axis is inverted. Used when | ||
* mode == 'fps' | ||
*/ | ||
module.exports = { | ||
schema: { | ||
easing: { default: 20 }, | ||
acceleration: { default: 65 }, | ||
angularAcceleration: { default: Math.PI / 4 }, | ||
enabled: { default: true }, | ||
mode: { default: 'default', oneOf: ['default', 'fly', 'fps']}, | ||
rollAxis: { default: 'z', oneOf: [ 'x', 'y', 'z' ] }, | ||
pitchAxis: { default: 'x', oneOf: [ 'x', 'y', 'z' ] }, | ||
rollAxisInverted: { default: false }, | ||
rollAxisEnabled: { default: true }, | ||
pitchAxisInverted: { default: false }, | ||
pitchAxisEnabled: { default: true }, | ||
yawAxisInverted: { default: false }, | ||
debug: { default: false } | ||
}, | ||
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(); | ||
}, | ||
/******************************************************************* | ||
* Movement | ||
*/ | ||
tick: (function () { | ||
var upVector = new THREE.Vector3(0, 1, 0); | ||
var rotation = new THREE.Euler(0, 0, 0, 'YXZ'); | ||
return function (t, dt) { | ||
var data = this.data; | ||
var acceleration = data.acceleration; | ||
var angularAcceleration = data.angularAcceleration; | ||
var easing = data.easing; | ||
var velocity = this.velocity; | ||
var keys = this.getKeys(); | ||
var movementVector; | ||
var pitchAxis = data.pitchAxis; | ||
var rollAxis = data.rollAxis; | ||
var pitchSign = data.pitchAxisInverted ? -1 : 1; | ||
var rollSign = data.rollAxisInverted ? -1 : 1; | ||
var yawSign = data.yawAxisInverted ? 1 : -1; | ||
var el = this.el; | ||
var strafeLeft = data.mode === 'fps' ? ['KeyQ', 'ArrowLeft'] : ['KeyA', 'ArrowLeft']; | ||
var strafeRight = data.mode === 'fps' ? ['KeyE', 'ArrowRight'] : ['KeyD', 'ArrowRight']; | ||
// If data changed or FPS too low, reset velocity. | ||
if (isNaN(dt) || dt > MAX_DELTA) { | ||
velocity[pitchAxis] = 0; | ||
velocity[rollAxis] = 0; | ||
this.angularVelocity = 0; | ||
return; | ||
} | ||
velocity[pitchAxis] -= velocity[pitchAxis] * easing * dt / 1000; | ||
velocity[rollAxis] -= velocity[rollAxis] * easing * dt / 1000; | ||
this.angularVelocity -= this.angularVelocity * easing * dt / 1000; | ||
var position = el.getComputedAttribute('position'); | ||
if (data.enabled) { | ||
if (data.pitchAxisEnabled) { | ||
if (strafeLeft.some(key => keys[key])) { | ||
velocity[pitchAxis] -= pitchSign * acceleration * dt / 1000; | ||
} | ||
if (strafeRight.some(key => keys[key])) { | ||
velocity[pitchAxis] += pitchSign * acceleration * dt / 1000; | ||
} | ||
} | ||
if (data.rollAxisEnabled) { | ||
if (keys.KeyW || keys.ArrowUp) { | ||
velocity[rollAxis] -= rollSign * acceleration * dt / 1000; | ||
} | ||
if (keys.KeyS || keys.ArrowDown) { | ||
velocity[rollAxis] += rollSign * acceleration * dt / 1000; | ||
} | ||
} | ||
if (data.mode === 'fps') { | ||
if (keys.KeyA) { | ||
this.angularVelocity -= yawSign * angularAcceleration * dt / 1000; | ||
} | ||
if (keys.KeyD) { | ||
this.angularVelocity += yawSign * angularAcceleration * dt / 1000; | ||
} | ||
} | ||
} | ||
if (data.mode === 'fps') { | ||
var elRotation = this.el.getAttribute('rotation'); | ||
this.rotateOnAxis(rotation, upVector, this.angularVelocity); | ||
el.setAttribute('rotation', { | ||
x: THREE.Math.radToDeg(rotation.x), | ||
y: THREE.Math.radToDeg(rotation.y), | ||
z: THREE.Math.radToDeg(rotation.z) | ||
}); | ||
} | ||
movementVector = this.getMovementVector(dt); | ||
el.object3D.translateX(movementVector.x); | ||
el.object3D.translateY(movementVector.y); | ||
el.object3D.translateZ(movementVector.z); | ||
el.setAttribute('position', { | ||
x: position.x + movementVector.x, | ||
y: position.y + movementVector.y, | ||
z: position.z + movementVector.z | ||
}); | ||
}; | ||
})(), | ||
rotateOnAxis: (function () { | ||
var quaternion = new THREE.Quaternion(); | ||
var eulerAsQuaternion = new THREE.Quaternion(); | ||
return function (euler, axis, angle) { | ||
quaternion.setFromAxisAngle(axis, angle); | ||
eulerAsQuaternion.setFromEuler(euler); | ||
eulerAsQuaternion.multiply(quaternion); | ||
euler.setFromQuaternion(eulerAsQuaternion, euler.order); | ||
}; | ||
})(), | ||
getMovementVector: (function () { | ||
var direction = new THREE.Vector3(0, 0, 0); | ||
var rotation = new THREE.Euler(0, 0, 0, 'YXZ'); | ||
return function (dt) { | ||
var velocity = this.velocity; | ||
var elRotation = this.el.getAttribute('rotation'); | ||
direction.copy(velocity); | ||
direction.multiplyScalar(dt / 1000); | ||
if (!elRotation) { return direction; } | ||
if (this.data.mode !== 'fly') { elRotation.x = 0; } | ||
rotation.set(THREE.Math.degToRad(elRotation.x), | ||
THREE.Math.degToRad(elRotation.y), 0); | ||
direction.applyEuler(rotation); | ||
return direction; | ||
}; | ||
})(), | ||
/******************************************************************* | ||
* Events | ||
*/ | ||
play: function () { | ||
this.attachEventListeners(); | ||
}, | ||
pause: function () { | ||
this.removeEventListeners(); | ||
}, | ||
remove: function () { | ||
this.pause(); | ||
}, | ||
attachEventListeners: function () { | ||
window.addEventListener('keydown', this.listeners.keydown, false); | ||
window.addEventListener('keyup', this.listeners.keyup, false); | ||
window.addEventListener('blur', this.listeners.blur, false); | ||
}, | ||
removeEventListeners: function () { | ||
window.removeEventListener('keydown', this.listeners.keydown); | ||
window.removeEventListener('keyup', this.listeners.keyup); | ||
window.removeEventListener('blur', this.listeners.blur); | ||
}, | ||
onKeyDown: function (event) { | ||
this.localKeys[event.code] = true; | ||
this.emit(event); | ||
}, | ||
onKeyUp: function (event) { | ||
delete this.localKeys[event.code]; | ||
this.emit(event); | ||
}, | ||
onBlur: function () { | ||
for (var code in this.localKeys) { | ||
if (this.localKeys.hasOwnProperty(code)) { | ||
delete this.localKeys[code]; | ||
} | ||
} | ||
}, | ||
emit: function (event) { | ||
// TODO - keydown only initially? | ||
// TODO - where the f is the spacebar | ||
// Emit original event. | ||
if (PROXY_FLAG in event) { | ||
// TODO - Method never triggered. | ||
this.el.emit(event.type, event); | ||
} | ||
// Emit convenience event, identifying key. | ||
this.el.emit(event.type + ':' + event.code, new KeyboardEvent(event.type, event)); | ||
if (this.data.debug) console.log(event.type + ':' + event.code); | ||
}, | ||
/******************************************************************* | ||
* Accessors | ||
*/ | ||
isPressed: function (code) { | ||
return code in this.getKeys(); | ||
}, | ||
getKeys: function () { | ||
if (this.isProxied()) { | ||
return this.el.sceneEl.components['proxy-controls'].getKeyboard(); | ||
} | ||
return this.localKeys; | ||
}, | ||
isProxied: function () { | ||
var proxyControls = this.el.sceneEl.components['proxy-controls']; | ||
return proxyControls && proxyControls.isConnected(); | ||
} | ||
}; | ||
/***/ }, | ||
/* 2 */ | ||
/***/ function(module, exports) { | ||
/** | ||
* Polyfill for the additional KeyboardEvent properties defined in the D3E and | ||
* D4E draft specifications, by @inexorabletash. | ||
* | ||
* See: https://github.com/inexorabletash/polyfill | ||
*/ | ||
(function(global) { | ||
var nativeKeyboardEvent = ('KeyboardEvent' in global); | ||
if (!nativeKeyboardEvent) | ||
global.KeyboardEvent = function KeyboardEvent() { throw TypeError('Illegal constructor'); }; | ||
global.KeyboardEvent.DOM_KEY_LOCATION_STANDARD = 0x00; // Default or unknown location | ||
global.KeyboardEvent.DOM_KEY_LOCATION_LEFT = 0x01; // e.g. Left Alt key | ||
global.KeyboardEvent.DOM_KEY_LOCATION_RIGHT = 0x02; // e.g. Right Alt key | ||
global.KeyboardEvent.DOM_KEY_LOCATION_NUMPAD = 0x03; // e.g. Numpad 0 or + | ||
var STANDARD = window.KeyboardEvent.DOM_KEY_LOCATION_STANDARD, | ||
LEFT = window.KeyboardEvent.DOM_KEY_LOCATION_LEFT, | ||
RIGHT = window.KeyboardEvent.DOM_KEY_LOCATION_RIGHT, | ||
NUMPAD = window.KeyboardEvent.DOM_KEY_LOCATION_NUMPAD; | ||
//-------------------------------------------------------------------- | ||
// | ||
// Utilities | ||
// | ||
//-------------------------------------------------------------------- | ||
function contains(s, ss) { return String(s).indexOf(ss) !== -1; } | ||
var os = (function() { | ||
if (contains(navigator.platform, 'Win')) { return 'win'; } | ||
if (contains(navigator.platform, 'Mac')) { return 'mac'; } | ||
if (contains(navigator.platform, 'CrOS')) { return 'cros'; } | ||
if (contains(navigator.platform, 'Linux')) { return 'linux'; } | ||
if (contains(navigator.userAgent, 'iPad') || contains(navigator.platform, 'iPod') || contains(navigator.platform, 'iPhone')) { return 'ios'; } | ||
return ''; | ||
} ()); | ||
var browser = (function() { | ||
if (contains(navigator.userAgent, 'Chrome/')) { return 'chrome'; } | ||
if (contains(navigator.vendor, 'Apple')) { return 'safari'; } | ||
if (contains(navigator.userAgent, 'MSIE')) { return 'ie'; } | ||
if (contains(navigator.userAgent, 'Gecko/')) { return 'moz'; } | ||
if (contains(navigator.userAgent, 'Opera/')) { return 'opera'; } | ||
return ''; | ||
} ()); | ||
var browser_os = browser + '-' + os; | ||
function mergeIf(baseTable, select, table) { | ||
if (browser_os === select || browser === select || os === select) { | ||
Object.keys(table).forEach(function(keyCode) { | ||
baseTable[keyCode] = table[keyCode]; | ||
}); | ||
} | ||
} | ||
function remap(o, key) { | ||
var r = {}; | ||
Object.keys(o).forEach(function(k) { | ||
var item = o[k]; | ||
if (key in item) { | ||
r[item[key]] = item; | ||
} | ||
}); | ||
return r; | ||
} | ||
function invert(o) { | ||
var r = {}; | ||
Object.keys(o).forEach(function(k) { | ||
r[o[k]] = k; | ||
}); | ||
return r; | ||
} | ||
//-------------------------------------------------------------------- | ||
// | ||
// Generic Mappings | ||
// | ||
//-------------------------------------------------------------------- | ||
// "keyInfo" is a dictionary: | ||
// code: string - name from DOM Level 3 KeyboardEvent code Values | ||
// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3Events-code.html | ||
// location (optional): number - one of the DOM_KEY_LOCATION values | ||
// keyCap (optional): string - keyboard label in en-US locale | ||
// USB code Usage ID from page 0x07 unless otherwise noted (Informative) | ||
// Map of keyCode to keyInfo | ||
var keyCodeToInfoTable = { | ||
// 0x01 - VK_LBUTTON | ||
// 0x02 - VK_RBUTTON | ||
0x03: { code: 'Cancel' }, // [USB: 0x9b] char \x0018 ??? (Not in D3E) | ||
// 0x04 - VK_MBUTTON | ||
// 0x05 - VK_XBUTTON1 | ||
// 0x06 - VK_XBUTTON2 | ||
0x06: { code: 'Help' }, // [USB: 0x75] ??? | ||
// 0x07 - undefined | ||
0x08: { code: 'Backspace' }, // [USB: 0x2a] Labelled Delete on Macintosh keyboards. | ||
0x09: { code: 'Tab' }, // [USB: 0x2b] | ||
// 0x0A-0x0B - reserved | ||
0X0C: { code: 'Clear' }, // [USB: 0x9c] NumPad Center (Not in D3E) | ||
0X0D: { code: 'Enter' }, // [USB: 0x28] | ||
// 0x0E-0x0F - undefined | ||
0x10: { code: 'Shift' }, | ||
0x11: { code: 'Control' }, | ||
0x12: { code: 'Alt' }, | ||
0x13: { code: 'Pause' }, // [USB: 0x48] | ||
0x14: { code: 'CapsLock' }, // [USB: 0x39] | ||
0x15: { code: 'KanaMode' }, // [USB: 0x88] - "HangulMode" for Korean layout | ||
0x16: { code: 'HangulMode' }, // [USB: 0x90] 0x15 as well in MSDN VK table ??? | ||
0x17: { code: 'JunjaMode' }, // (Not in D3E) | ||
0x18: { code: 'FinalMode' }, // (Not in D3E) | ||
0x19: { code: 'KanjiMode' }, // [USB: 0x91] - "HanjaMode" for Korean layout | ||
// 0x1A - undefined | ||
0x1B: { code: 'Escape' }, // [USB: 0x29] | ||
0x1C: { code: 'Convert' }, // [USB: 0x8a] | ||
0x1D: { code: 'NonConvert' }, // [USB: 0x8b] | ||
0x1E: { code: 'Accept' }, // (Not in D3E) | ||
0x1F: { code: 'ModeChange' }, // (Not in D3E) | ||
0x20: { code: 'Space' }, // [USB: 0x2c] | ||
0x21: { code: 'PageUp' }, // [USB: 0x4b] | ||
0x22: { code: 'PageDown' }, // [USB: 0x4e] | ||
0x23: { code: 'End' }, // [USB: 0x4d] | ||
0x24: { code: 'Home' }, // [USB: 0x4a] | ||
0x25: { code: 'ArrowLeft' }, // [USB: 0x50] | ||
0x26: { code: 'ArrowUp' }, // [USB: 0x52] | ||
0x27: { code: 'ArrowRight' }, // [USB: 0x4f] | ||
0x28: { code: 'ArrowDown' }, // [USB: 0x51] | ||
0x29: { code: 'Select' }, // (Not in D3E) | ||
0x2A: { code: 'Print' }, // (Not in D3E) | ||
0x2B: { code: 'Execute' }, // [USB: 0x74] (Not in D3E) | ||
0x2C: { code: 'PrintScreen' }, // [USB: 0x46] | ||
0x2D: { code: 'Insert' }, // [USB: 0x49] | ||
0x2E: { code: 'Delete' }, // [USB: 0x4c] | ||
0x2F: { code: 'Help' }, // [USB: 0x75] ??? | ||
0x30: { code: 'Digit0', keyCap: '0' }, // [USB: 0x27] 0) | ||
0x31: { code: 'Digit1', keyCap: '1' }, // [USB: 0x1e] 1! | ||
0x32: { code: 'Digit2', keyCap: '2' }, // [USB: 0x1f] 2@ | ||
0x33: { code: 'Digit3', keyCap: '3' }, // [USB: 0x20] 3# | ||
0x34: { code: 'Digit4', keyCap: '4' }, // [USB: 0x21] 4$ | ||
0x35: { code: 'Digit5', keyCap: '5' }, // [USB: 0x22] 5% | ||
0x36: { code: 'Digit6', keyCap: '6' }, // [USB: 0x23] 6^ | ||
0x37: { code: 'Digit7', keyCap: '7' }, // [USB: 0x24] 7& | ||
0x38: { code: 'Digit8', keyCap: '8' }, // [USB: 0x25] 8* | ||
0x39: { code: 'Digit9', keyCap: '9' }, // [USB: 0x26] 9( | ||
// 0x3A-0x40 - undefined | ||
0x41: { code: 'KeyA', keyCap: 'a' }, // [USB: 0x04] | ||
0x42: { code: 'KeyB', keyCap: 'b' }, // [USB: 0x05] | ||
0x43: { code: 'KeyC', keyCap: 'c' }, // [USB: 0x06] | ||
0x44: { code: 'KeyD', keyCap: 'd' }, // [USB: 0x07] | ||
0x45: { code: 'KeyE', keyCap: 'e' }, // [USB: 0x08] | ||
0x46: { code: 'KeyF', keyCap: 'f' }, // [USB: 0x09] | ||
0x47: { code: 'KeyG', keyCap: 'g' }, // [USB: 0x0a] | ||
0x48: { code: 'KeyH', keyCap: 'h' }, // [USB: 0x0b] | ||
0x49: { code: 'KeyI', keyCap: 'i' }, // [USB: 0x0c] | ||
0x4A: { code: 'KeyJ', keyCap: 'j' }, // [USB: 0x0d] | ||
0x4B: { code: 'KeyK', keyCap: 'k' }, // [USB: 0x0e] | ||
0x4C: { code: 'KeyL', keyCap: 'l' }, // [USB: 0x0f] | ||
0x4D: { code: 'KeyM', keyCap: 'm' }, // [USB: 0x10] | ||
0x4E: { code: 'KeyN', keyCap: 'n' }, // [USB: 0x11] | ||
0x4F: { code: 'KeyO', keyCap: 'o' }, // [USB: 0x12] | ||
0x50: { code: 'KeyP', keyCap: 'p' }, // [USB: 0x13] | ||
0x51: { code: 'KeyQ', keyCap: 'q' }, // [USB: 0x14] | ||
0x52: { code: 'KeyR', keyCap: 'r' }, // [USB: 0x15] | ||
0x53: { code: 'KeyS', keyCap: 's' }, // [USB: 0x16] | ||
0x54: { code: 'KeyT', keyCap: 't' }, // [USB: 0x17] | ||
0x55: { code: 'KeyU', keyCap: 'u' }, // [USB: 0x18] | ||
0x56: { code: 'KeyV', keyCap: 'v' }, // [USB: 0x19] | ||
0x57: { code: 'KeyW', keyCap: 'w' }, // [USB: 0x1a] | ||
0x58: { code: 'KeyX', keyCap: 'x' }, // [USB: 0x1b] | ||
0x59: { code: 'KeyY', keyCap: 'y' }, // [USB: 0x1c] | ||
0x5A: { code: 'KeyZ', keyCap: 'z' }, // [USB: 0x1d] | ||
0x5B: { code: 'OSLeft', location: LEFT }, // [USB: 0xe3] | ||
0x5C: { code: 'OSRight', location: RIGHT }, // [USB: 0xe7] | ||
0x5D: { code: 'ContextMenu' }, // [USB: 0x65] Context Menu | ||
// 0x5E - reserved | ||
0x5F: { code: 'Standby' }, // [USB: 0x82] Sleep | ||
0x60: { code: 'Numpad0', keyCap: '0', location: NUMPAD }, // [USB: 0x62] | ||
0x61: { code: 'Numpad1', keyCap: '1', location: NUMPAD }, // [USB: 0x59] | ||
0x62: { code: 'Numpad2', keyCap: '2', location: NUMPAD }, // [USB: 0x5a] | ||
0x63: { code: 'Numpad3', keyCap: '3', location: NUMPAD }, // [USB: 0x5b] | ||
0x64: { code: 'Numpad4', keyCap: '4', location: NUMPAD }, // [USB: 0x5c] | ||
0x65: { code: 'Numpad5', keyCap: '5', location: NUMPAD }, // [USB: 0x5d] | ||
0x66: { code: 'Numpad6', keyCap: '6', location: NUMPAD }, // [USB: 0x5e] | ||
0x67: { code: 'Numpad7', keyCap: '7', location: NUMPAD }, // [USB: 0x5f] | ||
0x68: { code: 'Numpad8', keyCap: '8', location: NUMPAD }, // [USB: 0x60] | ||
0x69: { code: 'Numpad9', keyCap: '9', location: NUMPAD }, // [USB: 0x61] | ||
0x6A: { code: 'NumpadMultiply', keyCap: '*', location: NUMPAD }, // [USB: 0x55] | ||
0x6B: { code: 'NumpadAdd', keyCap: '+', location: NUMPAD }, // [USB: 0x57] | ||
0x6C: { code: 'NumpadComma', keyCap: ',', location: NUMPAD }, // [USB: 0x85] | ||
0x6D: { code: 'NumpadSubtract', keyCap: '-', location: NUMPAD }, // [USB: 0x56] | ||
0x6E: { code: 'NumpadDecimal', keyCap: '.', location: NUMPAD }, // [USB: 0x63] | ||
0x6F: { code: 'NumpadDivide', keyCap: '/', location: NUMPAD }, // [USB: 0x54] | ||
0x70: { code: 'F1' }, // [USB: 0x3a] | ||
0x71: { code: 'F2' }, // [USB: 0x3b] | ||
0x72: { code: 'F3' }, // [USB: 0x3c] | ||
0x73: { code: 'F4' }, // [USB: 0x3d] | ||
0x74: { code: 'F5' }, // [USB: 0x3e] | ||
0x75: { code: 'F6' }, // [USB: 0x3f] | ||
0x76: { code: 'F7' }, // [USB: 0x40] | ||
0x77: { code: 'F8' }, // [USB: 0x41] | ||
0x78: { code: 'F9' }, // [USB: 0x42] | ||
0x79: { code: 'F10' }, // [USB: 0x43] | ||
0x7A: { code: 'F11' }, // [USB: 0x44] | ||
0x7B: { code: 'F12' }, // [USB: 0x45] | ||
0x7C: { code: 'F13' }, // [USB: 0x68] | ||
0x7D: { code: 'F14' }, // [USB: 0x69] | ||
0x7E: { code: 'F15' }, // [USB: 0x6a] | ||
0x7F: { code: 'F16' }, // [USB: 0x6b] | ||
0x80: { code: 'F17' }, // [USB: 0x6c] | ||
0x81: { code: 'F18' }, // [USB: 0x6d] | ||
0x82: { code: 'F19' }, // [USB: 0x6e] | ||
0x83: { code: 'F20' }, // [USB: 0x6f] | ||
0x84: { code: 'F21' }, // [USB: 0x70] | ||
0x85: { code: 'F22' }, // [USB: 0x71] | ||
0x86: { code: 'F23' }, // [USB: 0x72] | ||
0x87: { code: 'F24' }, // [USB: 0x73] | ||
// 0x88-0x8F - unassigned | ||
0x90: { code: 'NumLock', location: NUMPAD }, // [USB: 0x53] | ||
0x91: { code: 'ScrollLock' }, // [USB: 0x47] | ||
// 0x92-0x96 - OEM specific | ||
// 0x97-0x9F - unassigned | ||
// NOTE: 0xA0-0xA5 usually mapped to 0x10-0x12 in browsers | ||
0xA0: { code: 'ShiftLeft', location: LEFT }, // [USB: 0xe1] | ||
0xA1: { code: 'ShiftRight', location: RIGHT }, // [USB: 0xe5] | ||
0xA2: { code: 'ControlLeft', location: LEFT }, // [USB: 0xe0] | ||
0xA3: { code: 'ControlRight', location: RIGHT }, // [USB: 0xe4] | ||
0xA4: { code: 'AltLeft', location: LEFT }, // [USB: 0xe2] | ||
0xA5: { code: 'AltRight', location: RIGHT }, // [USB: 0xe6] | ||
0xA6: { code: 'BrowserBack' }, // [USB: 0x0c/0x0224] | ||
0xA7: { code: 'BrowserForward' }, // [USB: 0x0c/0x0225] | ||
0xA8: { code: 'BrowserRefresh' }, // [USB: 0x0c/0x0227] | ||
0xA9: { code: 'BrowserStop' }, // [USB: 0x0c/0x0226] | ||
0xAA: { code: 'BrowserSearch' }, // [USB: 0x0c/0x0221] | ||
0xAB: { code: 'BrowserFavorites' }, // [USB: 0x0c/0x0228] | ||
0xAC: { code: 'BrowserHome' }, // [USB: 0x0c/0x0222] | ||
0xAD: { code: 'VolumeMute' }, // [USB: 0x7f] | ||
0xAE: { code: 'VolumeDown' }, // [USB: 0x81] | ||
0xAF: { code: 'VolumeUp' }, // [USB: 0x80] | ||
0xB0: { code: 'MediaTrackNext' }, // [USB: 0x0c/0x00b5] | ||
0xB1: { code: 'MediaTrackPrevious' }, // [USB: 0x0c/0x00b6] | ||
0xB2: { code: 'MediaStop' }, // [USB: 0x0c/0x00b7] | ||
0xB3: { code: 'MediaPlayPause' }, // [USB: 0x0c/0x00cd] | ||
0xB4: { code: 'LaunchMail' }, // [USB: 0x0c/0x018a] | ||
0xB5: { code: 'MediaSelect' }, | ||
0xB6: { code: 'LaunchApp1' }, | ||
0xB7: { code: 'LaunchApp2' }, | ||
// 0xB8-0xB9 - reserved | ||
0xBA: { code: 'Semicolon', keyCap: ';' }, // [USB: 0x33] ;: (US Standard 101) | ||
0xBB: { code: 'Equal', keyCap: '=' }, // [USB: 0x2e] =+ | ||
0xBC: { code: 'Comma', keyCap: ',' }, // [USB: 0x36] ,< | ||
0xBD: { code: 'Minus', keyCap: '-' }, // [USB: 0x2d] -_ | ||
0xBE: { code: 'Period', keyCap: '.' }, // [USB: 0x37] .> | ||
0xBF: { code: 'Slash', keyCap: '/' }, // [USB: 0x38] /? (US Standard 101) | ||
0xC0: { code: 'Backquote', keyCap: '`' }, // [USB: 0x35] `~ (US Standard 101) | ||
// 0xC1-0xCF - reserved | ||
// 0xD0-0xD7 - reserved | ||
// 0xD8-0xDA - unassigned | ||
0xDB: { code: 'BracketLeft', keyCap: '[' }, // [USB: 0x2f] [{ (US Standard 101) | ||
0xDC: { code: 'Backslash', keyCap: '\\' }, // [USB: 0x31] \| (US Standard 101) | ||
0xDD: { code: 'BracketRight', keyCap: ']' }, // [USB: 0x30] ]} (US Standard 101) | ||
0xDE: { code: 'Quote', keyCap: '\'' }, // [USB: 0x34] '" (US Standard 101) | ||
// 0xDF - miscellaneous/varies | ||
// 0xE0 - reserved | ||
// 0xE1 - OEM specific | ||
0xE2: { code: 'IntlBackslash', keyCap: '\\' }, // [USB: 0x64] \| (UK Standard 102) | ||
// 0xE3-0xE4 - OEM specific | ||
0xE5: { code: 'Process' }, // (Not in D3E) | ||
// 0xE6 - OEM specific | ||
// 0xE7 - VK_PACKET | ||
// 0xE8 - unassigned | ||
// 0xE9-0xEF - OEM specific | ||
// 0xF0-0xF5 - OEM specific | ||
0xF6: { code: 'Attn' }, // [USB: 0x9a] (Not in D3E) | ||
0xF7: { code: 'CrSel' }, // [USB: 0xa3] (Not in D3E) | ||
0xF8: { code: 'ExSel' }, // [USB: 0xa4] (Not in D3E) | ||
0xF9: { code: 'EraseEof' }, // (Not in D3E) | ||
0xFA: { code: 'Play' }, // (Not in D3E) | ||
0xFB: { code: 'ZoomToggle' }, // (Not in D3E) | ||
// 0xFC - VK_NONAME - reserved | ||
// 0xFD - VK_PA1 | ||
0xFE: { code: 'Clear' } // [USB: 0x9c] (Not in D3E) | ||
}; | ||
// No legacy keyCode, but listed in D3E: | ||
// code: usb | ||
// 'IntlHash': 0x070032, | ||
// 'IntlRo': 0x070087, | ||
// 'IntlYen': 0x070089, | ||
// 'NumpadBackspace': 0x0700bb, | ||
// 'NumpadClear': 0x0700d8, | ||
// 'NumpadClearEntry': 0x0700d9, | ||
// 'NumpadMemoryAdd': 0x0700d3, | ||
// 'NumpadMemoryClear': 0x0700d2, | ||
// 'NumpadMemoryRecall': 0x0700d1, | ||
// 'NumpadMemoryStore': 0x0700d0, | ||
// 'NumpadMemorySubtract': 0x0700d4, | ||
// 'NumpadParenLeft': 0x0700b6, | ||
// 'NumpadParenRight': 0x0700b7, | ||
//-------------------------------------------------------------------- | ||
// | ||
// Browser/OS Specific Mappings | ||
// | ||
//-------------------------------------------------------------------- | ||
mergeIf(keyCodeToInfoTable, | ||
'moz', { | ||
0x3B: { code: 'Semicolon', keyCap: ';' }, // [USB: 0x33] ;: (US Standard 101) | ||
0x3D: { code: 'Equal', keyCap: '=' }, // [USB: 0x2e] =+ | ||
0x6B: { code: 'Equal', keyCap: '=' }, // [USB: 0x2e] =+ | ||
0x6D: { code: 'Minus', keyCap: '-' }, // [USB: 0x2d] -_ | ||
0xBB: { code: 'NumpadAdd', keyCap: '+', location: NUMPAD }, // [USB: 0x57] | ||
0xBD: { code: 'NumpadSubtract', keyCap: '-', location: NUMPAD } // [USB: 0x56] | ||
}); | ||
mergeIf(keyCodeToInfoTable, | ||
'moz-mac', { | ||
0x0C: { code: 'NumLock', location: NUMPAD }, // [USB: 0x53] | ||
0xAD: { code: 'Minus', keyCap: '-' } // [USB: 0x2d] -_ | ||
}); | ||
mergeIf(keyCodeToInfoTable, | ||
'moz-win', { | ||
0xAD: { code: 'Minus', keyCap: '-' } // [USB: 0x2d] -_ | ||
}); | ||
mergeIf(keyCodeToInfoTable, | ||
'chrome-mac', { | ||
0x5D: { code: 'OSRight', location: RIGHT } // [USB: 0xe7] | ||
}); | ||
// Windows via Bootcamp (!) | ||
if (0) { | ||
mergeIf(keyCodeToInfoTable, | ||
'chrome-win', { | ||
0xC0: { code: 'Quote', keyCap: '\'' }, // [USB: 0x34] '" (US Standard 101) | ||
0xDE: { code: 'Backslash', keyCap: '\\' }, // [USB: 0x31] \| (US Standard 101) | ||
0xDF: { code: 'Backquote', keyCap: '`' } // [USB: 0x35] `~ (US Standard 101) | ||
}); | ||
mergeIf(keyCodeToInfoTable, | ||
'ie', { | ||
0xC0: { code: 'Quote', keyCap: '\'' }, // [USB: 0x34] '" (US Standard 101) | ||
0xDE: { code: 'Backslash', keyCap: '\\' }, // [USB: 0x31] \| (US Standard 101) | ||
0xDF: { code: 'Backquote', keyCap: '`' } // [USB: 0x35] `~ (US Standard 101) | ||
}); | ||
} | ||
mergeIf(keyCodeToInfoTable, | ||
'safari', { | ||
0x03: { code: 'Enter' }, // [USB: 0x28] old Safari | ||
0x19: { code: 'Tab' } // [USB: 0x2b] old Safari for Shift+Tab | ||
}); | ||
mergeIf(keyCodeToInfoTable, | ||
'ios', { | ||
0x0A: { code: 'Enter', location: STANDARD } // [USB: 0x28] | ||
}); | ||
mergeIf(keyCodeToInfoTable, | ||
'safari-mac', { | ||
0x5B: { code: 'OSLeft', location: LEFT }, // [USB: 0xe3] | ||
0x5D: { code: 'OSRight', location: RIGHT }, // [USB: 0xe7] | ||
0xE5: { code: 'KeyQ', keyCap: 'Q' } // [USB: 0x14] On alternate presses, Ctrl+Q sends this | ||
}); | ||
//-------------------------------------------------------------------- | ||
// | ||
// Identifier Mappings | ||
// | ||
//-------------------------------------------------------------------- | ||
// Cases where newer-ish browsers send keyIdentifier which can be | ||
// used to disambiguate keys. | ||
// keyIdentifierTable[keyIdentifier] -> keyInfo | ||
var keyIdentifierTable = {}; | ||
if ('cros' === os) { | ||
keyIdentifierTable['U+00A0'] = { code: 'ShiftLeft', location: LEFT }; | ||
keyIdentifierTable['U+00A1'] = { code: 'ShiftRight', location: RIGHT }; | ||
keyIdentifierTable['U+00A2'] = { code: 'ControlLeft', location: LEFT }; | ||
keyIdentifierTable['U+00A3'] = { code: 'ControlRight', location: RIGHT }; | ||
keyIdentifierTable['U+00A4'] = { code: 'AltLeft', location: LEFT }; | ||
keyIdentifierTable['U+00A5'] = { code: 'AltRight', location: RIGHT }; | ||
} | ||
if ('chrome-mac' === browser_os) { | ||
keyIdentifierTable['U+0010'] = { code: 'ContextMenu' }; | ||
} | ||
if ('safari-mac' === browser_os) { | ||
keyIdentifierTable['U+0010'] = { code: 'ContextMenu' }; | ||
} | ||
if ('ios' === os) { | ||
// These only generate keyup events | ||
keyIdentifierTable['U+0010'] = { code: 'Function' }; | ||
keyIdentifierTable['U+001C'] = { code: 'ArrowLeft' }; | ||
keyIdentifierTable['U+001D'] = { code: 'ArrowRight' }; | ||
keyIdentifierTable['U+001E'] = { code: 'ArrowUp' }; | ||
keyIdentifierTable['U+001F'] = { code: 'ArrowDown' }; | ||
keyIdentifierTable['U+0001'] = { code: 'Home' }; // [USB: 0x4a] Fn + ArrowLeft | ||
keyIdentifierTable['U+0004'] = { code: 'End' }; // [USB: 0x4d] Fn + ArrowRight | ||
keyIdentifierTable['U+000B'] = { code: 'PageUp' }; // [USB: 0x4b] Fn + ArrowUp | ||
keyIdentifierTable['U+000C'] = { code: 'PageDown' }; // [USB: 0x4e] Fn + ArrowDown | ||
} | ||
//-------------------------------------------------------------------- | ||
// | ||
// Location Mappings | ||
// | ||
//-------------------------------------------------------------------- | ||
// Cases where newer-ish browsers send location/keyLocation which | ||
// can be used to disambiguate keys. | ||
// locationTable[location][keyCode] -> keyInfo | ||
var locationTable = []; | ||
locationTable[LEFT] = { | ||
0x10: { code: 'ShiftLeft', location: LEFT }, // [USB: 0xe1] | ||
0x11: { code: 'ControlLeft', location: LEFT }, // [USB: 0xe0] | ||
0x12: { code: 'AltLeft', location: LEFT } // [USB: 0xe2] | ||
}; | ||
locationTable[RIGHT] = { | ||
0x10: { code: 'ShiftRight', location: RIGHT }, // [USB: 0xe5] | ||
0x11: { code: 'ControlRight', location: RIGHT }, // [USB: 0xe4] | ||
0x12: { code: 'AltRight', location: RIGHT } // [USB: 0xe6] | ||
}; | ||
locationTable[NUMPAD] = { | ||
0x0D: { code: 'NumpadEnter', location: NUMPAD } // [USB: 0x58] | ||
}; | ||
mergeIf(locationTable[NUMPAD], 'moz', { | ||
0x6D: { code: 'NumpadSubtract', location: NUMPAD }, // [USB: 0x56] | ||
0x6B: { code: 'NumpadAdd', location: NUMPAD } // [USB: 0x57] | ||
}); | ||
mergeIf(locationTable[LEFT], 'moz-mac', { | ||
0xE0: { code: 'OSLeft', location: LEFT } // [USB: 0xe3] | ||
}); | ||
mergeIf(locationTable[RIGHT], 'moz-mac', { | ||
0xE0: { code: 'OSRight', location: RIGHT } // [USB: 0xe7] | ||
}); | ||
mergeIf(locationTable[RIGHT], 'moz-win', { | ||
0x5B: { code: 'OSRight', location: RIGHT } // [USB: 0xe7] | ||
}); | ||
mergeIf(locationTable[RIGHT], 'mac', { | ||
0x5D: { code: 'OSRight', location: RIGHT } // [USB: 0xe7] | ||
}); | ||
mergeIf(locationTable[NUMPAD], 'chrome-mac', { | ||
0x0C: { code: 'NumLock', location: NUMPAD } // [USB: 0x53] | ||
}); | ||
mergeIf(locationTable[NUMPAD], 'safari-mac', { | ||
0x0C: { code: 'NumLock', location: NUMPAD }, // [USB: 0x53] | ||
0xBB: { code: 'NumpadAdd', location: NUMPAD }, // [USB: 0x57] | ||
0xBD: { code: 'NumpadSubtract', location: NUMPAD }, // [USB: 0x56] | ||
0xBE: { code: 'NumpadDecimal', location: NUMPAD }, // [USB: 0x63] | ||
0xBF: { code: 'NumpadDivide', location: NUMPAD } // [USB: 0x54] | ||
}); | ||
//-------------------------------------------------------------------- | ||
// | ||
// Key Values | ||
// | ||
//-------------------------------------------------------------------- | ||
// Mapping from `code` values to `key` values. Values defined at: | ||
// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3Events-key.html | ||
// Entries are only provided when `key` differs from `code`. If | ||
// printable, `shiftKey` has the shifted printable character. This | ||
// assumes US Standard 101 layout | ||
var codeToKeyTable = { | ||
// Modifier Keys | ||
ShiftLeft: { key: 'Shift' }, | ||
ShiftRight: { key: 'Shift' }, | ||
ControlLeft: { key: 'Control' }, | ||
ControlRight: { key: 'Control' }, | ||
AltLeft: { key: 'Alt' }, | ||
AltRight: { key: 'Alt' }, | ||
OSLeft: { key: 'OS' }, | ||
OSRight: { key: 'OS' }, | ||
// Whitespace Keys | ||
NumpadEnter: { key: 'Enter' }, | ||
Space: { key: ' ' }, | ||
// Printable Keys | ||
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: '|' } | ||
}; | ||
mergeIf(codeToKeyTable, 'mac', { | ||
OSLeft: { key: 'Meta' }, | ||
OSRight: { key: 'Meta' } | ||
}); | ||
// Corrections for 'key' names in older browsers (e.g. FF36-) | ||
// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key#Key_values | ||
var keyFixTable = { | ||
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' | ||
}; | ||
//-------------------------------------------------------------------- | ||
// | ||
// Exported Functions | ||
// | ||
//-------------------------------------------------------------------- | ||
var codeTable = remap(keyCodeToInfoTable, 'code'); | ||
try { | ||
var nativeLocation = nativeKeyboardEvent && ('location' in new KeyboardEvent('')); | ||
} catch (_) {} | ||
function keyInfoForEvent(event) { | ||
var keyCode = 'keyCode' in event ? event.keyCode : 'which' in event ? event.which : 0; | ||
var keyInfo = (function(){ | ||
if (nativeLocation || 'keyLocation' in event) { | ||
var location = nativeLocation ? event.location : event.keyLocation; | ||
if (location && keyCode in locationTable[location]) { | ||
return locationTable[location][keyCode]; | ||
} | ||
} | ||
if ('keyIdentifier' in event && event.keyIdentifier in keyIdentifierTable) { | ||
return keyIdentifierTable[event.keyIdentifier]; | ||
} | ||
if (keyCode in keyCodeToInfoTable) { | ||
return keyCodeToInfoTable[keyCode]; | ||
} | ||
return null; | ||
}()); | ||
// TODO: Track these down and move to general tables | ||
if (0) { | ||
// TODO: Map these for newerish browsers? | ||
// TODO: iOS only? | ||
// TODO: Override with more common keyIdentifier name? | ||
switch (event.keyIdentifier) { | ||
case 'U+0010': keyInfo = { code: 'Function' }; break; | ||
case 'U+001C': keyInfo = { code: 'ArrowLeft' }; break; | ||
case 'U+001D': keyInfo = { code: 'ArrowRight' }; break; | ||
case 'U+001E': keyInfo = { code: 'ArrowUp' }; break; | ||
case 'U+001F': keyInfo = { code: 'ArrowDown' }; break; | ||
} | ||
} | ||
if (!keyInfo) | ||
return null; | ||
var key = (function() { | ||
var entry = codeToKeyTable[keyInfo.code]; | ||
if (!entry) return keyInfo.code; | ||
return (event.shiftKey && 'shiftKey' in entry) ? entry.shiftKey : entry.key; | ||
}()); | ||
return { | ||
code: keyInfo.code, | ||
key: key, | ||
location: keyInfo.location, | ||
keyCap: keyInfo.keyCap | ||
}; | ||
} | ||
function queryKeyCap(code, locale) { | ||
code = String(code); | ||
if (!codeTable.hasOwnProperty(code)) return 'Undefined'; | ||
if (locale && String(locale).toLowerCase() !== 'en-us') throw Error('Unsupported locale'); | ||
var keyInfo = codeTable[code]; | ||
return keyInfo.keyCap || keyInfo.code || 'Undefined'; | ||
} | ||
if ('KeyboardEvent' in global && 'defineProperty' in Object) { | ||
(function() { | ||
function define(o, p, v) { | ||
if (p in o) return; | ||
Object.defineProperty(o, p, v); | ||
} | ||
define(KeyboardEvent.prototype, 'code', { get: function() { | ||
var keyInfo = keyInfoForEvent(this); | ||
return keyInfo ? keyInfo.code : ''; | ||
}}); | ||
// Fix for nonstandard `key` values (FF36-) | ||
if ('key' in KeyboardEvent.prototype) { | ||
var desc = Object.getOwnPropertyDescriptor(KeyboardEvent.prototype, 'key'); | ||
Object.defineProperty(KeyboardEvent.prototype, 'key', { get: function() { | ||
var key = desc.get.call(this); | ||
return keyFixTable.hasOwnProperty(key) ? keyFixTable[key] : key; | ||
}}); | ||
} | ||
define(KeyboardEvent.prototype, 'key', { get: function() { | ||
var keyInfo = keyInfoForEvent(this); | ||
return (keyInfo && 'key' in keyInfo) ? keyInfo.key : 'Unidentified'; | ||
}}); | ||
define(KeyboardEvent.prototype, 'location', { get: function() { | ||
var keyInfo = keyInfoForEvent(this); | ||
return (keyInfo && 'location' in keyInfo) ? keyInfo.location : STANDARD; | ||
}}); | ||
define(KeyboardEvent.prototype, 'locale', { get: function() { | ||
return ''; | ||
}}); | ||
}()); | ||
} | ||
if (!('queryKeyCap' in global.KeyboardEvent)) | ||
global.KeyboardEvent.queryKeyCap = queryKeyCap; | ||
// Helper for IE8- | ||
global.identifyKey = function(event) { | ||
if ('code' in event) | ||
return; | ||
var keyInfo = keyInfoForEvent(event); | ||
event.code = keyInfo ? keyInfo.code : ''; | ||
event.key = (keyInfo && 'key' in keyInfo) ? keyInfo.key : 'Unidentified'; | ||
event.location = ('location' in event) ? event.location : | ||
('keyLocation' in event) ? event.keyLocation : | ||
(keyInfo && 'location' in keyInfo) ? keyInfo.location : STANDARD; | ||
event.locale = ''; | ||
}; | ||
} (window)); | ||
/***/ } | ||
/******/ ]); |
@@ -18,6 +18,12 @@ require('./lib/keyboard.polyfill'); | ||
* to the entity when pressing the keys. | ||
* @param {number} [angularAcceleration=Math.PI*0.25] - Determines the angular | ||
* acceleration given to the entity when pressing the keys. Only applied when | ||
* mode == 'fps'. Measured in Radians. | ||
* @param {bool} [enabled=true] - To completely enable or disable the controls | ||
* @param {bool} [fly=false] - Determines if the direction of the movement sticks | ||
* to the plane where the entity started off or if there are 6 degrees of | ||
* freedom as a diver underwater or a plane flying. | ||
* @param {string} [mode='default'] - | ||
* 'default' enforces the direction of the movement to stick to the plane | ||
* where the entity started off. | ||
* 'fps' extends 'default' by enabling "First Person Shooter" controls: W/S = | ||
* Forward/Back, Q/E = Strafe left/right, A/D = Rotate left/right | ||
* 'fly' enables 6 degrees of freedom as a diver underwater or a plane flying. | ||
* @param {string} [rollAxis='z'] - The front-to-back axis. | ||
@@ -27,16 +33,20 @@ * @param {string} [pitchAxis='x'] - The left-to-right axis. | ||
* @param {bool} [pitchAxisInverted=false] - Pitch axis is inverted | ||
* @param {bool} [yawAxisInverted=false] - Yaw axis is inverted. Used when | ||
* mode == 'fps' | ||
*/ | ||
module.exports = { | ||
schema: { | ||
easing: { default: 20 }, | ||
acceleration: { default: 65 }, | ||
enabled: { default: true }, | ||
fly: { default: false }, | ||
rollAxis: { default: 'z', oneOf: [ 'x', 'y', 'z' ] }, | ||
pitchAxis: { default: 'x', oneOf: [ 'x', 'y', 'z' ] }, | ||
rollAxisInverted: { default: false }, | ||
rollAxisEnabled: { default: true }, | ||
pitchAxisInverted: { default: false }, | ||
pitchAxisEnabled: { default: true }, | ||
debug: { default: false } | ||
easing: { default: 20 }, | ||
acceleration: { default: 65 }, | ||
angularAcceleration: { default: Math.PI / 4 }, | ||
enabled: { default: true }, | ||
mode: { default: 'default', oneOf: ['default', 'fly', 'fps']}, | ||
rollAxis: { default: 'z', oneOf: [ 'x', 'y', 'z' ] }, | ||
pitchAxis: { default: 'x', oneOf: [ 'x', 'y', 'z' ] }, | ||
rollAxisInverted: { default: false }, | ||
rollAxisEnabled: { default: true }, | ||
pitchAxisInverted: { default: false }, | ||
pitchAxisEnabled: { default: true }, | ||
yawAxisInverted: { default: false }, | ||
debug: { default: false } | ||
}, | ||
@@ -46,2 +56,3 @@ | ||
this.velocity = new THREE.Vector3(); | ||
this.angularVelocity = 0; | ||
this.localKeys = {}; | ||
@@ -60,58 +71,102 @@ this.listeners = { | ||
tick: function (t, dt) { | ||
var data = this.data; | ||
var acceleration = data.acceleration; | ||
var easing = data.easing; | ||
var velocity = this.velocity; | ||
var keys = this.getKeys(); | ||
var movementVector; | ||
var pitchAxis = data.pitchAxis; | ||
var rollAxis = data.rollAxis; | ||
var pitchSign = data.pitchAxisInverted ? -1 : 1; | ||
var rollSign = data.rollAxisInverted ? -1 : 1; | ||
var el = this.el; | ||
tick: (function () { | ||
var upVector = new THREE.Vector3(0, 1, 0); | ||
var rotation = new THREE.Euler(0, 0, 0, 'YXZ'); | ||
return function (t, dt) { | ||
var data = this.data; | ||
var acceleration = data.acceleration; | ||
var angularAcceleration = data.angularAcceleration; | ||
var easing = data.easing; | ||
var velocity = this.velocity; | ||
var keys = this.getKeys(); | ||
var movementVector; | ||
var pitchAxis = data.pitchAxis; | ||
var rollAxis = data.rollAxis; | ||
var pitchSign = data.pitchAxisInverted ? -1 : 1; | ||
var rollSign = data.rollAxisInverted ? -1 : 1; | ||
var yawSign = data.yawAxisInverted ? 1 : -1; | ||
var el = this.el; | ||
var strafeLeft = data.mode === 'fps' ? ['KeyQ', 'ArrowLeft'] : ['KeyA', 'ArrowLeft']; | ||
var strafeRight = data.mode === 'fps' ? ['KeyE', 'ArrowRight'] : ['KeyD', 'ArrowRight']; | ||
// If data changed or FPS too low, reset velocity. | ||
if (isNaN(dt) || dt > MAX_DELTA) { | ||
velocity[pitchAxis] = 0; | ||
velocity[rollAxis] = 0; | ||
return; | ||
} | ||
// If data changed or FPS too low, reset velocity. | ||
if (isNaN(dt) || dt > MAX_DELTA) { | ||
velocity[pitchAxis] = 0; | ||
velocity[rollAxis] = 0; | ||
this.angularVelocity = 0; | ||
return; | ||
} | ||
velocity[pitchAxis] -= velocity[pitchAxis] * easing * dt / 1000; | ||
velocity[rollAxis] -= velocity[rollAxis] * easing * dt / 1000; | ||
velocity[pitchAxis] -= velocity[pitchAxis] * easing * dt / 1000; | ||
velocity[rollAxis] -= velocity[rollAxis] * easing * dt / 1000; | ||
this.angularVelocity -= this.angularVelocity * easing * dt / 1000; | ||
var position = el.getComputedAttribute('position'); | ||
var position = el.getComputedAttribute('position'); | ||
if (data.enabled) { | ||
if (data.pitchAxisEnabled) { | ||
if (keys.KeyA || keys.ArrowLeft) { | ||
velocity[pitchAxis] -= pitchSign * acceleration * dt / 1000; | ||
if (data.enabled) { | ||
if (data.pitchAxisEnabled) { | ||
if (strafeLeft.some(key => keys[key])) { | ||
velocity[pitchAxis] -= pitchSign * acceleration * dt / 1000; | ||
} | ||
if (strafeRight.some(key => keys[key])) { | ||
velocity[pitchAxis] += pitchSign * acceleration * dt / 1000; | ||
} | ||
} | ||
if (keys.KeyD || keys.ArrowRight) { | ||
velocity[pitchAxis] += pitchSign * acceleration * dt / 1000; | ||
if (data.rollAxisEnabled) { | ||
if (keys.KeyW || keys.ArrowUp) { | ||
velocity[rollAxis] -= rollSign * acceleration * dt / 1000; | ||
} | ||
if (keys.KeyS || keys.ArrowDown) { | ||
velocity[rollAxis] += rollSign * acceleration * dt / 1000; | ||
} | ||
} | ||
} | ||
if (data.rollAxisEnabled) { | ||
if (keys.KeyW || keys.ArrowUp) { | ||
velocity[rollAxis] -= rollSign * acceleration * dt / 1000; | ||
if (data.mode === 'fps') { | ||
if (keys.KeyA) { | ||
this.angularVelocity -= yawSign * angularAcceleration * dt / 1000; | ||
} | ||
if (keys.KeyD) { | ||
this.angularVelocity += yawSign * angularAcceleration * dt / 1000; | ||
} | ||
} | ||
if (keys.KeyS || keys.ArrowDown) { | ||
velocity[rollAxis] += rollSign * acceleration * dt / 1000; | ||
} | ||
} | ||
} | ||
movementVector = this.getMovementVector(dt); | ||
el.object3D.translateX(movementVector.x); | ||
el.object3D.translateY(movementVector.y); | ||
el.object3D.translateZ(movementVector.z); | ||
if (data.mode === 'fps') { | ||
var elRotation = this.el.getAttribute('rotation'); | ||
this.rotateOnAxis(rotation, upVector, this.angularVelocity); | ||
el.setAttribute('position', { | ||
x: position.x + movementVector.x, | ||
y: position.y + movementVector.y, | ||
z: position.z + movementVector.z | ||
}); | ||
}, | ||
el.setAttribute('rotation', { | ||
x: THREE.Math.radToDeg(rotation.x), | ||
y: THREE.Math.radToDeg(rotation.y), | ||
z: THREE.Math.radToDeg(rotation.z) | ||
}); | ||
} | ||
movementVector = this.getMovementVector(dt); | ||
el.object3D.translateX(movementVector.x); | ||
el.object3D.translateY(movementVector.y); | ||
el.object3D.translateZ(movementVector.z); | ||
el.setAttribute('position', { | ||
x: position.x + movementVector.x, | ||
y: position.y + movementVector.y, | ||
z: position.z + movementVector.z | ||
}); | ||
}; | ||
})(), | ||
rotateOnAxis: (function () { | ||
var quaternion = new THREE.Quaternion(); | ||
var eulerAsQuaternion = new THREE.Quaternion(); | ||
return function (euler, axis, angle) { | ||
quaternion.setFromAxisAngle(axis, angle); | ||
eulerAsQuaternion.setFromEuler(euler); | ||
eulerAsQuaternion.multiply(quaternion); | ||
euler.setFromQuaternion(eulerAsQuaternion, euler.order); | ||
}; | ||
})(), | ||
getMovementVector: (function () { | ||
@@ -126,3 +181,3 @@ var direction = new THREE.Vector3(0, 0, 0); | ||
if (!elRotation) { return direction; } | ||
if (!this.data.fly) { elRotation.x = 0; } | ||
if (this.data.mode !== 'fly') { elRotation.x = 0; } | ||
rotation.set(THREE.Math.degToRad(elRotation.x), | ||
@@ -129,0 +184,0 @@ THREE.Math.degToRad(elRotation.y), 0); |
{ | ||
"name": "aframe-keyboard-controls", | ||
"version": "1.0.0", | ||
"version": "1.1.0", | ||
"description": "Keyboard controls for A-Frame.", | ||
@@ -5,0 +5,0 @@ "main": "keyboard-controls.js", |
@@ -31,2 +31,5 @@ # A-Frame `keyboard-controls` Component | ||
The full list of options can be seen in | ||
[`keyboard-controls.js`](keyboard-controls.js). | ||
## Usage + Remote Device | ||
@@ -33,0 +36,0 @@ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
241733
2876
73