From 1d855171a8210fd73ddcf60ad9797ddb38461028 Mon Sep 17 00:00:00 2001 From: tom32i Date: Fri, 20 Jun 2014 22:11:38 +0200 Subject: [PATCH] Better events --- .jshintrc | 5 +- README.md | 28 +++++--- dist/gamepad.js | 149 ++++++++++++++++++++++------------------- dist/gamepad.min.js | 2 +- src/GamepadHandler.js | 45 ++++++++++++- src/GamepadListener.js | 104 ++++++++++------------------ 6 files changed, 187 insertions(+), 146 deletions(-) diff --git a/.jshintrc b/.jshintrc index ebf66b9..2aca120 100644 --- a/.jshintrc +++ b/.jshintrc @@ -25,6 +25,9 @@ "CustomEvent", "Event", "console", - "navigator" + "navigator", + "EventEmitter", + "OptionResolver", + "GamepadHandler" ] } \ No newline at end of file diff --git a/README.md b/README.md index 098e7e3..6156000 100644 --- a/README.md +++ b/README.md @@ -28,18 +28,12 @@ Percent of noise to ignore around 0. Ex: deadZone set to 0.3 will cause stick position of from -0.3 to 0.3 to be considered 0. Stick moves below 30% from default positon won't trigger a change. -__eventType:__ (String: default 'gamepad', available 'gamepad', 'key', 'global') -* global: Send 'gamepad:axis' and 'gamepad:button' each time an axis or a button state change. -* gamepad: Send 'gamepad:(index):axis' and 'gamepad:(index):button' where '(index)' is the index of the gamepad, allowing you to listen to a specific gamepad. -* key: Send 'gamepad:(gamepadIndex):axis:(axisIndex)' and 'gamepad:(gamepadIndex):button:(buttonIndex)' where '(gamepadIndex)' is the index of the gamepad and '(axisIndex)' / 'buttonIndex' is the index of the axis/button, allowing you to listen to a specific button or axis. - Theses options can be set for the whole gamepad: ```javascript var listener = new GamepadListener({ analog: false, - deadZone: 0.3, - eventType: 'key' + deadZone: 0.3 }); ``` @@ -59,7 +53,16 @@ var listener = new GamepadListener({ ## Events: -Listen for value change on gampads: +* 'gamepad:connected': When a new gamepad is connected. +* 'gamepad:disconnected': When a gamepad is disconnected. +* 'gamepad:axis': When a gamepad axis changes. +* 'gamepad:{gamepad}:axis': When a specific gamepad axis changes, '{gamepad}' being the numeric index. +* 'gamepad:{gamepad}:axis:{axis}': When a specific axis on a specific gamepad changes, '{axis}' being the numeric index of the axis. +* 'gamepad:button': When a gamepad button changes. +* 'gamepad:{gamepad}:button': When a specific gamepad button changes, '{gamepad}' being the numeric index. +* 'gamepad:{gamepad}:button:{button}': When a specific button on a specific gamepad changes, '{button}' being the numeric index of the button. + +__Listen for value change on gampads:__ ```javascript @@ -73,6 +76,15 @@ listener.on('gamepad:connected', function (event) { */ }); +listener.on('gamepad:disconnected', function (event) { + /** + * event: CustomEvent + * detail: { + * index: 0 + * } + */ +}); + listener.on('gamepad:axis', function (event) { /** * event: CustomEvent diff --git a/dist/gamepad.js b/dist/gamepad.js index 98f474a..044a8fc 100644 --- a/dist/gamepad.js +++ b/dist/gamepad.js @@ -23,7 +23,7 @@ function GamepadHandler(gamepad, options) EventEmitter.call(this); this.gamepad = gamepad; - this.options = options; + this.options = this.resolveOptions(typeof(options) === 'object' ? options : {}); this.sticks = new Array(this.gamepad.axes.length); this.buttons = new Array(this.gamepad.buttons.length); @@ -40,6 +40,49 @@ function GamepadHandler(gamepad, options) GamepadHandler.prototype = Object.create(EventEmitter.prototype); +/** + * Option resolver + * + * @type {OptionResolver} + */ +GamepadHandler.prototype.optionResolver = new OptionResolver(false); + +GamepadHandler.prototype.optionResolver.setDefaults({ + analog: true, + deadZone: 0, + precision: 0 +}); + +GamepadHandler.prototype.optionResolver.setTypes({ + analog: 'boolean', + deadZone: 'number', + precision: 'number' +}); + +/** + * Resolve options + * + * @param {Object} options + * + * @return {Object} + */ +GamepadHandler.prototype.resolveOptions = function(source) +{ + var customStick = typeof source.stick !== 'undefined', + customButton = typeof source.button !== 'undefined', + options = { + stick: this.optionResolver.resolve(customStick ? source.stick : (customButton ? {} : source)), + button: this.optionResolver.resolve(customButton ? source.button : (customStick ? {} : source)) + }; + + options.stick.deadZone = Math.max(Math.min(options.stick.deadZone, 1), 0); + options.button.deadZone = Math.max(Math.min(options.button.deadZone, 1), 0); + options.stick.precision = options.stick.precision ? Math.pow(10, options.stick.precision) : 0; + options.button.precision = options.button.precision ? Math.pow(10, options.button.precision) : 0; + + return options; +}; + /** * Update */ @@ -128,65 +171,20 @@ function GamepadListener(options) { EventEmitter.call(this); - this.options = this.resolveOptions(typeof(options) === 'object' ? options : {}); + this.options = typeof(options) === 'object' ? options : {}; this.frame = null; this.update = this.update.bind(this); this.onAxis = this.onAxis.bind(this); this.onButton = this.onButton.bind(this); this.stop = this.stop.bind(this); + this.handlers = new Array(4); window.addEventListener('error', this.stop); - this.start(); } GamepadListener.prototype = Object.create(EventEmitter.prototype); -/** - * Option resolver - * - * @type {OptionResolver} - */ -GamepadListener.prototype.optionResolver = new OptionResolver(false); - -GamepadListener.prototype.optionResolver.setDefaults({ - analog: true, - deadZone: 0, - precision: 0, - eventType: 'gamepad' -}); - -GamepadListener.prototype.optionResolver.setTypes({ - analog: 'boolean', - deadZone: 'number', - precision: 'number', - eventType: 'string' -}); - -/** - * Resolve options - * - * @param {Object} options - * - * @return {Object} - */ -GamepadListener.prototype.resolveOptions = function(source) -{ - var customStick = typeof source.stick !== 'undefined', - customButton = typeof source.button !== 'undefined', - options = { - stick: this.optionResolver.resolve(customStick ? source.stick : (customButton ? {} : source)), - button: this.optionResolver.resolve(customButton ? source.button : (customStick ? {} : source)) - }; - - options.stick.deadZone = Math.max(Math.min(options.stick.deadZone, 1), 0); - options.button.deadZone = Math.max(Math.min(options.button.deadZone, 1), 0); - options.stick.precision = options.stick.precision ? Math.pow(10, options.stick.precision) : 0; - options.button.precision = options.button.precision ? Math.pow(10, options.button.precision) : 0; - - return options; -}; - /** * Start */ @@ -224,6 +222,8 @@ GamepadListener.prototype.update = function() } gamepads[i].handler.update(); + } else if (this.handlers[i]) { + this.removeGamepad(i); } } }; @@ -235,12 +235,37 @@ GamepadListener.prototype.update = function() */ GamepadListener.prototype.addGamepad = function(gamepad) { + console.log('addGamepad', gamepad); + var handler = new GamepadHandler(gamepad, this.options); - handler.on('gamepad:axis', this.onAxis); - handler.on('gamepad:button', this.onButton); + handler.on('axis', this.onAxis); + handler.on('button', this.onButton); this.emit('gamepad:connected', {gamepad: gamepad, index: gamepad.index}); + this.emit('gamepad:' + gamepad.index + ':connected', {gamepad: gamepad, index: gamepad.index}); + + this.handlers[gamepad.index] = handler; +}; + +/** + * Add gamepad + * + * @param {Gamepad} gamepad + */ +GamepadListener.prototype.removeGamepad = function(index) +{ + var handler = this.handlers[index]; + + console.log('removeGamepad', index, handler, handler.gamepad); + + handler.off('axis', this.onAxis); + handler.off('button', this.onButton); + + this.emit('gamepad:disconnected', {index: index}); + this.emit('gamepad:' + index + ':disconnected', {index: index}); + + this.handlers[index] = null; }; /** @@ -250,15 +275,9 @@ GamepadListener.prototype.addGamepad = function(gamepad) */ GamepadListener.prototype.onAxis = function(event) { - var eventName = 'gamepad:axis'; - - if (this.options.eventType == 'gamepad') { - eventName = 'gamepad:' + event.detail.gamepad.index + ':axis'; - } else if (this.options.eventType == 'key') { - eventName = 'gamepad:' + event.detail.gamepad.index + ':axis:' + event.detail.axis; - } - - this.emit(eventName, event.detail); + this.emit('gamepad:axis', event.detail); + this.emit('gamepad:' + event.detail.gamepad.index + ':axis', event.detail); + this.emit('gamepad:' + event.detail.gamepad.index + ':axis:' + event.detail.axis, event.detail); }; /** @@ -268,15 +287,9 @@ GamepadListener.prototype.onAxis = function(event) */ GamepadListener.prototype.onButton = function(event) { - var eventName = 'gamepad:button'; - - if (this.options.eventType == 'gamepad') { - eventName = 'gamepad:' + event.detail.gamepad.index + ':button'; - } else if (this.options.eventType == 'key') { - eventName = 'gamepad:' + event.detail.gamepad.index + ':button:' + event.detail.index; - } - - this.emit(eventName, event.detail); + this.emit('gamepad:button', event.detail); + this.emit('gamepad:' + event.detail.gamepad.index + ':button', event.detail); + this.emit('gamepad:' + event.detail.gamepad.index + ':button:' + event.detail.index, event.detail); }; /** diff --git a/dist/gamepad.min.js b/dist/gamepad.min.js index e72858b..a6d2857 100644 --- a/dist/gamepad.min.js +++ b/dist/gamepad.min.js @@ -4,4 +4,4 @@ * Copyright 2014 Thomas JARRAND */ -function EventEmitter(){this._eventElement=document.createElement("div")}function OptionResolver(t){this.allowExtra="undefined"!=typeof t&&t,this.defaults={},this.types={},this.optional=[],this.required=[]}function GamepadHandler(t,e){EventEmitter.call(this),this.gamepad=t,this.options=e,this.sticks=new Array(this.gamepad.axes.length),this.buttons=new Array(this.gamepad.buttons.length);for(var i=this.sticks.length-1;i>=0;i--)this.sticks[i]=[0,0];for(var n=this.buttons.length-1;n>=0;n--)this.buttons[n]=0;this.gamepad.handler=this}function GamepadListener(t){EventEmitter.call(this),this.options=this.resolveOptions("object"==typeof t?t:{}),this.frame=null,this.update=this.update.bind(this),this.onAxis=this.onAxis.bind(this),this.onButton=this.onButton.bind(this),this.stop=this.stop.bind(this),window.addEventListener("error",this.stop),this.start()}EventEmitter.prototype.emit=function(t,e){this._eventElement.dispatchEvent(new CustomEvent(t,{detail:e}))},EventEmitter.prototype.addEventListener=function(t,e){this._eventElement.addEventListener(t,e,!1)},EventEmitter.prototype.removeEventListener=function(t,e){this._eventElement.removeEventListener(t,e,!1)},EventEmitter.prototype.on=EventEmitter.prototype.addEventListener,EventEmitter.prototype.off=EventEmitter.prototype.removeEventListener,OptionResolver.prototype.setDefaults=function(t){for(var e in t)t.hasOwnProperty(e)&&(this.defaults[e]=t[e]);return this},OptionResolver.prototype.setTypes=function(t){for(var e in t)t.hasOwnProperty(e)&&(this.types[e]=t[e]);return this},OptionResolver.prototype.setOptional=function(t){return this.allowExtra?void 0:(this.addToArray(this.optionals,t),this)},OptionResolver.prototype.setRequired=function(t){return this.addToArray(this.required,t),this},OptionResolver.prototype.resolve=function(t){var e={};for(var i in this.defaults)this.defaults.hasOwnProperty(i)&&(e[i]=this.getValue(t,i));for(var n=this.required.length-1;n>=0;n--)if(i=this.required[n],"undefined"==typeof e[i])throw'Option "'+i+'" is required.';return e},OptionResolver.prototype.getValue=function(t,e){var i=null;if(!this.optionExists(e))throw'Unkown option "'+e+'".';return"undefined"!=typeof t[e]?i=t[e]:"undefined"!=typeof this.defaults[e]&&(i=this.defaults[e]),this.checkType(e,i),i},OptionResolver.prototype.checkType=function(t,e){var i="undefined"!=typeof this.types[t]?this.types[t]:!1,n=typeof e;if(i&&n!==i&&("string"===i&&(e=String(e)),"boolean"===i&&(e=Boolean(e)),"number"===i&&(e=Number(e)),n=typeof e,i!==n))throw'Wrong type for option "'+t+'". Expected '+this.types[t]+" but got "+typeof e},OptionResolver.prototype.optionExists=function(t){return this.allowExtra?!0:"undefined"!=typeof this.defaults[t]||this.optional.indexOf(t)>=0||this.required.indexOf(t)>=0},OptionResolver.prototype.addToArray=function(t,e){for(var i,n=e.length-1;n>=0;n--)i=e[n],t.indexOf(i)>=0&&t.push(i)},GamepadHandler.prototype=Object.create(EventEmitter.prototype),GamepadHandler.prototype.update=function(){var t=0,e=0,i=0;for(e=0;2>e;e++)for(i=0;2>i;i++)this.setStick(e,i,this.gamepad.axes[t],this.options.stick),t++;for(t=this.gamepad.buttons.length-1;t>=0;t--)this.setButton(t,this.gamepad.buttons[t],this.options.button)},GamepadHandler.prototype.setStick=function(t,e,i,n){n.deadZone&&i-n.deadZone&&(i=0),n.analog?n.precision&&(i=Math.round(i*n.precision)/n.precision):i=i>0?1:0>i?-1:0,this.sticks[t][e]!==i&&(this.sticks[t][e]=i,this.emit("axis",{gamepad:this.gamepad,axis:e,value:this.sticks[t][e]}))},GamepadHandler.prototype.setButton=function(t,e,i){var n=e.value;i.deadZone&&e.value-i.deadZone&&(n=0),i.analog?i.precision&&(n=Math.round(n*i.precision)/i.precision):n=e.pressed?1:0,this.buttons[t]!==n&&(this.buttons[t]=n,this.emit("button",{gamepad:this.gamepad,button:e,index:t,pressed:e.pressed,value:n}))},GamepadListener.prototype=Object.create(EventEmitter.prototype),GamepadListener.prototype.optionResolver=new OptionResolver(!1),GamepadListener.prototype.optionResolver.setDefaults({analog:!0,deadZone:0,precision:0,eventType:"gamepad"}),GamepadListener.prototype.optionResolver.setTypes({analog:"boolean",deadZone:"number",precision:"number",eventType:"string"}),GamepadListener.prototype.resolveOptions=function(t){var e="undefined"!=typeof t.stick,i="undefined"!=typeof t.button,n={stick:this.optionResolver.resolve(e?t.stick:i?{}:t),button:this.optionResolver.resolve(i?t.button:e?{}:t)};return n.stick.deadZone=Math.max(Math.min(n.stick.deadZone,1),0),n.button.deadZone=Math.max(Math.min(n.button.deadZone,1),0),n.stick.precision=n.stick.precision?Math.pow(10,n.stick.precision):0,n.button.precision=n.button.precision?Math.pow(10,n.button.precision):0,n},GamepadListener.prototype.start=function(){this.frame||this.update()},GamepadListener.prototype.stop=function(){this.frame&&(window.cancelAnimationFrame(this.frame),this.frame=null)},GamepadListener.prototype.update=function(){this.frame=window.requestAnimationFrame(this.update);for(var t=this.getGamepads(),e=t.length-1;e>=0;e--)t[e]&&("undefined"==typeof t[e].handler&&this.addGamepad(t[e]),t[e].handler.update())},GamepadListener.prototype.addGamepad=function(t){var e=new GamepadHandler(t,this.options);e.on("gamepad:axis",this.onAxis),e.on("gamepad:button",this.onButton),this.emit("gamepad:connected",{gamepad:t,index:t.index})},GamepadListener.prototype.onAxis=function(t){var e="gamepad:axis";"gamepad"==this.options.eventType?e="gamepad:"+t.detail.gamepad.index+":axis":"key"==this.options.eventType&&(e="gamepad:"+t.detail.gamepad.index+":axis:"+t.detail.axis),this.emit(e,t.detail)},GamepadListener.prototype.onButton=function(t){var e="gamepad:button";"gamepad"==this.options.eventType?e="gamepad:"+t.detail.gamepad.index+":button":"key"==this.options.eventType&&(e="gamepad:"+t.detail.gamepad.index+":button:"+t.detail.index),this.emit(e,t.detail)},GamepadListener.prototype.getGamepads=function(){return"undefined"!=typeof navigator.getGamepads?navigator.getGamepads():"undefined"!=typeof navigator.webkitGetGamepads?navigator.webkitGetGamepads():null}; \ No newline at end of file +function EventEmitter(){this._eventElement=document.createElement("div")}function OptionResolver(t){this.allowExtra="undefined"!=typeof t&&t,this.defaults={},this.types={},this.optional=[],this.required=[]}function GamepadHandler(t,e){EventEmitter.call(this),this.gamepad=t,this.options=this.resolveOptions("object"==typeof e?e:{}),this.sticks=new Array(this.gamepad.axes.length),this.buttons=new Array(this.gamepad.buttons.length);for(var i=this.sticks.length-1;i>=0;i--)this.sticks[i]=[0,0];for(var n=this.buttons.length-1;n>=0;n--)this.buttons[n]=0;this.gamepad.handler=this}function GamepadListener(t){EventEmitter.call(this),this.options="object"==typeof t?t:{},this.frame=null,this.update=this.update.bind(this),this.onAxis=this.onAxis.bind(this),this.onButton=this.onButton.bind(this),this.stop=this.stop.bind(this),this.handlers=new Array(4),window.addEventListener("error",this.stop)}EventEmitter.prototype.emit=function(t,e){this._eventElement.dispatchEvent(new CustomEvent(t,{detail:e}))},EventEmitter.prototype.addEventListener=function(t,e){this._eventElement.addEventListener(t,e,!1)},EventEmitter.prototype.removeEventListener=function(t,e){this._eventElement.removeEventListener(t,e,!1)},EventEmitter.prototype.on=EventEmitter.prototype.addEventListener,EventEmitter.prototype.off=EventEmitter.prototype.removeEventListener,OptionResolver.prototype.setDefaults=function(t){for(var e in t)t.hasOwnProperty(e)&&(this.defaults[e]=t[e]);return this},OptionResolver.prototype.setTypes=function(t){for(var e in t)t.hasOwnProperty(e)&&(this.types[e]=t[e]);return this},OptionResolver.prototype.setOptional=function(t){return this.allowExtra?void 0:(this.addToArray(this.optionals,t),this)},OptionResolver.prototype.setRequired=function(t){return this.addToArray(this.required,t),this},OptionResolver.prototype.resolve=function(t){var e={};for(var i in this.defaults)this.defaults.hasOwnProperty(i)&&(e[i]=this.getValue(t,i));for(var n=this.required.length-1;n>=0;n--)if(i=this.required[n],"undefined"==typeof e[i])throw'Option "'+i+'" is required.';return e},OptionResolver.prototype.getValue=function(t,e){var i=null;if(!this.optionExists(e))throw'Unkown option "'+e+'".';return"undefined"!=typeof t[e]?i=t[e]:"undefined"!=typeof this.defaults[e]&&(i=this.defaults[e]),this.checkType(e,i),i},OptionResolver.prototype.checkType=function(t,e){var i="undefined"!=typeof this.types[t]?this.types[t]:!1,n=typeof e;if(i&&n!==i&&("string"===i&&(e=String(e)),"boolean"===i&&(e=Boolean(e)),"number"===i&&(e=Number(e)),n=typeof e,i!==n))throw'Wrong type for option "'+t+'". Expected '+this.types[t]+" but got "+typeof e},OptionResolver.prototype.optionExists=function(t){return this.allowExtra?!0:"undefined"!=typeof this.defaults[t]||this.optional.indexOf(t)>=0||this.required.indexOf(t)>=0},OptionResolver.prototype.addToArray=function(t,e){for(var i,n=e.length-1;n>=0;n--)i=e[n],t.indexOf(i)>=0&&t.push(i)},GamepadHandler.prototype=Object.create(EventEmitter.prototype),GamepadHandler.prototype.optionResolver=new OptionResolver(!1),GamepadHandler.prototype.optionResolver.setDefaults({analog:!0,deadZone:0,precision:0}),GamepadHandler.prototype.optionResolver.setTypes({analog:"boolean",deadZone:"number",precision:"number"}),GamepadHandler.prototype.resolveOptions=function(t){var e="undefined"!=typeof t.stick,i="undefined"!=typeof t.button,n={stick:this.optionResolver.resolve(e?t.stick:i?{}:t),button:this.optionResolver.resolve(i?t.button:e?{}:t)};return n.stick.deadZone=Math.max(Math.min(n.stick.deadZone,1),0),n.button.deadZone=Math.max(Math.min(n.button.deadZone,1),0),n.stick.precision=n.stick.precision?Math.pow(10,n.stick.precision):0,n.button.precision=n.button.precision?Math.pow(10,n.button.precision):0,n},GamepadHandler.prototype.update=function(){var t=0,e=0,i=0;for(e=0;2>e;e++)for(i=0;2>i;i++)this.setStick(e,i,this.gamepad.axes[t],this.options.stick),t++;for(t=this.gamepad.buttons.length-1;t>=0;t--)this.setButton(t,this.gamepad.buttons[t],this.options.button)},GamepadHandler.prototype.setStick=function(t,e,i,n){n.deadZone&&i-n.deadZone&&(i=0),n.analog?n.precision&&(i=Math.round(i*n.precision)/n.precision):i=i>0?1:0>i?-1:0,this.sticks[t][e]!==i&&(this.sticks[t][e]=i,this.emit("axis",{gamepad:this.gamepad,axis:e,value:this.sticks[t][e]}))},GamepadHandler.prototype.setButton=function(t,e,i){var n=e.value;i.deadZone&&e.value-i.deadZone&&(n=0),i.analog?i.precision&&(n=Math.round(n*i.precision)/i.precision):n=e.pressed?1:0,this.buttons[t]!==n&&(this.buttons[t]=n,this.emit("button",{gamepad:this.gamepad,button:e,index:t,pressed:e.pressed,value:n}))},GamepadListener.prototype=Object.create(EventEmitter.prototype),GamepadListener.prototype.start=function(){this.frame||this.update()},GamepadListener.prototype.stop=function(){this.frame&&(window.cancelAnimationFrame(this.frame),this.frame=null)},GamepadListener.prototype.update=function(){this.frame=window.requestAnimationFrame(this.update);for(var t=this.getGamepads(),e=t.length-1;e>=0;e--)t[e]?("undefined"==typeof t[e].handler&&this.addGamepad(t[e]),t[e].handler.update()):this.handlers[e]&&this.removeGamepad(e)},GamepadListener.prototype.addGamepad=function(t){console.log("addGamepad",t);var e=new GamepadHandler(t,this.options);e.on("axis",this.onAxis),e.on("button",this.onButton),this.emit("gamepad:connected",{gamepad:t,index:t.index}),this.emit("gamepad:"+t.index+":connected",{gamepad:t,index:t.index}),this.handlers[t.index]=e},GamepadListener.prototype.removeGamepad=function(t){var e=this.handlers[t];console.log("removeGamepad",t,e,e.gamepad),e.off("axis",this.onAxis),e.off("button",this.onButton),this.emit("gamepad:disconnected",{index:t}),this.emit("gamepad:"+t+":disconnected",{index:t}),this.handlers[t]=null},GamepadListener.prototype.onAxis=function(t){this.emit("gamepad:axis",t.detail),this.emit("gamepad:"+t.detail.gamepad.index+":axis",t.detail),this.emit("gamepad:"+t.detail.gamepad.index+":axis:"+t.detail.axis,t.detail)},GamepadListener.prototype.onButton=function(t){this.emit("gamepad:button",t.detail),this.emit("gamepad:"+t.detail.gamepad.index+":button",t.detail),this.emit("gamepad:"+t.detail.gamepad.index+":button:"+t.detail.index,t.detail)},GamepadListener.prototype.getGamepads=function(){return"undefined"!=typeof navigator.getGamepads?navigator.getGamepads():"undefined"!=typeof navigator.webkitGetGamepads?navigator.webkitGetGamepads():null}; \ No newline at end of file diff --git a/src/GamepadHandler.js b/src/GamepadHandler.js index 0e46623..498abb4 100644 --- a/src/GamepadHandler.js +++ b/src/GamepadHandler.js @@ -9,7 +9,7 @@ function GamepadHandler(gamepad, options) EventEmitter.call(this); this.gamepad = gamepad; - this.options = options; + this.options = this.resolveOptions(typeof(options) === 'object' ? options : {}); this.sticks = new Array(this.gamepad.axes.length); this.buttons = new Array(this.gamepad.buttons.length); @@ -26,6 +26,49 @@ function GamepadHandler(gamepad, options) GamepadHandler.prototype = Object.create(EventEmitter.prototype); +/** + * Option resolver + * + * @type {OptionResolver} + */ +GamepadHandler.prototype.optionResolver = new OptionResolver(false); + +GamepadHandler.prototype.optionResolver.setDefaults({ + analog: true, + deadZone: 0, + precision: 0 +}); + +GamepadHandler.prototype.optionResolver.setTypes({ + analog: 'boolean', + deadZone: 'number', + precision: 'number' +}); + +/** + * Resolve options + * + * @param {Object} options + * + * @return {Object} + */ +GamepadHandler.prototype.resolveOptions = function(source) +{ + var customStick = typeof source.stick !== 'undefined', + customButton = typeof source.button !== 'undefined', + options = { + stick: this.optionResolver.resolve(customStick ? source.stick : (customButton ? {} : source)), + button: this.optionResolver.resolve(customButton ? source.button : (customStick ? {} : source)) + }; + + options.stick.deadZone = Math.max(Math.min(options.stick.deadZone, 1), 0); + options.button.deadZone = Math.max(Math.min(options.button.deadZone, 1), 0); + options.stick.precision = options.stick.precision ? Math.pow(10, options.stick.precision) : 0; + options.button.precision = options.button.precision ? Math.pow(10, options.button.precision) : 0; + + return options; +}; + /** * Update */ diff --git a/src/GamepadListener.js b/src/GamepadListener.js index ab09f09..badbf41 100644 --- a/src/GamepadListener.js +++ b/src/GamepadListener.js @@ -5,65 +5,20 @@ function GamepadListener(options) { EventEmitter.call(this); - this.options = this.resolveOptions(typeof(options) === 'object' ? options : {}); + this.options = typeof(options) === 'object' ? options : {}; this.frame = null; this.update = this.update.bind(this); this.onAxis = this.onAxis.bind(this); this.onButton = this.onButton.bind(this); this.stop = this.stop.bind(this); + this.handlers = new Array(4); window.addEventListener('error', this.stop); - this.start(); } GamepadListener.prototype = Object.create(EventEmitter.prototype); -/** - * Option resolver - * - * @type {OptionResolver} - */ -GamepadListener.prototype.optionResolver = new OptionResolver(false); - -GamepadListener.prototype.optionResolver.setDefaults({ - analog: true, - deadZone: 0, - precision: 0, - eventType: 'gamepad' -}); - -GamepadListener.prototype.optionResolver.setTypes({ - analog: 'boolean', - deadZone: 'number', - precision: 'number', - eventType: 'string' -}); - -/** - * Resolve options - * - * @param {Object} options - * - * @return {Object} - */ -GamepadListener.prototype.resolveOptions = function(source) -{ - var customStick = typeof source.stick !== 'undefined', - customButton = typeof source.button !== 'undefined', - options = { - stick: this.optionResolver.resolve(customStick ? source.stick : (customButton ? {} : source)), - button: this.optionResolver.resolve(customButton ? source.button : (customStick ? {} : source)) - }; - - options.stick.deadZone = Math.max(Math.min(options.stick.deadZone, 1), 0); - options.button.deadZone = Math.max(Math.min(options.button.deadZone, 1), 0); - options.stick.precision = options.stick.precision ? Math.pow(10, options.stick.precision) : 0; - options.button.precision = options.button.precision ? Math.pow(10, options.button.precision) : 0; - - return options; -}; - /** * Start */ @@ -101,6 +56,8 @@ GamepadListener.prototype.update = function() } gamepads[i].handler.update(); + } else if (this.handlers[i]) { + this.removeGamepad(i); } } }; @@ -112,12 +69,37 @@ GamepadListener.prototype.update = function() */ GamepadListener.prototype.addGamepad = function(gamepad) { + console.log('addGamepad', gamepad); + var handler = new GamepadHandler(gamepad, this.options); - handler.on('gamepad:axis', this.onAxis); - handler.on('gamepad:button', this.onButton); + handler.on('axis', this.onAxis); + handler.on('button', this.onButton); this.emit('gamepad:connected', {gamepad: gamepad, index: gamepad.index}); + this.emit('gamepad:' + gamepad.index + ':connected', {gamepad: gamepad, index: gamepad.index}); + + this.handlers[gamepad.index] = handler; +}; + +/** + * Add gamepad + * + * @param {Gamepad} gamepad + */ +GamepadListener.prototype.removeGamepad = function(index) +{ + var handler = this.handlers[index]; + + console.log('removeGamepad', index, handler, handler.gamepad); + + handler.off('axis', this.onAxis); + handler.off('button', this.onButton); + + this.emit('gamepad:disconnected', {index: index}); + this.emit('gamepad:' + index + ':disconnected', {index: index}); + + this.handlers[index] = null; }; /** @@ -127,15 +109,9 @@ GamepadListener.prototype.addGamepad = function(gamepad) */ GamepadListener.prototype.onAxis = function(event) { - var eventName = 'gamepad:axis'; - - if (this.options.eventType == 'gamepad') { - eventName = 'gamepad:' + event.detail.gamepad.index + ':axis'; - } else if (this.options.eventType == 'key') { - eventName = 'gamepad:' + event.detail.gamepad.index + ':axis:' + event.detail.axis; - } - - this.emit(eventName, event.detail); + this.emit('gamepad:axis', event.detail); + this.emit('gamepad:' + event.detail.gamepad.index + ':axis', event.detail); + this.emit('gamepad:' + event.detail.gamepad.index + ':axis:' + event.detail.axis, event.detail); }; /** @@ -145,15 +121,9 @@ GamepadListener.prototype.onAxis = function(event) */ GamepadListener.prototype.onButton = function(event) { - var eventName = 'gamepad:button'; - - if (this.options.eventType == 'gamepad') { - eventName = 'gamepad:' + event.detail.gamepad.index + ':button'; - } else if (this.options.eventType == 'key') { - eventName = 'gamepad:' + event.detail.gamepad.index + ':button:' + event.detail.index; - } - - this.emit(eventName, event.detail); + this.emit('gamepad:button', event.detail); + this.emit('gamepad:' + event.detail.gamepad.index + ':button', event.detail); + this.emit('gamepad:' + event.detail.gamepad.index + ':button:' + event.detail.index, event.detail); }; /**