feat(): WIP add telemetry skin
This commit is contained in:
parent
000036e4f2
commit
203d3bf110
17
.vscode/tasks.json
vendored
Normal file
17
.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||||
|
// for the documentation about the tasks.json format
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "docker",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "docker compose up",
|
||||||
|
"problemMatcher": [],
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -134,6 +134,7 @@ body.unsupported #gamepad {
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background: whitesmoke;
|
background: whitesmoke;
|
||||||
border: none;
|
border: none;
|
||||||
|
max-width: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#help-popout {
|
#help-popout {
|
||||||
|
@ -84,6 +84,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="gamepad"></div>
|
<div id="gamepad"></div>
|
||||||
<div id="overlay" style="display: none;">
|
<div id="overlay" style="display: none;">
|
||||||
|
<span id="gamepad-id">
|
||||||
|
<label for="gamepad-id">Gamepad</label>
|
||||||
|
<select name="gamepad-id">
|
||||||
|
<option value="auto">Auto</option>
|
||||||
|
</select>
|
||||||
|
</span>
|
||||||
<span id="skin">
|
<span id="skin">
|
||||||
<label for="skin">Skin</label>
|
<label for="skin">Skin</label>
|
||||||
<select name="skin">
|
<select name="skin">
|
||||||
@ -91,6 +97,7 @@
|
|||||||
<option value="ds4">DualShock 4</option>
|
<option value="ds4">DualShock 4</option>
|
||||||
<option value="xbox-one">Xbox One</option>
|
<option value="xbox-one">Xbox One</option>
|
||||||
<option value="debug">Debug</option>
|
<option value="debug">Debug</option>
|
||||||
|
<option value="telemetry">Telemetry</option>
|
||||||
</select>
|
</select>
|
||||||
</span>
|
</span>
|
||||||
<span id="background">
|
<span id="background">
|
||||||
|
449
js/gamepad.js
449
js/gamepad.js
@ -9,39 +9,38 @@ class Gamepad {
|
|||||||
*/
|
*/
|
||||||
constructor() {
|
constructor() {
|
||||||
// cached DOM references
|
// cached DOM references
|
||||||
this.$body = $("body");
|
this.$body = $('body');
|
||||||
this.$instructions = $("#instructions");
|
this.$instructions = $('#instructions');
|
||||||
this.$placeholder = $("#placeholder");
|
this.$placeholder = $('#placeholder');
|
||||||
this.$gamepad = $("#gamepad");
|
this.$gamepad = $('#gamepad');
|
||||||
this.$overlay = $("#overlay");
|
this.$overlay = $('#overlay');
|
||||||
this.$skinSelect = $("select[name=skin]");
|
this.$gamepadSelect = $('select[name=gamepad-id]');
|
||||||
this.$backgroundSelect = $("select[name=background]");
|
this.$skinSelect = $('select[name=skin]');
|
||||||
this.$colorOverlay = this.$overlay.find("#color");
|
this.$backgroundSelect = $('select[name=background]');
|
||||||
this.$colorSelect = this.$colorOverlay.find("select[name=color]");
|
this.$colorOverlay = this.$overlay.find('#color');
|
||||||
this.$triggersOverlay = this.$overlay.find("#triggers");
|
this.$colorSelect = this.$colorOverlay.find('select[name=color]');
|
||||||
this.$triggersSelect = this.$triggersOverlay.find(
|
this.$triggersOverlay = this.$overlay.find('#triggers');
|
||||||
"select[name=triggers]"
|
this.$triggersSelect = this.$triggersOverlay.find('select[name=triggers]');
|
||||||
);
|
this.$helpPopout = $('#help-popout');
|
||||||
this.$helpPopout = $("#help-popout");
|
this.$gamepadList = $('#gamepad-list');
|
||||||
this.$gamepadList = $("#gamepad-list");
|
|
||||||
|
|
||||||
this.backgroundStyle = [
|
this.backgroundStyle = [
|
||||||
"transparent",
|
'transparent',
|
||||||
"checkered",
|
'checkered',
|
||||||
"dimgrey",
|
'dimgrey',
|
||||||
"black",
|
'black',
|
||||||
"white",
|
'white',
|
||||||
"lime",
|
'lime',
|
||||||
"magenta",
|
'magenta',
|
||||||
];
|
];
|
||||||
this.textColors = [
|
this.textColors = [
|
||||||
"black",
|
'black',
|
||||||
"black",
|
'black',
|
||||||
"black",
|
'black',
|
||||||
"white",
|
'white',
|
||||||
"black",
|
'black',
|
||||||
"black",
|
'black',
|
||||||
"black",
|
'black',
|
||||||
];
|
];
|
||||||
|
|
||||||
// ensure the GamePad API is available on this browser
|
// ensure the GamePad API is available on this browser
|
||||||
@ -55,40 +54,44 @@ class Gamepad {
|
|||||||
// See: https://html5gamepad.com/codes
|
// See: https://html5gamepad.com/codes
|
||||||
debug: {
|
debug: {
|
||||||
id: /debug/,
|
id: /debug/,
|
||||||
name: "Debug",
|
name: 'Debug',
|
||||||
},
|
},
|
||||||
ds4: {
|
ds4: {
|
||||||
id: /054c|54c|09cc|046d|0810|2563/, // 054c = Sony vendor code, 046d,0810,2563 = PS-like controllers vendor codes
|
id: /054c|54c|09cc|046d|0810|2563/, // 054c = Sony vendor code, 046d,0810,2563 = PS-like controllers vendor codes
|
||||||
name: "DualShock 4",
|
name: 'DualShock 4',
|
||||||
colors: ["black", "white", "red", "blue"],
|
colors: ['black', 'white', 'red', 'blue'],
|
||||||
triggers: true,
|
triggers: true,
|
||||||
},
|
},
|
||||||
// gamecube: {
|
// gamecube: {
|
||||||
// id: /0079/, // 0079 = Nintendo GameCube vendor code
|
// id: /0079/, // 0079 = Nintendo GameCube vendor code
|
||||||
// name: "GameCube Controller",
|
// name: 'GameCube Controller',
|
||||||
// colors: ["black", "purple"],
|
// colors: ['black', 'purple'],
|
||||||
// },
|
// },
|
||||||
// "joy-con": {
|
// 'joy-con': {
|
||||||
// id: /200e/, // 0079 = Joy-Con specific product code
|
// id: /200e/, // 0079 = Joy-Con specific product code
|
||||||
// name: "Joy-Con (L+R) Controllers",
|
// name: 'Joy-Con (L+R) Controllers',
|
||||||
// colors: ["blue-red", "grey-grey"],
|
// colors: ['blue-red', 'grey-grey'],
|
||||||
// },
|
// },
|
||||||
// stadia: {
|
// stadia: {
|
||||||
// id: /18d1/, // 18d1 = Google vendor code
|
// id: /18d1/, // 18d1 = Google vendor code
|
||||||
// name: "Stadia Controller",
|
// name: 'Stadia Controller',
|
||||||
// colors: ["black"],
|
// colors: ['black'],
|
||||||
// },
|
// },
|
||||||
// "switch-pro": {
|
// 'switch-pro': {
|
||||||
// id: /057e|20d6/, // 057e = Nintendo Switch vendor code, 20d6 = Switch Pro-like vendor code
|
// id: /057e|20d6/, // 057e = Nintendo Switch vendor code, 20d6 = Switch Pro-like vendor code
|
||||||
// name: "Switch Pro Controller",
|
// name: 'Switch Pro Controller',
|
||||||
// colors: ["black"],
|
// colors: ['black'],
|
||||||
// },
|
// },
|
||||||
"xbox-one": {
|
'xbox-one': {
|
||||||
id: /045e|xinput|XInput/, // 045e = Microsoft vendor code, xinput = standard Windows controller
|
id: /045e|xinput|XInput/, // 045e = Microsoft vendor code, xinput = standard Windows controller
|
||||||
name: "Xbox One",
|
name: 'Xbox One',
|
||||||
colors: ["black", "white"],
|
colors: ['black', 'white'],
|
||||||
triggers: true,
|
triggers: true,
|
||||||
},
|
},
|
||||||
|
'telemetry': {
|
||||||
|
id: /telemetry/, // 045e = Microsoft vendor code, xinput = standard Windows controller
|
||||||
|
name: 'Telemetry'
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// gamepad help default values
|
// gamepad help default values
|
||||||
@ -100,6 +103,7 @@ class Gamepad {
|
|||||||
this.overlayDelay = 5000;
|
this.overlayDelay = 5000;
|
||||||
|
|
||||||
// active gamepad default values
|
// active gamepad default values
|
||||||
|
this.isFirstscan = true;
|
||||||
this.scanDelay = 200;
|
this.scanDelay = 200;
|
||||||
this.debug = false;
|
this.debug = false;
|
||||||
this.index = null;
|
this.index = null;
|
||||||
@ -111,7 +115,7 @@ class Gamepad {
|
|||||||
this.colorIndex = null;
|
this.colorIndex = null;
|
||||||
this.colorName = null;
|
this.colorName = null;
|
||||||
this.triggersMeter = false;
|
this.triggersMeter = false;
|
||||||
this.zoomMode = "auto";
|
this.zoomMode = 'auto';
|
||||||
this.zoomLevel = 1;
|
this.zoomLevel = 1;
|
||||||
this.mapping = {
|
this.mapping = {
|
||||||
buttons: [],
|
buttons: [],
|
||||||
@ -122,36 +126,36 @@ class Gamepad {
|
|||||||
// this.hash = this.readHash();
|
// this.hash = this.readHash();
|
||||||
|
|
||||||
// listen for gamepad related events
|
// listen for gamepad related events
|
||||||
this.haveEvents = "GamepadEvent" in window;
|
this.haveEvents = 'GamepadEvent' in window;
|
||||||
if (this.haveEvents) {
|
if (this.haveEvents) {
|
||||||
window.addEventListener(
|
window.addEventListener(
|
||||||
"gamepadconnected",
|
'gamepadconnected',
|
||||||
this.onGamepadConnect.bind(this)
|
this.onGamepadConnect.bind(this)
|
||||||
);
|
);
|
||||||
window.addEventListener(
|
window.addEventListener(
|
||||||
"gamepaddisconnected",
|
'gamepaddisconnected',
|
||||||
this.onGamepadDisconnect.bind(this)
|
this.onGamepadDisconnect.bind(this)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// listen for mouse move events
|
// listen for mouse move events
|
||||||
window.addEventListener("mousemove", this.onMouseMove.bind(this));
|
window.addEventListener('mousemove', this.onMouseMove.bind(this));
|
||||||
// listen for keyboard events
|
// listen for keyboard events
|
||||||
window.addEventListener("keydown", this.onKeyDown.bind(this));
|
window.addEventListener('keydown', this.onKeyDown.bind(this));
|
||||||
// listen for keyboard events
|
// listen for keyboard events
|
||||||
window.addEventListener("resize", this.onResize.bind(this));
|
window.addEventListener('resize', this.onResize.bind(this));
|
||||||
|
|
||||||
// bind a gamepads scan
|
// bind a gamepads scan
|
||||||
window.setInterval(this.scan.bind(this), this.scanDelay);
|
window.setInterval(this.scan.bind(this), this.scanDelay);
|
||||||
|
|
||||||
// change the type if specified
|
// change the type if specified
|
||||||
const skin = this.getUrlParam("type");
|
const skin = this.getUrlParam('type');
|
||||||
if (skin) {
|
if (skin) {
|
||||||
this.changeSkin(skin);
|
this.changeSkin(skin);
|
||||||
}
|
}
|
||||||
|
|
||||||
// change the background if specified
|
// change the background if specified
|
||||||
const background = this.getUrlParam("background");
|
const background = this.getUrlParam('background');
|
||||||
if (background) {
|
if (background) {
|
||||||
let backgroundStyleIndex;
|
let backgroundStyleIndex;
|
||||||
for (let i = 0; i < this.backgroundStyle.length; i++) {
|
for (let i = 0; i < this.backgroundStyle.length; i++) {
|
||||||
@ -179,8 +183,8 @@ class Gamepad {
|
|||||||
? () => navigator.webkitGetGamepads()
|
? () => navigator.webkitGetGamepads()
|
||||||
: null;
|
: null;
|
||||||
if (!getGamepadsFn) {
|
if (!getGamepadsFn) {
|
||||||
this.$body.addClass("unsupported");
|
this.$body.addClass('unsupported');
|
||||||
throw new Error("Unsupported gamepad API");
|
throw new Error('Unsupported gamepad API');
|
||||||
}
|
}
|
||||||
this.getNavigatorGamepads = getGamepadsFn;
|
this.getNavigatorGamepads = getGamepadsFn;
|
||||||
}
|
}
|
||||||
@ -189,17 +193,20 @@ class Gamepad {
|
|||||||
* Initialises the overlay selectors
|
* Initialises the overlay selectors
|
||||||
*/
|
*/
|
||||||
initOverlaySelectors() {
|
initOverlaySelectors() {
|
||||||
this.$skinSelect.on("change", () =>
|
this.$gamepadSelect.on('change', () =>
|
||||||
|
this.changeGamepad(this.$gamepadSelect.val())
|
||||||
|
);
|
||||||
|
this.$skinSelect.on('change', () =>
|
||||||
this.changeSkin(this.$skinSelect.val())
|
this.changeSkin(this.$skinSelect.val())
|
||||||
);
|
);
|
||||||
this.$backgroundSelect.on("change", () =>
|
this.$backgroundSelect.on('change', () =>
|
||||||
this.changeBackgroundStyle(this.$backgroundSelect.val())
|
this.changeBackgroundStyle(this.$backgroundSelect.val())
|
||||||
);
|
);
|
||||||
this.$colorSelect.on("change", () =>
|
this.$colorSelect.on('change', () =>
|
||||||
this.changeGamepadColor(this.$colorSelect.val())
|
this.changeGamepadColor(this.$colorSelect.val())
|
||||||
);
|
);
|
||||||
this.$triggersSelect.on("change", () =>
|
this.$triggersSelect.on('change', () =>
|
||||||
this.toggleTriggersMeter(this.$triggersSelect.val() === "meter")
|
this.toggleTriggersMeter(this.$triggersSelect.val() === 'meter')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -299,6 +306,25 @@ class Gamepad {
|
|||||||
}, this.overlayDelay);
|
}, this.overlayDelay);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the list of connected gamepads in the overlay
|
||||||
|
*/
|
||||||
|
updateGamepadList() {
|
||||||
|
this.$gamepadSelect.find('.entry').remove();
|
||||||
|
const $options = [];
|
||||||
|
for (let key = 0; key < this.gamepads.length; key++) {
|
||||||
|
const gamepad = this.gamepads[key];
|
||||||
|
if (!gamepad) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$options.push(
|
||||||
|
`<option class='entry' value='${gamepad.id}'>${gamepad.id}</option>'`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this.$gamepadSelect.append($options.join(''));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update colors following the active/inactive gamepad
|
* Update colors following the active/inactive gamepad
|
||||||
*/
|
*/
|
||||||
@ -315,7 +341,7 @@ class Gamepad {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const colorOptions = colors.map(
|
const colorOptions = colors.map(
|
||||||
(color) => `<option value="${color}">${color}</option>`
|
(color) => `<option value='${color}'>${color}</option>`
|
||||||
);
|
);
|
||||||
this.$colorSelect.html(colorOptions);
|
this.$colorSelect.html(colorOptions);
|
||||||
this.$colorOverlay.fadeIn();
|
this.$colorOverlay.fadeIn();
|
||||||
@ -345,6 +371,12 @@ class Gamepad {
|
|||||||
* @param {GamepadEvent} e
|
* @param {GamepadEvent} e
|
||||||
*/
|
*/
|
||||||
onGamepadConnect(e) {
|
onGamepadConnect(e) {
|
||||||
|
// refresh gamepads information
|
||||||
|
this.pollGamepads();
|
||||||
|
|
||||||
|
// refresh gamepad list on overlay
|
||||||
|
this.updateGamepadList();
|
||||||
|
|
||||||
// refresh gamepad list on help, if displayed
|
// refresh gamepad list on help, if displayed
|
||||||
if (this.helpVisible) this.buildHelpGamepadList();
|
if (this.helpVisible) this.buildHelpGamepadList();
|
||||||
}
|
}
|
||||||
@ -355,9 +387,15 @@ class Gamepad {
|
|||||||
* @param {GamepadEvent} e
|
* @param {GamepadEvent} e
|
||||||
*/
|
*/
|
||||||
onGamepadDisconnect(e) {
|
onGamepadDisconnect(e) {
|
||||||
|
// refresh gamepads information
|
||||||
|
this.pollGamepads();
|
||||||
|
|
||||||
|
// refresh gamepad list on overlay
|
||||||
|
this.updateGamepadList();
|
||||||
|
|
||||||
if (e.gamepad.index === this.index) {
|
if (e.gamepad.index === this.index) {
|
||||||
// display a disconnection indicator
|
// display a disconnection indicator
|
||||||
this.$gamepad.addClass("disconnected");
|
this.$gamepad.addClass('disconnected');
|
||||||
this.disconnectedIndex = e.gamepad.index;
|
this.disconnectedIndex = e.gamepad.index;
|
||||||
|
|
||||||
// refresh gamepad list on help, if displayed
|
// refresh gamepad list on help, if displayed
|
||||||
@ -366,7 +404,7 @@ class Gamepad {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the mouse "mousemove" event
|
* Handles the mouse 'mousemove' event
|
||||||
*
|
*
|
||||||
* @param {MouseEvent} e
|
* @param {MouseEvent} e
|
||||||
*/
|
*/
|
||||||
@ -377,61 +415,61 @@ class Gamepad {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the keyboard "keydown" event
|
* Handles the keyboard 'keydown' event
|
||||||
*
|
*
|
||||||
* @param {KeyboardEvent} e
|
* @param {KeyboardEvent} e
|
||||||
*/
|
*/
|
||||||
onKeyDown(e) {
|
onKeyDown(e) {
|
||||||
switch (e.code) {
|
switch (e.code) {
|
||||||
case "Delete":
|
case 'Delete':
|
||||||
case "Escape":
|
case 'Escape':
|
||||||
this.clear();
|
this.clear();
|
||||||
this.displayPlaceholder();
|
this.displayPlaceholder();
|
||||||
break;
|
break;
|
||||||
case "KeyB":
|
case 'KeyB':
|
||||||
this.changeBackgroundStyle();
|
this.changeBackgroundStyle();
|
||||||
break;
|
break;
|
||||||
case "KeyC":
|
case 'KeyC':
|
||||||
this.changeGamepadColor();
|
this.changeGamepadColor();
|
||||||
break;
|
break;
|
||||||
case "KeyD":
|
case 'KeyD':
|
||||||
this.toggleDebug();
|
this.toggleDebug();
|
||||||
break;
|
break;
|
||||||
case "KeyG":
|
case 'KeyG':
|
||||||
this.toggleGamepadType();
|
this.toggleGamepadType();
|
||||||
break;
|
break;
|
||||||
case "KeyH":
|
case 'KeyH':
|
||||||
this.toggleHelp();
|
this.toggleHelp();
|
||||||
break;
|
break;
|
||||||
case "KeyT":
|
case 'KeyT':
|
||||||
this.toggleTriggersMeter();
|
this.toggleTriggersMeter();
|
||||||
break;
|
break;
|
||||||
case "NumpadAdd":
|
case 'NumpadAdd':
|
||||||
case "Equal":
|
case 'Equal':
|
||||||
this.changeZoom("+");
|
this.changeZoom('+');
|
||||||
break;
|
break;
|
||||||
case "NumpadSubtract":
|
case 'NumpadSubtract':
|
||||||
case "Minus":
|
case 'Minus':
|
||||||
this.changeZoom("-");
|
this.changeZoom('-');
|
||||||
break;
|
break;
|
||||||
case "Numpad5":
|
case 'Numpad5':
|
||||||
case "Digit5":
|
case 'Digit5':
|
||||||
this.changeZoom("auto");
|
this.changeZoom('auto');
|
||||||
break;
|
break;
|
||||||
case "Numpad0":
|
case 'Numpad0':
|
||||||
case "Digit0":
|
case 'Digit0':
|
||||||
this.changeZoom(0);
|
this.changeZoom(0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the keyboard "keydown" event
|
* Handles the keyboard 'keydown' event
|
||||||
*
|
*
|
||||||
* @param {WindowEvent} e
|
* @param {WindowEvent} e
|
||||||
*/
|
*/
|
||||||
onResize(e) {
|
onResize(e) {
|
||||||
if (this.zoomMode === "auto") this.changeZoom("auto");
|
if (this.zoomMode === 'auto') this.changeZoom('auto');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -447,9 +485,6 @@ class Gamepad {
|
|||||||
* Builds the help gamepad list
|
* Builds the help gamepad list
|
||||||
*/
|
*/
|
||||||
buildHelpGamepadList() {
|
buildHelpGamepadList() {
|
||||||
// refresh gamepads information
|
|
||||||
this.pollGamepads();
|
|
||||||
|
|
||||||
const $tbody = [];
|
const $tbody = [];
|
||||||
for (let key = 0; key < this.gamepads.length; key++) {
|
for (let key = 0; key < this.gamepads.length; key++) {
|
||||||
const gamepad = this.gamepads[key];
|
const gamepad = this.gamepads[key];
|
||||||
@ -458,18 +493,20 @@ class Gamepad {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$tbody.push(
|
$tbody.push(
|
||||||
`<tr><td>${gamepad.index}</td><td>${gamepad.id}</td></tr>"`
|
`<tr><td>${gamepad.index}</td><td>${gamepad.id}</td></tr>'`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if ($tbody.length === 0) {
|
if ($tbody.length === 0) {
|
||||||
$tbody.push('<tr><td colspan="2">No gamepad detected.</td></tr>');
|
$tbody.push('<tr><td colspan="2">No gamepad detected.</td></tr>');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$gamepadList.html($tbody.join(""));
|
this.$gamepadList.html($tbody.join(''));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the connected gamepad
|
* Return the connected gamepad
|
||||||
|
*
|
||||||
|
* @returns {object}
|
||||||
*/
|
*/
|
||||||
getActive() {
|
getActive() {
|
||||||
return this.gamepads[this.index];
|
return this.gamepads[this.index];
|
||||||
@ -479,14 +516,15 @@ class Gamepad {
|
|||||||
* Return the gamepad type for the connected gamepad
|
* Return the gamepad type for the connected gamepad
|
||||||
*
|
*
|
||||||
* @param {object} gamepad
|
* @param {object} gamepad
|
||||||
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
getType(gamepad) {
|
getType(gamepad) {
|
||||||
const type = this.getUrlParam("type");
|
const type = this.getUrlParam('type');
|
||||||
|
|
||||||
// if the debug option is active, use the associated template
|
// if the debug option is active, use the associated template
|
||||||
if (type === "debug") this.debug = true;
|
if (type === 'debug') this.debug = true;
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
return "debug";
|
return 'debug';
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the gamepad type is set through params, apply it
|
// if the gamepad type is set through params, apply it
|
||||||
@ -501,7 +539,7 @@ class Gamepad {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return "xbox-one";
|
return 'xbox-one';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -514,6 +552,12 @@ class Gamepad {
|
|||||||
// refresh gamepad information
|
// refresh gamepad information
|
||||||
this.pollGamepads();
|
this.pollGamepads();
|
||||||
|
|
||||||
|
if (this.isFirstscan) {
|
||||||
|
// update the overlay list
|
||||||
|
this.updateGamepadList();
|
||||||
|
this.isFirstscan = false;
|
||||||
|
}
|
||||||
|
|
||||||
for (let index = 0; index < this.gamepads.length; index++) {
|
for (let index = 0; index < this.gamepads.length; index++) {
|
||||||
if (
|
if (
|
||||||
null !== this.disconnectedIndex &&
|
null !== this.disconnectedIndex &&
|
||||||
@ -524,6 +568,15 @@ class Gamepad {
|
|||||||
const gamepad = this.gamepads[index];
|
const gamepad = this.gamepads[index];
|
||||||
if (!gamepad) continue;
|
if (!gamepad) continue;
|
||||||
|
|
||||||
|
// check the parameters for a selected gamepad
|
||||||
|
const gamepadId = this.getUrlParam('gamepad');
|
||||||
|
if (gamepadId) {
|
||||||
|
if (gamepad.id === gamepadId) {
|
||||||
|
this.map(gamepad.index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// read the gamepad buttons
|
// read the gamepad buttons
|
||||||
let button;
|
let button;
|
||||||
for (
|
for (
|
||||||
@ -563,17 +616,17 @@ class Gamepad {
|
|||||||
*/
|
*/
|
||||||
map(index) {
|
map(index) {
|
||||||
// ensure a gamepad need to be mapped
|
// ensure a gamepad need to be mapped
|
||||||
if ("undefined" === typeof index) return;
|
if ('undefined' === typeof index) return;
|
||||||
|
|
||||||
// hide the help messages
|
// hide the help messages
|
||||||
this.hideInstructions(true);
|
this.hideInstructions(true);
|
||||||
this.$helpPopout.removeClass("active");
|
this.$helpPopout.removeClass('active');
|
||||||
this.hidePlaceholder(true);
|
this.hidePlaceholder(true);
|
||||||
|
|
||||||
// update local references
|
// update local references
|
||||||
this.index = index;
|
this.index = index;
|
||||||
this.disconnectedIndex = null;
|
this.disconnectedIndex = null;
|
||||||
this.$gamepad.removeClass("disconnected");
|
this.$gamepad.removeClass('disconnected');
|
||||||
const gamepad = this.getActive();
|
const gamepad = this.getActive();
|
||||||
|
|
||||||
// ensure that a gamepad was actually found for this index
|
// ensure that a gamepad was actually found for this index
|
||||||
@ -594,7 +647,8 @@ class Gamepad {
|
|||||||
// initial setup of the gamepad
|
// initial setup of the gamepad
|
||||||
this.identifier = this.identifiers[this.type];
|
this.identifier = this.identifiers[this.type];
|
||||||
|
|
||||||
// update gamepad color and triggers selectors on overlay
|
// update the overlay selectors
|
||||||
|
this.$gamepadSelect.val(gamepad.id);
|
||||||
this.updateColors();
|
this.updateColors();
|
||||||
this.updateTriggers();
|
this.updateTriggers();
|
||||||
|
|
||||||
@ -607,15 +661,27 @@ class Gamepad {
|
|||||||
|
|
||||||
// save statistics
|
// save statistics
|
||||||
if (!!window.ga) {
|
if (!!window.ga) {
|
||||||
ga("send", "event", {
|
ga('send', 'event', {
|
||||||
eventCategory: "Gamepad",
|
eventCategory: 'Gamepad',
|
||||||
eventAction: "map",
|
eventAction: 'map',
|
||||||
eventLabel: "Map",
|
eventLabel: 'Map',
|
||||||
eventValue: this.identifier,
|
eventValue: this.identifier,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes a SHA-1 for a given string
|
||||||
|
*
|
||||||
|
* @param {string} value
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
async toHash(value) {
|
||||||
|
return crypto.subtle
|
||||||
|
.digest('SHA-1', new TextEncoder().encode(value))
|
||||||
|
.then(ab => encodeURIComponent(String.fromCharCode.apply(null, new Uint8Array(ab))));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disconnect the active gamepad
|
* Disconnect the active gamepad
|
||||||
*
|
*
|
||||||
@ -637,16 +703,17 @@ class Gamepad {
|
|||||||
this.colorName = null;
|
this.colorName = null;
|
||||||
this.zoomLevel = 1;
|
this.zoomLevel = 1;
|
||||||
this.$gamepad.empty();
|
this.$gamepad.empty();
|
||||||
|
this.$gamepadSelect.val('auto')
|
||||||
this.updateColors();
|
this.updateColors();
|
||||||
this.updateTriggers();
|
this.updateTriggers();
|
||||||
this.clearUrlParams();
|
this.clearUrlParams();
|
||||||
|
|
||||||
// save statistics
|
// save statistics
|
||||||
if (!!window.ga) {
|
if (!!window.ga) {
|
||||||
ga("send", "event", {
|
ga('send', 'event', {
|
||||||
eventCategory: "Gamepad",
|
eventCategory: 'Gamepad',
|
||||||
eventAction: "disconnect",
|
eventAction: 'disconnect',
|
||||||
eventLabel: "Disconnect",
|
eventLabel: 'Disconnect',
|
||||||
eventValue: this.identifier,
|
eventValue: this.identifier,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -667,22 +734,22 @@ class Gamepad {
|
|||||||
|
|
||||||
// read for parameters to apply:
|
// read for parameters to apply:
|
||||||
// - color
|
// - color
|
||||||
this.changeGamepadColor(this.getUrlParam("color"));
|
this.changeGamepadColor(this.getUrlParam('color'));
|
||||||
// - triggers mode
|
// - triggers mode
|
||||||
this.toggleTriggersMeter(this.getUrlParam("triggers") === "meter");
|
this.toggleTriggersMeter(this.getUrlParam('triggers') === 'meter');
|
||||||
// - zoom$
|
// - zoom$
|
||||||
window.setTimeout(() =>
|
window.setTimeout(() =>
|
||||||
this.changeZoom(
|
this.changeZoom(
|
||||||
this.type === "debug"
|
this.type === 'debug'
|
||||||
? "auto"
|
? 'auto'
|
||||||
: this.getUrlParam("zoom") || "auto"
|
: this.getUrlParam('zoom') || 'auto'
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// save the buttons mapping of this template
|
// save the buttons mapping of this template
|
||||||
this.mapping.buttons = [];
|
this.mapping.buttons = [];
|
||||||
for (let index = 0; index < gamepad.buttons.length; index++) {
|
for (let index = 0; index < gamepad.buttons.length; index++) {
|
||||||
this.mapping.buttons[index] = $(`[data-button="${index}"]`);
|
this.mapping.buttons[index] = $(`[data-button='${index}']`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// save the axes mapping of this template
|
// save the axes mapping of this template
|
||||||
@ -738,20 +805,20 @@ class Gamepad {
|
|||||||
for (let index = 0; index < gamepad.buttons.length; index++) {
|
for (let index = 0; index < gamepad.buttons.length; index++) {
|
||||||
// find the DOM element
|
// find the DOM element
|
||||||
const $button = this.mapping.buttons[index];
|
const $button = this.mapping.buttons[index];
|
||||||
if (!$button) {
|
if (!$button || $button.length === 0) {
|
||||||
// nothing to do for this button if no DOM element exists
|
// nothing to do for this button if no DOM element exists
|
||||||
break;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// read the button data
|
// read the button data
|
||||||
const button = gamepad.buttons[index];
|
const button = gamepad.buttons[index];
|
||||||
|
|
||||||
// update the display values
|
// update the display values
|
||||||
$button.attr("data-pressed", button.pressed);
|
$button.attr('data-pressed', button.pressed);
|
||||||
$button.attr("data-value", button.value);
|
$button.attr('data-value', button.value);
|
||||||
|
|
||||||
// hook the template defined button update method
|
// hook the template defined button update method
|
||||||
if ("function" === typeof this.updateButton) {
|
if ('function' === typeof this.updateButton) {
|
||||||
this.updateButton($button);
|
this.updateButton($button);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -767,35 +834,44 @@ class Gamepad {
|
|||||||
for (let index = 0; index < gamepad.axes.length; index++) {
|
for (let index = 0; index < gamepad.axes.length; index++) {
|
||||||
// find the DOM element
|
// find the DOM element
|
||||||
const $axis = this.mapping.axes[index];
|
const $axis = this.mapping.axes[index];
|
||||||
if (!$axis) {
|
if (!$axis || $axis.length === 0) {
|
||||||
// nothing to do for this button if no DOM element exists
|
// nothing to do for this axis if no DOM element exists
|
||||||
break;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// read the axis data
|
// read the axis data
|
||||||
const axis = gamepad.axes[index];
|
const axis = gamepad.axes[index];
|
||||||
|
|
||||||
// update the display values
|
// update the display values
|
||||||
if ($axis.is("[data-axis=" + index + "]")) {
|
if ($axis.is('[data-axis=' + index + ']')) {
|
||||||
$axis.attr("data-value", axis);
|
$axis.attr('data-value', axis);
|
||||||
}
|
}
|
||||||
if ($axis.is("[data-axis-x=" + index + "]")) {
|
if ($axis.is('[data-axis-x=' + index + ']')) {
|
||||||
$axis.attr("data-value-x", axis);
|
$axis.attr('data-value-x', axis);
|
||||||
}
|
}
|
||||||
if ($axis.is("[data-axis-y=" + index + "]")) {
|
if ($axis.is('[data-axis-y=' + index + ']')) {
|
||||||
$axis.attr("data-value-y", axis);
|
$axis.attr('data-value-y', axis);
|
||||||
}
|
}
|
||||||
if ($axis.is("[data-axis-z=" + index + "]")) {
|
if ($axis.is('[data-axis-z=' + index + ']')) {
|
||||||
$axis.attr("data-value-z", axis);
|
$axis.attr('data-value-z', axis);
|
||||||
}
|
}
|
||||||
|
|
||||||
// hook the template defined axis update method
|
// hook the template defined axis update method
|
||||||
if ("function" === typeof this.updateAxis) {
|
if ('function' === typeof this.updateAxis) {
|
||||||
this.updateAxis($axis);
|
this.updateAxis($axis);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
changeGamepad(gamepadId) {
|
||||||
|
// get the index corresponding to the identifier of the gamepad
|
||||||
|
const index = this.gamepads.findIndex(g => g && g.id === gamepadId);
|
||||||
|
|
||||||
|
// set the selected gamepad
|
||||||
|
this.updateUrlParams({ gamepad: gamepadId !== 'auto' ? gamepadId : undefined });
|
||||||
|
index === -1 ? this.clear() : this.map(index);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes the skin
|
* Changes the skin
|
||||||
*
|
*
|
||||||
@ -807,7 +883,7 @@ class Gamepad {
|
|||||||
|
|
||||||
// set the selected skin
|
// set the selected skin
|
||||||
this.debug = skin === 'debug';
|
this.debug = skin === 'debug';
|
||||||
this.updateUrlParams({ type: skin !== "auto" ? skin : undefined });
|
this.updateUrlParams({ type: skin !== 'auto' ? skin : undefined });
|
||||||
this.map(this.index);
|
this.map(this.index);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -817,12 +893,12 @@ class Gamepad {
|
|||||||
* @param {any} style
|
* @param {any} style
|
||||||
*/
|
*/
|
||||||
changeBackgroundStyle(style) {
|
changeBackgroundStyle(style) {
|
||||||
if ("undefined" === typeof style) {
|
if ('undefined' === typeof style) {
|
||||||
this.backgroundStyleIndex++;
|
this.backgroundStyleIndex++;
|
||||||
if (this.backgroundStyleIndex > this.backgroundStyle.length - 1) {
|
if (this.backgroundStyleIndex > this.backgroundStyle.length - 1) {
|
||||||
this.backgroundStyleIndex = 0;
|
this.backgroundStyleIndex = 0;
|
||||||
}
|
}
|
||||||
} else if ("string" === typeof style) {
|
} else if ('string' === typeof style) {
|
||||||
this.backgroundStyleIndex = this.backgroundStyle.findIndex(
|
this.backgroundStyleIndex = this.backgroundStyle.findIndex(
|
||||||
(s) => s === style
|
(s) => s === style
|
||||||
);
|
);
|
||||||
@ -834,8 +910,8 @@ class Gamepad {
|
|||||||
|
|
||||||
this.$body.css({
|
this.$body.css({
|
||||||
background:
|
background:
|
||||||
this.backgroundStyleName === "checkered"
|
this.backgroundStyleName === 'checkered'
|
||||||
? "url(css/transparent-bg.png)"
|
? 'url(css/transparent-bg.png)'
|
||||||
: this.backgroundStyleName,
|
: this.backgroundStyleName,
|
||||||
color: this.textColors[this.backgroundStyleIndex],
|
color: this.textColors[this.backgroundStyleIndex],
|
||||||
});
|
});
|
||||||
@ -846,10 +922,10 @@ class Gamepad {
|
|||||||
|
|
||||||
// save statistics
|
// save statistics
|
||||||
if (!!window.ga) {
|
if (!!window.ga) {
|
||||||
ga("send", "event", {
|
ga('send', 'event', {
|
||||||
eventCategory: "Gamepad",
|
eventCategory: 'Gamepad',
|
||||||
eventAction: "change-background-color",
|
eventAction: 'change-background-color',
|
||||||
eventLabel: "Change Background Color",
|
eventLabel: 'Change Background Color',
|
||||||
eventValue: this.backgroundStyleName,
|
eventValue: this.backgroundStyleName,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -864,13 +940,13 @@ class Gamepad {
|
|||||||
// ensure that a gamepad is currently active
|
// ensure that a gamepad is currently active
|
||||||
if (this.index === null) return;
|
if (this.index === null) return;
|
||||||
|
|
||||||
if ("undefined" === typeof color) {
|
if ('undefined' === typeof color) {
|
||||||
// no color was specified, load the next one in list
|
// no color was specified, load the next one in list
|
||||||
this.colorIndex++;
|
this.colorIndex++;
|
||||||
if (this.colorIndex > this.identifier.colors.length - 1) {
|
if (this.colorIndex > this.identifier.colors.length - 1) {
|
||||||
this.colorIndex = 0;
|
this.colorIndex = 0;
|
||||||
}
|
}
|
||||||
} else if ("string" === typeof style) {
|
} else if ('string' === typeof style) {
|
||||||
this.colorIndex = this.identifier.colors.findIndex(
|
this.colorIndex = this.identifier.colors.findIndex(
|
||||||
(c) => c === color
|
(c) => c === color
|
||||||
);
|
);
|
||||||
@ -894,7 +970,7 @@ class Gamepad {
|
|||||||
: null;
|
: null;
|
||||||
|
|
||||||
// update the DOM with the color value
|
// update the DOM with the color value
|
||||||
this.$gamepad.attr("data-color", this.colorName);
|
this.$gamepad.attr('data-color', this.colorName);
|
||||||
|
|
||||||
// update current settings
|
// update current settings
|
||||||
this.updateUrlParams({ color: this.colorName });
|
this.updateUrlParams({ color: this.colorName });
|
||||||
@ -902,10 +978,10 @@ class Gamepad {
|
|||||||
|
|
||||||
// save statistics
|
// save statistics
|
||||||
if (!!window.ga) {
|
if (!!window.ga) {
|
||||||
ga("send", "event", {
|
ga('send', 'event', {
|
||||||
eventCategory: "Gamepad",
|
eventCategory: 'Gamepad',
|
||||||
eventAction: "change-gamepad-color",
|
eventAction: 'change-gamepad-color',
|
||||||
eventLabel: "Change Gamepad Color",
|
eventLabel: 'Change Gamepad Color',
|
||||||
eventValue: this.colorName,
|
eventValue: this.colorName,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -921,12 +997,12 @@ class Gamepad {
|
|||||||
if (this.index === null) return;
|
if (this.index === null) return;
|
||||||
|
|
||||||
// ensure we have some data to process
|
// ensure we have some data to process
|
||||||
if (typeof level === "undefined") return;
|
if (typeof level === 'undefined') return;
|
||||||
|
|
||||||
this.zoomMode = level === "auto" ? "auto" : "manual";
|
this.zoomMode = level === 'auto' ? 'auto' : 'manual';
|
||||||
|
|
||||||
if (this.zoomMode === "auto") {
|
if (this.zoomMode === 'auto') {
|
||||||
// "auto" means a "contained in window" zoom, with a max zoom of 1
|
// 'auto' means a 'contained in window' zoom, with a max zoom of 1
|
||||||
this.zoomLevel = Math.min(
|
this.zoomLevel = Math.min(
|
||||||
window.innerWidth / this.$gamepad.width(),
|
window.innerWidth / this.$gamepad.width(),
|
||||||
window.innerHeight / this.$gamepad.height(),
|
window.innerHeight / this.$gamepad.height(),
|
||||||
@ -935,11 +1011,11 @@ class Gamepad {
|
|||||||
} else if (level === 0) {
|
} else if (level === 0) {
|
||||||
// 0 means a zoom reset
|
// 0 means a zoom reset
|
||||||
this.zoomLevel = 1;
|
this.zoomLevel = 1;
|
||||||
} else if (level === "+" && this.zoomLevel < 2) {
|
} else if (level === '+' && this.zoomLevel < 2) {
|
||||||
// "+" means a zoom in if we still can
|
// '+' means a zoom in if we still can
|
||||||
this.zoomLevel += 0.1;
|
this.zoomLevel += 0.1;
|
||||||
} else if (level === "-" && this.zoomLevel > 0.1) {
|
} else if (level === '-' && this.zoomLevel > 0.1) {
|
||||||
// "-" means a zoom out if we still can
|
// '-' means a zoom out if we still can
|
||||||
this.zoomLevel -= 0.1;
|
this.zoomLevel -= 0.1;
|
||||||
} else if (!isNaN((level = parseFloat(level)))) {
|
} else if (!isNaN((level = parseFloat(level)))) {
|
||||||
// an integer value means a value-based zoom
|
// an integer value means a value-based zoom
|
||||||
@ -951,21 +1027,21 @@ class Gamepad {
|
|||||||
|
|
||||||
// update the DOM with the zoom value
|
// update the DOM with the zoom value
|
||||||
this.$gamepad.css(
|
this.$gamepad.css(
|
||||||
"transform",
|
'transform',
|
||||||
`translate(-50%, -50%) scale(${this.zoomLevel}, ${this.zoomLevel})`
|
`translate(-50%, -50%) scale(${this.zoomLevel}, ${this.zoomLevel})`
|
||||||
);
|
);
|
||||||
|
|
||||||
// update current settings
|
// update current settings
|
||||||
this.updateUrlParams({
|
this.updateUrlParams({
|
||||||
zoom: this.zoomMode === "auto" ? undefined : this.zoomLevel,
|
zoom: this.zoomMode === 'auto' ? undefined : this.zoomLevel,
|
||||||
});
|
});
|
||||||
|
|
||||||
// save statistics
|
// save statistics
|
||||||
if (!!window.ga) {
|
if (!!window.ga) {
|
||||||
ga("send", "event", {
|
ga('send', 'event', {
|
||||||
eventCategory: "Gamepad",
|
eventCategory: 'Gamepad',
|
||||||
eventAction: "change-zoom",
|
eventAction: 'change-zoom',
|
||||||
eventLabel: "Change Zoom",
|
eventLabel: 'Change Zoom',
|
||||||
eventValue: this.zoomLevel,
|
eventValue: this.zoomLevel,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -983,7 +1059,7 @@ class Gamepad {
|
|||||||
|
|
||||||
// compute next type
|
// compute next type
|
||||||
const types = Object.keys(this.identifiers).filter(
|
const types = Object.keys(this.identifiers).filter(
|
||||||
(i) => i !== "debug"
|
(i) => i !== 'debug'
|
||||||
);
|
);
|
||||||
let typeIndex = types.reduce((typeIndex, type, index) => {
|
let typeIndex = types.reduce((typeIndex, type, index) => {
|
||||||
return type === this.type ? index : typeIndex;
|
return type === this.type ? index : typeIndex;
|
||||||
@ -992,10 +1068,10 @@ class Gamepad {
|
|||||||
|
|
||||||
// save statistics
|
// save statistics
|
||||||
if (!!window.ga) {
|
if (!!window.ga) {
|
||||||
ga("send", "event", {
|
ga('send', 'event', {
|
||||||
eventCategory: "Gamepad",
|
eventCategory: 'Gamepad',
|
||||||
eventAction: "toggle-type",
|
eventAction: 'toggle-type',
|
||||||
eventLabel: "Toggle Type",
|
eventLabel: 'Toggle Type',
|
||||||
eventValue: this.type,
|
eventValue: this.type,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1019,10 +1095,10 @@ class Gamepad {
|
|||||||
|
|
||||||
// save statistics
|
// save statistics
|
||||||
if (!!window.ga) {
|
if (!!window.ga) {
|
||||||
ga("send", "event", {
|
ga('send', 'event', {
|
||||||
eventCategory: "Gamepad",
|
eventCategory: 'Gamepad',
|
||||||
eventAction: "toggle-debug",
|
eventAction: 'toggle-debug',
|
||||||
eventLabel: "Toggle Debug",
|
eventLabel: 'Toggle Debug',
|
||||||
eventValue: this.debug,
|
eventValue: this.debug,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1036,19 +1112,20 @@ class Gamepad {
|
|||||||
*/
|
*/
|
||||||
toggleHelp() {
|
toggleHelp() {
|
||||||
// refresh gamepad lsit with latest data
|
// refresh gamepad lsit with latest data
|
||||||
|
this.pollGamepads();
|
||||||
this.buildHelpGamepadList();
|
this.buildHelpGamepadList();
|
||||||
|
|
||||||
// display the help popout
|
// display the help popout
|
||||||
this.$helpPopout.toggleClass("active");
|
this.$helpPopout.toggleClass('active');
|
||||||
this.helpVisible = this.$helpPopout.is(".active");
|
this.helpVisible = this.$helpPopout.is('.active');
|
||||||
|
|
||||||
// save statistics
|
// save statistics
|
||||||
if (!!window.ga) {
|
if (!!window.ga) {
|
||||||
ga("send", "event", {
|
ga('send', 'event', {
|
||||||
eventCategory: "Gamepad",
|
eventCategory: 'Gamepad',
|
||||||
eventAction: "toggle-help",
|
eventAction: 'toggle-help',
|
||||||
eventLabel: "Toggle Help",
|
eventLabel: 'Toggle Help',
|
||||||
eventValue: this.$helpPopout.is("active"),
|
eventValue: this.$helpPopout.is('active'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1062,12 +1139,12 @@ class Gamepad {
|
|||||||
|
|
||||||
this.triggersMeter =
|
this.triggersMeter =
|
||||||
useMeter !== undefined ? useMeter : !this.triggersMeter;
|
useMeter !== undefined ? useMeter : !this.triggersMeter;
|
||||||
this.$gamepad[this.triggersMeter ? "addClass" : "removeClass"](
|
this.$gamepad[this.triggersMeter ? 'addClass' : 'removeClass'](
|
||||||
"triggers-meter"
|
'triggers-meter'
|
||||||
);
|
);
|
||||||
|
|
||||||
// update current settings
|
// update current settings
|
||||||
const triggers = this.triggersMeter ? "meter" : "opacity";
|
const triggers = this.triggersMeter ? 'meter' : 'opacity';
|
||||||
this.updateUrlParams({ triggers });
|
this.updateUrlParams({ triggers });
|
||||||
this.$triggersSelect.val(triggers);
|
this.$triggersSelect.val(triggers);
|
||||||
}
|
}
|
||||||
@ -1076,9 +1153,10 @@ class Gamepad {
|
|||||||
* Reads an URL search parameter
|
* Reads an URL search parameter
|
||||||
*
|
*
|
||||||
* @param {*} name
|
* @param {*} name
|
||||||
|
* @returns {string|boolean|null}
|
||||||
*/
|
*/
|
||||||
getUrlParam(name) {
|
getUrlParam(name) {
|
||||||
let matches = new RegExp("[?&]" + name + "(=([^&#]*))?").exec(
|
let matches = new RegExp('[?&]' + name + '(=([^&#]*))?').exec(
|
||||||
window.location.search
|
window.location.search
|
||||||
);
|
);
|
||||||
return matches ? decodeURIComponent(matches[2] || true) || true : null;
|
return matches ? decodeURIComponent(matches[2] || true) || true : null;
|
||||||
@ -1086,12 +1164,14 @@ class Gamepad {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Read url settings to produce a key/value object
|
* Read url settings to produce a key/value object
|
||||||
|
*
|
||||||
|
* @returns {object}
|
||||||
*/
|
*/
|
||||||
getUrlParams() {
|
getUrlParams() {
|
||||||
const settingsArr = window.location.search
|
const settingsArr = window.location.search
|
||||||
.replace("?", "")
|
.replace('?', '')
|
||||||
.split("&")
|
.split('&')
|
||||||
.map((param) => param.split("="));
|
.map((param) => param.split('='));
|
||||||
const settings = {};
|
const settings = {};
|
||||||
Object.keys(settingsArr).forEach((key) => {
|
Object.keys(settingsArr).forEach((key) => {
|
||||||
const [k, v] = settingsArr[key];
|
const [k, v] = settingsArr[key];
|
||||||
@ -1105,6 +1185,7 @@ class Gamepad {
|
|||||||
*/
|
*/
|
||||||
clearUrlParams() {
|
clearUrlParams() {
|
||||||
this.updateUrlParams({
|
this.updateUrlParams({
|
||||||
|
gamepad: undefined,
|
||||||
type: undefined,
|
type: undefined,
|
||||||
color: undefined,
|
color: undefined,
|
||||||
debug: undefined,
|
debug: undefined,
|
||||||
@ -1123,7 +1204,7 @@ class Gamepad {
|
|||||||
const query = Object.entries(params)
|
const query = Object.entries(params)
|
||||||
.filter(([, value]) => value !== undefined && value !== null)
|
.filter(([, value]) => value !== undefined && value !== null)
|
||||||
.map(([key, value]) => `${key}=${value}`)
|
.map(([key, value]) => `${key}=${value}`)
|
||||||
.join("&");
|
.join('&');
|
||||||
window.history.replaceState({}, document.title, `/?${query}`);
|
window.history.replaceState({}, document.title, `/?${query}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
5
samples.txt
Normal file
5
samples.txt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
Sample: T818
|
||||||
|
http://localhost:8081/?gamepad=Thrustmaster%20Thrustmaster%20Advance%20Racer%20(Vendor:%20044f%20Product:%20b696)&triggers=opacity&clutchIndex=6&clutchMin=1&clutchMax=-1&brakeIndex=1&brakeMin=1&brakeMax=-1&throttleIndex=5&throttleMin=1&throttleMax=-1&directionIndex=0&directionDegrees=900&type=telemetry
|
||||||
|
|
||||||
|
Sample: Xbox Controller
|
||||||
|
http://localhost:8081/?gamepad=Microsoft%20Controller%20(STANDARD%20GAMEPAD%20Vendor:%20045e%20Product:%200b00)&triggers=opacity&brakeType=button&brakeIndex=6&brakeMin=0&brakeMax=1&throttleType=button&throttleIndex=7&throttleMin=0&throttleMax=1&directionIndex=0&directionDegrees=180&type=telemetry
|
@ -17,7 +17,7 @@
|
|||||||
$id.html(activeGamepad.id);
|
$id.html(activeGamepad.id);
|
||||||
updateTimestamp();
|
updateTimestamp();
|
||||||
$index.html(activeGamepad.index);
|
$index.html(activeGamepad.index);
|
||||||
$mapping.html(activeGamepad.mapping);
|
$mapping.html(activeGamepad.mapping || 'N/A');
|
||||||
$rumble.html(
|
$rumble.html(
|
||||||
activeGamepad.vibrationActuator
|
activeGamepad.vibrationActuator
|
||||||
? activeGamepad.vibrationActuator.type
|
? activeGamepad.vibrationActuator.type
|
||||||
|
116
templates/telemetry/template.css
Normal file
116
templates/telemetry/template.css
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
#gamepad #telemetry {
|
||||||
|
background: var(--main-bg-color);
|
||||||
|
display: flex;
|
||||||
|
width: 650px;
|
||||||
|
height: 120px;
|
||||||
|
border-top-right-radius: 60px;
|
||||||
|
border-bottom-right-radius: 60px;
|
||||||
|
|
||||||
|
--black-color: black;
|
||||||
|
--white-color: white;
|
||||||
|
--grey-color: grey;
|
||||||
|
--main-bg-color: #444;
|
||||||
|
--main-component-color: grey;
|
||||||
|
--meter-idle-color: var(--grey-color);
|
||||||
|
--clutch-color: blue;
|
||||||
|
--brake-color: red;
|
||||||
|
--throttle-color: lime;
|
||||||
|
}
|
||||||
|
|
||||||
|
#gamepad #telemetry #chart {
|
||||||
|
flex: 1;
|
||||||
|
margin: 4px;
|
||||||
|
background-color: var(--main-component-color);
|
||||||
|
border: 1px solid var(--black-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#gamepad #telemetry #meters {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
width: 90px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#gamepad #telemetry #meters .meter {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
|
margin: 4px 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#gamepad #telemetry #meters .meter .value {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 10pt;
|
||||||
|
color: var(--white-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#gamepad #telemetry #meters .meter .bar {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
flex: 1;
|
||||||
|
background-color: var(--main-component-color);
|
||||||
|
border: 1px solid var(--black-color);
|
||||||
|
}
|
||||||
|
#gamepad #telemetry #meters .meter .bar .filler {
|
||||||
|
width: 100%;
|
||||||
|
height: 0%;
|
||||||
|
transition: height 100ms;
|
||||||
|
}
|
||||||
|
#gamepad #telemetry #meters #clutch.meter .bar .filler {
|
||||||
|
background-color: var(--clutch-color);
|
||||||
|
}
|
||||||
|
#gamepad #telemetry #meters #brake.meter .bar .filler {
|
||||||
|
background-color: var(--brake-color);
|
||||||
|
}
|
||||||
|
#gamepad #telemetry #meters #throttle.meter .bar .filler {
|
||||||
|
background-color: var(--throttle-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#gamepad #telemetry #direction {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 120px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#gamepad #telemetry #direction #wheel {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
width: 90%;
|
||||||
|
height: 90%;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: var(--black-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#gamepad #telemetry #direction #wheel #wheel--center {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
top: 10%;
|
||||||
|
left: 10%;
|
||||||
|
width: 80%;
|
||||||
|
height: 80%;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: var(--main-bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#gamepad #telemetry #direction #wheel #wheel--indicator {
|
||||||
|
display: block;
|
||||||
|
width: 7%;
|
||||||
|
height: 50%;
|
||||||
|
background-color: var(--white-color);
|
||||||
|
transform-origin: bottom;
|
||||||
|
transform: rotate(0deg);
|
||||||
|
transition: transform 100ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
#gamepad #telemetry #direction {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 120px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
32
templates/telemetry/template.html
Normal file
32
templates/telemetry/template.html
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<link rel="stylesheet" href="templates/telemetry/template.css">
|
||||||
|
<script src="https://www.gstatic.com/charts/loader.js"></script>
|
||||||
|
<script src="templates/telemetry/template.js"></script>
|
||||||
|
<div id="telemetry">
|
||||||
|
<div id="chart"></div>
|
||||||
|
<div id="meters">
|
||||||
|
<div id="clutch" class="meter">
|
||||||
|
<div class="value">0</div>
|
||||||
|
<div class="bar">
|
||||||
|
<div class="filler"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="brake" class="meter">
|
||||||
|
<div class="value">0</div>
|
||||||
|
<div class="bar">
|
||||||
|
<div class="filler"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="throttle" class="meter">
|
||||||
|
<div class="value">0</div>
|
||||||
|
<div class="bar">
|
||||||
|
<div class="filler"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="direction">
|
||||||
|
<div id="wheel">
|
||||||
|
<div id="wheel--indicator"></div>
|
||||||
|
<div id="wheel--center"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
143
templates/telemetry/template.js
Normal file
143
templates/telemetry/template.js
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
/**
|
||||||
|
* The Telemetry template class
|
||||||
|
*
|
||||||
|
* @class TelemetryTemplate
|
||||||
|
*/
|
||||||
|
class TelemetryTemplate {
|
||||||
|
/**
|
||||||
|
* Creates an instance of TelemetryTemplate.
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
this.AXES = ['clutch', 'brake', 'throttle', 'direction'];
|
||||||
|
|
||||||
|
this.frequency = 1000 / 60;
|
||||||
|
this.historyLength = 5000;
|
||||||
|
this.length = this.historyLength / this.frequency;
|
||||||
|
this.index = 0;
|
||||||
|
|
||||||
|
this.$clutchValue = document.querySelector('#clutch .value');
|
||||||
|
this.$clutchBar = document.querySelector('#clutch .bar .filler');
|
||||||
|
this.$brakeValue = document.querySelector('#brake .value');
|
||||||
|
this.$brakeBar = document.querySelector('#brake .bar .filler');
|
||||||
|
this.$throttleValue = document.querySelector('#throttle .value');
|
||||||
|
this.$throttleBar = document.querySelector('#throttle .bar .filler');
|
||||||
|
this.$directionIndicator = document.querySelector('#direction #wheel--indicator');
|
||||||
|
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
toPercentage(value, min, max) {
|
||||||
|
// debugger;
|
||||||
|
return value !== undefined
|
||||||
|
? Math.round((value - min) * (100 / (max - min)))
|
||||||
|
: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
toDegrees(value, min, max) {
|
||||||
|
const percentage = this.toPercentage(value, min, max);
|
||||||
|
return (this.directionDegrees) * (percentage - 50) / 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
toAxisValue(gamepad, axis) {
|
||||||
|
const { [`${axis}Type`]: type, [`${axis}Index`]: index, [`${axis}Min`]: min, [`${axis}Max`]: max } = this[axis];
|
||||||
|
const value = type === 'button' ? gamepad.buttons[index].value : gamepad.axes[index];
|
||||||
|
return axis === 'direction' ? this.toDegrees(value, min, max) : this.toPercentage(value, min, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadAxes() {
|
||||||
|
this.AXES.forEach((axis) => {
|
||||||
|
this[axis] = {
|
||||||
|
[`${axis}Type`]: (window.gamepad.getUrlParam(`${axis}Type`) || 'axis').replace('axis', 'axe'),
|
||||||
|
[`${axis}Index`]: window.gamepad.getUrlParam(`${axis}Index`),
|
||||||
|
[`${axis}Min`]: window.gamepad.getUrlParam(`${axis}Min`) || -1,
|
||||||
|
[`${axis}Max`]: window.gamepad.getUrlParam(`${axis}Max`) || 1,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.directionDegrees = window.gamepad.getUrlParam('directionDegrees') || 360;
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this.loadAxes();
|
||||||
|
|
||||||
|
if (!window.google) {
|
||||||
|
window.setTimeout(this.init.bind(this), 100);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
google.charts.load('current', {
|
||||||
|
packages: ['corechart', 'line'],
|
||||||
|
});
|
||||||
|
google.charts.setOnLoadCallback(this.drawChart.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
drawChart() {
|
||||||
|
this.initialData = [['Time', 'Clutch', 'Brake', 'Throttle']];
|
||||||
|
for (this.index = 0; this.index < this.length; this.index++) {
|
||||||
|
this.initialData.push([this.index, 0, 0, 0]);
|
||||||
|
}
|
||||||
|
this.data = google.visualization.arrayToDataTable(this.initialData);
|
||||||
|
this.options = {
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
chartArea: {
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
},
|
||||||
|
hAxis: {
|
||||||
|
textPosition: 'none',
|
||||||
|
gridlines: {
|
||||||
|
color: 'transparent',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
vAxis: {
|
||||||
|
textPosition: 'none',
|
||||||
|
gridlines: {
|
||||||
|
color: 'transparent',
|
||||||
|
},
|
||||||
|
minValue: 0,
|
||||||
|
maxValue: 100,
|
||||||
|
},
|
||||||
|
colors: ['blue', 'red', 'lime'],
|
||||||
|
legend: 'none'
|
||||||
|
};
|
||||||
|
this.chart = new google.visualization.LineChart(document.querySelector('#chart'));
|
||||||
|
this.chart.draw(this.data, this.options);
|
||||||
|
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
const gamepad = window.gamepad.getActive();
|
||||||
|
if (!gamepad) return;
|
||||||
|
|
||||||
|
const [clutch, brake, throttle, direction] = this.AXES.map((axis) => this.toAxisValue(gamepad, axis));
|
||||||
|
|
||||||
|
this.updateChart(clutch, brake, throttle);
|
||||||
|
this.updateMeters(clutch, brake, throttle, direction);
|
||||||
|
window.setTimeout(this.update.bind(this), this.frequency);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateChart(clutch, brake, throttle) {
|
||||||
|
if (this.data.getNumberOfRows() > this.length) {
|
||||||
|
this.data.removeRows(0, this.data.getNumberOfRows() - this.length);
|
||||||
|
}
|
||||||
|
this.data.addRow([this.index, clutch, brake, throttle]);
|
||||||
|
this.chart.draw(this.data, this.options);
|
||||||
|
this.index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateMeters(clutch, brake, throttle, direction) {
|
||||||
|
Object.entries({ clutch, brake, throttle, direction }).forEach(([axis, value]) => {
|
||||||
|
if (axis === 'direction') {
|
||||||
|
this.$directionIndicator.style.transform = `rotate(${value}deg)`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this[`$${axis}Value`].innerHTML = value;
|
||||||
|
this[`$${axis}Bar`].style.height = `${value}%`;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
window.telemetryTemplate = new TelemetryTemplate();
|
Loading…
x
Reference in New Issue
Block a user