1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00

Remove focus-prevent-scroll, migrate gamepadtokey and chromecastHelper to ES6

This commit is contained in:
MrTimscampi 2020-08-06 23:56:29 +02:00
parent e2aff66203
commit 58361ce70c
8 changed files with 540 additions and 569 deletions

View file

@ -62,6 +62,7 @@
"epubjs": "^0.3.85", "epubjs": "^0.3.85",
"fast-text-encoding": "^1.0.3", "fast-text-encoding": "^1.0.3",
"flv.js": "^1.5.0", "flv.js": "^1.5.0",
"focus-options-polyfill": "^1.5.0",
"headroom.js": "^0.11.0", "headroom.js": "^0.11.0",
"hls.js": "^0.14.7", "hls.js": "^0.14.7",
"howler": "^2.2.0", "howler": "^2.2.0",
@ -278,6 +279,7 @@
"src/plugins/backdropScreensaver/plugin.js", "src/plugins/backdropScreensaver/plugin.js",
"src/plugins/bookPlayer/plugin.js", "src/plugins/bookPlayer/plugin.js",
"src/plugins/bookPlayer/tableOfContents.js", "src/plugins/bookPlayer/tableOfContents.js",
"src/plugins/chromecastPlayer/chromecastHelper.js",
"src/plugins/photoPlayer/plugin.js", "src/plugins/photoPlayer/plugin.js",
"src/plugins/youtubePlayer/plugin.js", "src/plugins/youtubePlayer/plugin.js",
"src/scripts/alphanumericshortcuts.js", "src/scripts/alphanumericshortcuts.js",

View file

@ -10,6 +10,11 @@ _define('fetch', function() {
return fetch; return fetch;
}); });
var focusPreventScroll = require('focus-options-polyfill');
_define('focus-options-polyfill', function() {
return focusPreventScroll;
});
// Blurhash // Blurhash
var blurhash = require('blurhash'); var blurhash = require('blurhash');
_define('blurhash', function() { _define('blurhash', function() {

View file

@ -1,43 +0,0 @@
// TODO: Move to external library (https://github.com/calvellido/focus-options-polyfill)
// Polyfill to add support for preventScroll by focus function
if (HTMLElement.prototype.nativeFocus === undefined) {
(function () {
var supportsPreventScrollOption = false;
try {
var focusElem = document.createElement('div');
focusElem.addEventListener('focus', function(event) {
event.preventDefault();
event.stopPropagation();
}, true);
var opts = Object.defineProperty({}, 'preventScroll', {
// eslint-disable-next-line getter-return
get: function () {
supportsPreventScrollOption = true;
}
});
focusElem.focus(opts);
} catch (e) {
console.error('error checking preventScroll support');
}
if (!supportsPreventScrollOption) {
HTMLElement.prototype.nativeFocus = HTMLElement.prototype.focus;
HTMLElement.prototype.focus = function(options) {
var scrollX = window.scrollX;
var scrollY = window.scrollY;
this.nativeFocus();
// Restore window scroll if preventScroll
if (options && options.preventScroll) {
window.scroll(scrollX, scrollY);
}
};
}
})();
}

View file

@ -1,229 +1,229 @@
define(['events'], function (events) { import events from 'events';
'use strict';
// LinkParser // LinkParser
//
// https://github.com/ravisorg/LinkParser
//
// Locate and extract almost any URL within a string. Handles protocol-less domains, IPv4 and
// IPv6, unrecognised TLDs, and more.
//
// This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
// http://creativecommons.org/licenses/by-sa/4.0/
(function () {
// Original URL regex from the Android android.text.util.Linkify function, found here:
// http://stackoverflow.com/a/19696443
// //
// https://github.com/ravisorg/LinkParser // However there were problems with it, most probably related to the fact it was
// written in 2007, and it's been highly modified.
// //
// Locate and extract almost any URL within a string. Handles protocol-less domains, IPv4 and // 1) I didn't like the fact that it was tied to specific TLDs, since new ones
// IPv6, unrecognised TLDs, and more. // are being added all the time it wouldn't be reasonable to expect developer to
// be continually updating their regular expressions.
// //
// This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. // 2) It didn't allow unicode characters in the domains which are now allowed in
// http://creativecommons.org/licenses/by-sa/4.0/ // many languages, (including some IDN TLDs). Again these are constantly being
(function () { // added to and it doesn't seem reasonable to hard-code them. Note this ended up
// Original URL regex from the Android android.text.util.Linkify function, found here: // not being possible in standard JS due to the way it handles multibyte strings.
// http://stackoverflow.com/a/19696443 // It is possible using XRegExp, however a big performance hit results. Disabled
// // for now.
// However there were problems with it, most probably related to the fact it was //
// written in 2007, and it's been highly modified. // 3) It didn't allow for IPv6 hostnames
// // IPv6 regex from http://stackoverflow.com/a/17871737
// 1) I didn't like the fact that it was tied to specific TLDs, since new ones //
// are being added all the time it wouldn't be reasonable to expect developer to // 4) It was very poorly commented
// be continually updating their regular expressions. //
// // 5) It wasn't as smart as it could have been about what should be part of a
// 2) It didn't allow unicode characters in the domains which are now allowed in // URL and what should be part of human language.
// many languages, (including some IDN TLDs). Again these are constantly being
// added to and it doesn't seem reasonable to hard-code them. Note this ended up
// not being possible in standard JS due to the way it handles multibyte strings.
// It is possible using XRegExp, however a big performance hit results. Disabled
// for now.
//
// 3) It didn't allow for IPv6 hostnames
// IPv6 regex from http://stackoverflow.com/a/17871737
//
// 4) It was very poorly commented
//
// 5) It wasn't as smart as it could have been about what should be part of a
// URL and what should be part of human language.
var protocols = '(?:(?:http|https|rtsp|ftp):\\/\\/)'; const protocols = '(?:(?:http|https|rtsp|ftp):\\/\\/)';
var credentials = "(?:(?:[a-z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-f0-9]{2})){1,64}" // username (1-64 normal or url escaped characters) const credentials = "(?:(?:[a-z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-f0-9]{2})){1,64}" // username (1-64 normal or url escaped characters)
+ "(?:\\:(?:[a-z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-f0-9]{2})){1,25})?" // followed by optional password (: + 1-25 normal or url escaped characters) + "(?:\\:(?:[a-z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-f0-9]{2})){1,25})?" // followed by optional password (: + 1-25 normal or url escaped characters)
+ '\\@)'; + '\\@)';
// IPv6 Regex http://forums.intermapper.com/viewtopic.php?t=452 // IPv6 Regex http://forums.intermapper.com/viewtopic.php?t=452
// by Dartware, LLC is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License // by Dartware, LLC is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License
// http://intermapper.com/ // http://intermapper.com/
var ipv6 = '(' const ipv6 = '('
+ '(([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))' + '(([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))'
+ '|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))' + '|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))'
+ '|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))' + '|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))'
+ '|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))' + '|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))'
+ '|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))' + '|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))'
+ '|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))' + '|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))'
+ '|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))' + '|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))'
+ '|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))' + '|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))'
+ ')(%.+)?'; + ')(%.+)?';
var ipv4 = '(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.' const ipv4 = '(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.'
+ '(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.' + '(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.'
+ '(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.' + '(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.'
+ '(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[0-9])'; + '(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[0-9])';
// This would have been a lot cleaner if JS RegExp supported conditionals... // This would have been a lot cleaner if JS RegExp supported conditionals...
var linkRegExpString = const linkRegExpString =
// begin match for protocol / username / password / host // begin match for protocol / username / password / host
'(?:' '(?:'
// ============================ // ============================
// If we have a recognized protocol at the beginning of the URL, we're // If we have a recognized protocol at the beginning of the URL, we're
// more relaxed about what we accept, because we assume the user wants // more relaxed about what we accept, because we assume the user wants
// this to be a URL, and we're not accidentally matching human language // this to be a URL, and we're not accidentally matching human language
+ protocols + '?' + protocols + '?'
// optional username:password@ // optional username:password@
+ credentials + '?' + credentials + '?'
// IP address (both v4 and v6) // IP address (both v4 and v6)
+ '(?:' + '(?:'
// IPv6 // IPv6
+ ipv6 + ipv6
// IPv4 // IPv4
+ '|' + ipv4 + '|' + ipv4
+ ')' + ')'
// end match for protocol / username / password / host // end match for protocol / username / password / host
+ ')' + ')'
// optional port number // optional port number
+ '(?:\\:\\d{1,5})?' + '(?:\\:\\d{1,5})?'
// plus optional path and query params (no unicode allowed here?) // plus optional path and query params (no unicode allowed here?)
+ '(?:' + '(?:'
+ '\\/(?:' + '\\/(?:'
// some characters we'll accept because it's unlikely human language // some characters we'll accept because it's unlikely human language
// would use them after a URL unless they were part of the url // would use them after a URL unless they were part of the url
+ '(?:[a-z0-9\\/\\@\\&\\#\\~\\*\\_\\-\\+])' + '(?:[a-z0-9\\/\\@\\&\\#\\~\\*\\_\\-\\+])'
+ '|(?:\\%[a-f0-9]{2})' + '|(?:\\%[a-f0-9]{2})'
// some characters are much more likely to be used AFTER a url and // some characters are much more likely to be used AFTER a url and
// were not intended to be included in the url itself. Mostly end // were not intended to be included in the url itself. Mostly end
// of sentence type things. It's also likely that the URL would // of sentence type things. It's also likely that the URL would
// still work if any of these characters were missing from the end // still work if any of these characters were missing from the end
// because we parsed it incorrectly. For these characters to be accepted // because we parsed it incorrectly. For these characters to be accepted
// they must be followed by another character that we're reasonably // they must be followed by another character that we're reasonably
// sure is part of the url // sure is part of the url
+ "|(?:[\\;\\?\\:\\.\\!\\'\\(\\)\\,\\=]+(?=(?:[a-z0-9\\/\\@\\&\\#\\~\\*\\_\\-\\+])|(?:\\%[a-f0-9]{2})))" + "|(?:[\\;\\?\\:\\.\\!\\'\\(\\)\\,\\=]+(?=(?:[a-z0-9\\/\\@\\&\\#\\~\\*\\_\\-\\+])|(?:\\%[a-f0-9]{2})))"
+ ')*' + ')*'
+ '|\\b|\$' + '|\\b|\$'
+ ')'; + ')';
// regex = XRegExp(regex,'gi'); // regex = XRegExp(regex,'gi');
var linkRegExp = RegExp(linkRegExpString, 'gi'); const linkRegExp = RegExp(linkRegExpString, 'gi');
var protocolRegExp = RegExp('^' + protocols, 'i'); const protocolRegExp = RegExp('^' + protocols, 'i');
// if url doesn't begin with a known protocol, add http by default // if url doesn't begin with a known protocol, add http by default
function ensureProtocol(url) { function ensureProtocol(url) {
if (!url.match(protocolRegExp)) { if (!url.match(protocolRegExp)) {
url = 'http://' + url; url = 'http://' + url;
}
return url;
} }
return url;
}
// look for links in the text // look for links in the text
var LinkParser = { const LinkParser = {
parse: function (text) { parse: function (text) {
var links = []; const links = [];
var match; let match;
// eslint-disable-next-line no-cond-assign // eslint-disable-next-line no-cond-assign
while (match = linkRegExp.exec(text)) { while (match = linkRegExp.exec(text)) {
console.debug(match); console.debug(match);
var txt = match[0]; const txt = match[0];
var pos = match.index; const pos = match.index;
var len = txt.length; const len = txt.length;
var url = ensureProtocol(text); const url = ensureProtocol(text);
links.push({ 'pos': pos, 'text': txt, 'len': len, 'url': url }); links.push({ 'pos': pos, 'text': txt, 'len': len, 'url': url });
}
return links;
} }
}; return links;
window.LinkParser = LinkParser;
})();
var cache = {};
function isValidIpAddress(address) {
var links = LinkParser.parse(address);
return links.length == 1;
}
function isLocalIpAddress(address) {
address = address.toLowerCase();
if (address.indexOf('127.0.0.1') !== -1) {
return true;
}
if (address.indexOf('localhost') !== -1) {
return true;
} }
return false;
}
function getServerAddress(apiClient) {
var serverAddress = apiClient.serverAddress();
if (isValidIpAddress(serverAddress) && !isLocalIpAddress(serverAddress)) {
return Promise.resolve(serverAddress);
}
var cachedValue = getCachedValue(serverAddress);
if (cachedValue) {
return Promise.resolve(cachedValue);
}
return apiClient.getEndpointInfo().then(function (endpoint) {
if (endpoint.IsInNetwork) {
return apiClient.getPublicSystemInfo().then(function (info) {
var localAddress = info.LocalAddress;
if (!localAddress) {
console.debug('No valid local address returned, defaulting to external one');
localAddress = serverAddress;
}
addToCache(serverAddress, localAddress);
return localAddress;
});
} else {
addToCache(serverAddress, serverAddress);
return serverAddress;
}
});
}
function clearCache() {
cache = {};
}
function addToCache(key, value) {
cache[key] = {
value: value,
time: new Date().getTime()
};
}
function getCachedValue(key) {
var obj = cache[key];
if (obj && (new Date().getTime() - obj.time) < 180000) {
return obj.value;
}
return null;
}
events.on(ConnectionManager, 'localusersignedin', clearCache);
events.on(ConnectionManager, 'localusersignedout', clearCache);
return {
getServerAddress: getServerAddress
}; };
});
window.LinkParser = LinkParser;
})();
let cache = {};
// TODO: Replace with isIP (https://www.npmjs.com/package/is-ip)
function isValidIpAddress(address) {
const links = LinkParser.parse(address);
return links.length == 1;
}
// TODO: Add IPv6 support. Potentially replace with isLocalhost (https://www.npmjs.com/package/is-localhost-ip)
function isLocalIpAddress(address) {
address = address.toLowerCase();
if (address.includes('127.0.0.1')) {
return true;
}
if (address.includes('localhost')) {
return true;
}
return false;
}
function getServerAddress(apiClient) {
const serverAddress = apiClient.serverAddress();
if (isValidIpAddress(serverAddress) && !isLocalIpAddress(serverAddress)) {
return Promise.resolve(serverAddress);
}
const cachedValue = getCachedValue(serverAddress);
if (cachedValue) {
return Promise.resolve(cachedValue);
}
return apiClient.getEndpointInfo().then(function (endpoint) {
if (endpoint.IsInNetwork) {
return apiClient.getPublicSystemInfo().then(function (info) {
let localAddress = info.LocalAddress;
if (!localAddress) {
console.debug('No valid local address returned, defaulting to external one');
localAddress = serverAddress;
}
addToCache(serverAddress, localAddress);
return localAddress;
});
} else {
addToCache(serverAddress, serverAddress);
return serverAddress;
}
});
}
function clearCache() {
cache = {};
}
function addToCache(key, value) {
cache[key] = {
value: value,
time: new Date().getTime()
};
}
function getCachedValue(key) {
const obj = cache[key];
if (obj && (new Date().getTime() - obj.time) < 180000) {
return obj.value;
}
return null;
}
events.on(ConnectionManager, 'localusersignedin', clearCache);
events.on(ConnectionManager, 'localusersignedout', clearCache);
export default {
getServerAddress: getServerAddress
};

View file

@ -366,6 +366,8 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', '
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
require(['./chromecastHelper'], function (chromecastHelper) { require(['./chromecastHelper'], function (chromecastHelper) {
chromecastHelper = chromecastHelper.default || chromecastHelper;
chromecastHelper.getServerAddress(apiClient).then(function (serverAddress) { chromecastHelper.getServerAddress(apiClient).then(function (serverAddress) {
message.serverAddress = serverAddress; message.serverAddress = serverAddress;
player.sendMessageInternal(message).then(resolve, reject); player.sendMessageInternal(message).then(resolve, reject);

View file

@ -19,384 +19,383 @@
// # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// # THE SOFTWARE. // # THE SOFTWARE.
require(['apphost'], function (appHost) {
'use strict';
var _GAMEPAD_A_BUTTON_INDEX = 0; import appHost from 'apphost';
var _GAMEPAD_B_BUTTON_INDEX = 1;
var _GAMEPAD_DPAD_UP_BUTTON_INDEX = 12;
var _GAMEPAD_DPAD_DOWN_BUTTON_INDEX = 13;
var _GAMEPAD_DPAD_LEFT_BUTTON_INDEX = 14;
var _GAMEPAD_DPAD_RIGHT_BUTTON_INDEX = 15;
var _GAMEPAD_A_KEY = 'GamepadA';
var _GAMEPAD_B_KEY = 'GamepadB';
var _GAMEPAD_DPAD_UP_KEY = 'GamepadDPadUp';
var _GAMEPAD_DPAD_DOWN_KEY = 'GamepadDPadDown';
var _GAMEPAD_DPAD_LEFT_KEY = 'GamepadDPadLeft';
var _GAMEPAD_DPAD_RIGHT_KEY = 'GamepadDPadRight';
var _GAMEPAD_LEFT_THUMBSTICK_UP_KEY = 'GamepadLeftThumbStickUp';
var _GAMEPAD_LEFT_THUMBSTICK_DOWN_KEY = 'GamepadLeftThumbStickDown';
var _GAMEPAD_LEFT_THUMBSTICK_LEFT_KEY = 'GamepadLeftThumbStickLeft';
var _GAMEPAD_LEFT_THUMBSTICK_RIGHT_KEY = 'GamepadLeftThumbStickRight';
var _GAMEPAD_A_KEYCODE = 0;
var _GAMEPAD_B_KEYCODE = 27;
var _GAMEPAD_DPAD_UP_KEYCODE = 38;
var _GAMEPAD_DPAD_DOWN_KEYCODE = 40;
var _GAMEPAD_DPAD_LEFT_KEYCODE = 37;
var _GAMEPAD_DPAD_RIGHT_KEYCODE = 39;
var _GAMEPAD_LEFT_THUMBSTICK_UP_KEYCODE = 38;
var _GAMEPAD_LEFT_THUMBSTICK_DOWN_KEYCODE = 40;
var _GAMEPAD_LEFT_THUMBSTICK_LEFT_KEYCODE = 37;
var _GAMEPAD_LEFT_THUMBSTICK_RIGHT_KEYCODE = 39;
var _THUMB_STICK_THRESHOLD = 0.75;
var _leftThumbstickUpPressed = false; var _GAMEPAD_A_BUTTON_INDEX = 0;
var _leftThumbstickDownPressed = false; var _GAMEPAD_B_BUTTON_INDEX = 1;
var _leftThumbstickLeftPressed = false; var _GAMEPAD_DPAD_UP_BUTTON_INDEX = 12;
var _leftThumbstickRightPressed = false; var _GAMEPAD_DPAD_DOWN_BUTTON_INDEX = 13;
var _dPadUpPressed = false; var _GAMEPAD_DPAD_LEFT_BUTTON_INDEX = 14;
var _dPadDownPressed = false; var _GAMEPAD_DPAD_RIGHT_BUTTON_INDEX = 15;
var _dPadLeftPressed = false; var _GAMEPAD_A_KEY = 'GamepadA';
var _dPadRightPressed = false; var _GAMEPAD_B_KEY = 'GamepadB';
var _gamepadAPressed = false; var _GAMEPAD_DPAD_UP_KEY = 'GamepadDPadUp';
var _gamepadBPressed = false; var _GAMEPAD_DPAD_DOWN_KEY = 'GamepadDPadDown';
var _GAMEPAD_DPAD_LEFT_KEY = 'GamepadDPadLeft';
var _GAMEPAD_DPAD_RIGHT_KEY = 'GamepadDPadRight';
var _GAMEPAD_LEFT_THUMBSTICK_UP_KEY = 'GamepadLeftThumbStickUp';
var _GAMEPAD_LEFT_THUMBSTICK_DOWN_KEY = 'GamepadLeftThumbStickDown';
var _GAMEPAD_LEFT_THUMBSTICK_LEFT_KEY = 'GamepadLeftThumbStickLeft';
var _GAMEPAD_LEFT_THUMBSTICK_RIGHT_KEY = 'GamepadLeftThumbStickRight';
var _GAMEPAD_A_KEYCODE = 0;
var _GAMEPAD_B_KEYCODE = 27;
var _GAMEPAD_DPAD_UP_KEYCODE = 38;
var _GAMEPAD_DPAD_DOWN_KEYCODE = 40;
var _GAMEPAD_DPAD_LEFT_KEYCODE = 37;
var _GAMEPAD_DPAD_RIGHT_KEYCODE = 39;
var _GAMEPAD_LEFT_THUMBSTICK_UP_KEYCODE = 38;
var _GAMEPAD_LEFT_THUMBSTICK_DOWN_KEYCODE = 40;
var _GAMEPAD_LEFT_THUMBSTICK_LEFT_KEYCODE = 37;
var _GAMEPAD_LEFT_THUMBSTICK_RIGHT_KEYCODE = 39;
var _THUMB_STICK_THRESHOLD = 0.75;
// The set of buttons on the gamepad we listen for. var _leftThumbstickUpPressed = false;
var ProcessedButtons = [ var _leftThumbstickDownPressed = false;
_GAMEPAD_DPAD_UP_BUTTON_INDEX, var _leftThumbstickLeftPressed = false;
_GAMEPAD_DPAD_DOWN_BUTTON_INDEX, var _leftThumbstickRightPressed = false;
_GAMEPAD_DPAD_LEFT_BUTTON_INDEX, var _dPadUpPressed = false;
_GAMEPAD_DPAD_RIGHT_BUTTON_INDEX, var _dPadDownPressed = false;
_GAMEPAD_A_BUTTON_INDEX, var _dPadLeftPressed = false;
_GAMEPAD_B_BUTTON_INDEX var _dPadRightPressed = false;
]; var _gamepadAPressed = false;
var _gamepadBPressed = false;
var _ButtonPressedState = {}; // The set of buttons on the gamepad we listen for.
_ButtonPressedState.getgamepadA = function () { var ProcessedButtons = [
return _gamepadAPressed; _GAMEPAD_DPAD_UP_BUTTON_INDEX,
}; _GAMEPAD_DPAD_DOWN_BUTTON_INDEX,
_GAMEPAD_DPAD_LEFT_BUTTON_INDEX,
_GAMEPAD_DPAD_RIGHT_BUTTON_INDEX,
_GAMEPAD_A_BUTTON_INDEX,
_GAMEPAD_B_BUTTON_INDEX
];
_ButtonPressedState.setgamepadA = function (newPressedState) { var _ButtonPressedState = {};
raiseKeyEvent(_gamepadAPressed, newPressedState, _GAMEPAD_A_KEY, _GAMEPAD_A_KEYCODE, false, true); _ButtonPressedState.getgamepadA = function () {
_gamepadAPressed = newPressedState; return _gamepadAPressed;
}; };
_ButtonPressedState.getgamepadB = function () { _ButtonPressedState.setgamepadA = function (newPressedState) {
return _gamepadBPressed; raiseKeyEvent(_gamepadAPressed, newPressedState, _GAMEPAD_A_KEY, _GAMEPAD_A_KEYCODE, false, true);
}; _gamepadAPressed = newPressedState;
};
_ButtonPressedState.setgamepadB = function (newPressedState) { _ButtonPressedState.getgamepadB = function () {
raiseKeyEvent(_gamepadBPressed, newPressedState, _GAMEPAD_B_KEY, _GAMEPAD_B_KEYCODE); return _gamepadBPressed;
_gamepadBPressed = newPressedState; };
};
_ButtonPressedState.getleftThumbstickUp = function () { _ButtonPressedState.setgamepadB = function (newPressedState) {
return _leftThumbstickUpPressed; raiseKeyEvent(_gamepadBPressed, newPressedState, _GAMEPAD_B_KEY, _GAMEPAD_B_KEYCODE);
}; _gamepadBPressed = newPressedState;
};
_ButtonPressedState.setleftThumbstickUp = function (newPressedState) { _ButtonPressedState.getleftThumbstickUp = function () {
raiseKeyEvent(_leftThumbstickUpPressed, newPressedState, _GAMEPAD_LEFT_THUMBSTICK_UP_KEY, _GAMEPAD_LEFT_THUMBSTICK_UP_KEYCODE, true); return _leftThumbstickUpPressed;
_leftThumbstickUpPressed = newPressedState; };
};
_ButtonPressedState.getleftThumbstickDown = function () { _ButtonPressedState.setleftThumbstickUp = function (newPressedState) {
return _leftThumbstickDownPressed; raiseKeyEvent(_leftThumbstickUpPressed, newPressedState, _GAMEPAD_LEFT_THUMBSTICK_UP_KEY, _GAMEPAD_LEFT_THUMBSTICK_UP_KEYCODE, true);
}; _leftThumbstickUpPressed = newPressedState;
};
_ButtonPressedState.setleftThumbstickDown = function (newPressedState) { _ButtonPressedState.getleftThumbstickDown = function () {
raiseKeyEvent(_leftThumbstickDownPressed, newPressedState, _GAMEPAD_LEFT_THUMBSTICK_DOWN_KEY, _GAMEPAD_LEFT_THUMBSTICK_DOWN_KEYCODE, true); return _leftThumbstickDownPressed;
_leftThumbstickDownPressed = newPressedState; };
};
_ButtonPressedState.getleftThumbstickLeft = function () { _ButtonPressedState.setleftThumbstickDown = function (newPressedState) {
return _leftThumbstickLeftPressed; raiseKeyEvent(_leftThumbstickDownPressed, newPressedState, _GAMEPAD_LEFT_THUMBSTICK_DOWN_KEY, _GAMEPAD_LEFT_THUMBSTICK_DOWN_KEYCODE, true);
}; _leftThumbstickDownPressed = newPressedState;
};
_ButtonPressedState.setleftThumbstickLeft = function (newPressedState) { _ButtonPressedState.getleftThumbstickLeft = function () {
raiseKeyEvent(_leftThumbstickLeftPressed, newPressedState, _GAMEPAD_LEFT_THUMBSTICK_LEFT_KEY, _GAMEPAD_LEFT_THUMBSTICK_LEFT_KEYCODE, true); return _leftThumbstickLeftPressed;
_leftThumbstickLeftPressed = newPressedState; };
};
_ButtonPressedState.getleftThumbstickRight = function () { _ButtonPressedState.setleftThumbstickLeft = function (newPressedState) {
return _leftThumbstickRightPressed; raiseKeyEvent(_leftThumbstickLeftPressed, newPressedState, _GAMEPAD_LEFT_THUMBSTICK_LEFT_KEY, _GAMEPAD_LEFT_THUMBSTICK_LEFT_KEYCODE, true);
}; _leftThumbstickLeftPressed = newPressedState;
};
_ButtonPressedState.setleftThumbstickRight = function (newPressedState) { _ButtonPressedState.getleftThumbstickRight = function () {
raiseKeyEvent(_leftThumbstickRightPressed, newPressedState, _GAMEPAD_LEFT_THUMBSTICK_RIGHT_KEY, _GAMEPAD_LEFT_THUMBSTICK_RIGHT_KEYCODE, true); return _leftThumbstickRightPressed;
_leftThumbstickRightPressed = newPressedState; };
};
_ButtonPressedState.getdPadUp = function () { _ButtonPressedState.setleftThumbstickRight = function (newPressedState) {
return _dPadUpPressed; raiseKeyEvent(_leftThumbstickRightPressed, newPressedState, _GAMEPAD_LEFT_THUMBSTICK_RIGHT_KEY, _GAMEPAD_LEFT_THUMBSTICK_RIGHT_KEYCODE, true);
}; _leftThumbstickRightPressed = newPressedState;
};
_ButtonPressedState.setdPadUp = function (newPressedState) { _ButtonPressedState.getdPadUp = function () {
raiseKeyEvent(_dPadUpPressed, newPressedState, _GAMEPAD_DPAD_UP_KEY, _GAMEPAD_DPAD_UP_KEYCODE, true); return _dPadUpPressed;
_dPadUpPressed = newPressedState; };
};
_ButtonPressedState.getdPadDown = function () { _ButtonPressedState.setdPadUp = function (newPressedState) {
return _dPadDownPressed; raiseKeyEvent(_dPadUpPressed, newPressedState, _GAMEPAD_DPAD_UP_KEY, _GAMEPAD_DPAD_UP_KEYCODE, true);
}; _dPadUpPressed = newPressedState;
};
_ButtonPressedState.setdPadDown = function (newPressedState) { _ButtonPressedState.getdPadDown = function () {
raiseKeyEvent(_dPadDownPressed, newPressedState, _GAMEPAD_DPAD_DOWN_KEY, _GAMEPAD_DPAD_DOWN_KEYCODE, true); return _dPadDownPressed;
_dPadDownPressed = newPressedState; };
};
_ButtonPressedState.getdPadLeft = function () { _ButtonPressedState.setdPadDown = function (newPressedState) {
return _dPadLeftPressed; raiseKeyEvent(_dPadDownPressed, newPressedState, _GAMEPAD_DPAD_DOWN_KEY, _GAMEPAD_DPAD_DOWN_KEYCODE, true);
}; _dPadDownPressed = newPressedState;
};
_ButtonPressedState.setdPadLeft = function (newPressedState) { _ButtonPressedState.getdPadLeft = function () {
raiseKeyEvent(_dPadLeftPressed, newPressedState, _GAMEPAD_DPAD_LEFT_KEY, _GAMEPAD_DPAD_LEFT_KEYCODE, true); return _dPadLeftPressed;
_dPadLeftPressed = newPressedState; };
};
_ButtonPressedState.getdPadRight = function () { _ButtonPressedState.setdPadLeft = function (newPressedState) {
return _dPadRightPressed; raiseKeyEvent(_dPadLeftPressed, newPressedState, _GAMEPAD_DPAD_LEFT_KEY, _GAMEPAD_DPAD_LEFT_KEYCODE, true);
}; _dPadLeftPressed = newPressedState;
};
_ButtonPressedState.setdPadRight = function (newPressedState) { _ButtonPressedState.getdPadRight = function () {
raiseKeyEvent(_dPadRightPressed, newPressedState, _GAMEPAD_DPAD_RIGHT_KEY, _GAMEPAD_DPAD_RIGHT_KEYCODE, true); return _dPadRightPressed;
_dPadRightPressed = newPressedState; };
};
var times = {}; _ButtonPressedState.setdPadRight = function (newPressedState) {
raiseKeyEvent(_dPadRightPressed, newPressedState, _GAMEPAD_DPAD_RIGHT_KEY, _GAMEPAD_DPAD_RIGHT_KEYCODE, true);
_dPadRightPressed = newPressedState;
};
function throttle(key) { var times = {};
var time = times[key] || 0;
var now = new Date().getTime();
if ((now - time) >= 200) { function throttle(key) {
//times[key] = now; var time = times[key] || 0;
return true; var now = new Date().getTime();
}
return false;
}
function resetThrottle(key) {
times[key] = new Date().getTime();
}
var isElectron = navigator.userAgent.toLowerCase().indexOf('electron') !== -1;
function allowInput() {
// This would be nice but always seems to return true with electron
if (!isElectron && document.hidden) { /* eslint-disable-line compat/compat */
return false;
}
if (appHost.getWindowState() === 'Minimized') {
return false;
}
if ((now - time) >= 200) {
//times[key] = now;
return true; return true;
} }
function raiseEvent(name, key, keyCode) { return false;
if (!allowInput()) { }
return;
}
var event = document.createEvent('Event'); function resetThrottle(key) {
event.initEvent(name, true, true); times[key] = new Date().getTime();
event.key = key; }
event.keyCode = keyCode;
(document.activeElement || document.body).dispatchEvent(event); var isElectron = navigator.userAgent.toLowerCase().indexOf('electron') !== -1;
function allowInput() {
// This would be nice but always seems to return true with electron
if (!isElectron && document.hidden) { /* eslint-disable-line compat/compat */
return false;
} }
function clickElement(elem) { if (appHost.getWindowState() === 'Minimized') {
if (!allowInput()) { return false;
return;
}
elem.click();
} }
function raiseKeyEvent(oldPressedState, newPressedState, key, keyCode, enableRepeatKeyDown, clickonKeyUp) { return true;
// No-op if oldPressedState === newPressedState }
if (newPressedState === true) {
// button down
var fire = false;
// always fire if this is the initial down press function raiseEvent(name, key, keyCode) {
if (oldPressedState === false) { if (!allowInput()) {
fire = true; return;
resetThrottle(key); }
} else if (enableRepeatKeyDown) {
fire = throttle(key);
}
if (fire && keyCode) { var event = document.createEvent('Event');
raiseEvent('keydown', key, keyCode); event.initEvent(name, true, true);
} event.key = key;
} else if (newPressedState === false && oldPressedState === true) { event.keyCode = keyCode;
(document.activeElement || document.body).dispatchEvent(event);
}
function clickElement(elem) {
if (!allowInput()) {
return;
}
elem.click();
}
function raiseKeyEvent(oldPressedState, newPressedState, key, keyCode, enableRepeatKeyDown, clickonKeyUp) {
// No-op if oldPressedState === newPressedState
if (newPressedState === true) {
// button down
var fire = false;
// always fire if this is the initial down press
if (oldPressedState === false) {
fire = true;
resetThrottle(key); resetThrottle(key);
} else if (enableRepeatKeyDown) {
fire = throttle(key);
}
// button up if (fire && keyCode) {
if (keyCode) { raiseEvent('keydown', key, keyCode);
raiseEvent('keyup', key, keyCode); }
} } else if (newPressedState === false && oldPressedState === true) {
if (clickonKeyUp) { resetThrottle(key);
clickElement(document.activeElement || window);
} // button up
if (keyCode) {
raiseEvent('keyup', key, keyCode);
}
if (clickonKeyUp) {
clickElement(document.activeElement || window);
} }
} }
}
var inputLoopTimer; var inputLoopTimer;
function runInputLoop() { function runInputLoop() {
// Get the latest gamepad state. // Get the latest gamepad state.
var gamepads = navigator.getGamepads(); /* eslint-disable-line compat/compat */ var gamepads = navigator.getGamepads(); /* eslint-disable-line compat/compat */
for (var i = 0, len = gamepads.length; i < len; i++) { for (var i = 0, len = gamepads.length; i < len; i++) {
var gamepad = gamepads[i]; var gamepad = gamepads[i];
if (!gamepad) { if (!gamepad) {
continue; continue;
} }
// Iterate through the axes // Iterate through the axes
var axes = gamepad.axes; var axes = gamepad.axes;
var leftStickX = axes[0]; var leftStickX = axes[0];
var leftStickY = axes[1]; var leftStickY = axes[1];
if (leftStickX > _THUMB_STICK_THRESHOLD) { // Right if (leftStickX > _THUMB_STICK_THRESHOLD) { // Right
_ButtonPressedState.setleftThumbstickRight(true); _ButtonPressedState.setleftThumbstickRight(true);
} else if (leftStickX < -_THUMB_STICK_THRESHOLD) { // Left } else if (leftStickX < -_THUMB_STICK_THRESHOLD) { // Left
_ButtonPressedState.setleftThumbstickLeft(true); _ButtonPressedState.setleftThumbstickLeft(true);
} else if (leftStickY < -_THUMB_STICK_THRESHOLD) { // Up } else if (leftStickY < -_THUMB_STICK_THRESHOLD) { // Up
_ButtonPressedState.setleftThumbstickUp(true); _ButtonPressedState.setleftThumbstickUp(true);
} else if (leftStickY > _THUMB_STICK_THRESHOLD) { // Down } else if (leftStickY > _THUMB_STICK_THRESHOLD) { // Down
_ButtonPressedState.setleftThumbstickDown(true); _ButtonPressedState.setleftThumbstickDown(true);
} else { } else {
_ButtonPressedState.setleftThumbstickLeft(false); _ButtonPressedState.setleftThumbstickLeft(false);
_ButtonPressedState.setleftThumbstickRight(false); _ButtonPressedState.setleftThumbstickRight(false);
_ButtonPressedState.setleftThumbstickUp(false); _ButtonPressedState.setleftThumbstickUp(false);
_ButtonPressedState.setleftThumbstickDown(false); _ButtonPressedState.setleftThumbstickDown(false);
} }
// Iterate through the buttons to see if Left thumbstick, DPad, A and B are pressed. // Iterate through the buttons to see if Left thumbstick, DPad, A and B are pressed.
var buttons = gamepad.buttons; var buttons = gamepad.buttons;
for (var j = 0, len = buttons.length; j < len; j++) { for (var j = 0, len = buttons.length; j < len; j++) {
if (ProcessedButtons.indexOf(j) !== -1) { if (ProcessedButtons.indexOf(j) !== -1) {
if (buttons[j].pressed) { if (buttons[j].pressed) {
switch (j) { switch (j) {
case _GAMEPAD_DPAD_UP_BUTTON_INDEX: case _GAMEPAD_DPAD_UP_BUTTON_INDEX:
_ButtonPressedState.setdPadUp(true); _ButtonPressedState.setdPadUp(true);
break; break;
case _GAMEPAD_DPAD_DOWN_BUTTON_INDEX: case _GAMEPAD_DPAD_DOWN_BUTTON_INDEX:
_ButtonPressedState.setdPadDown(true); _ButtonPressedState.setdPadDown(true);
break; break;
case _GAMEPAD_DPAD_LEFT_BUTTON_INDEX: case _GAMEPAD_DPAD_LEFT_BUTTON_INDEX:
_ButtonPressedState.setdPadLeft(true); _ButtonPressedState.setdPadLeft(true);
break; break;
case _GAMEPAD_DPAD_RIGHT_BUTTON_INDEX: case _GAMEPAD_DPAD_RIGHT_BUTTON_INDEX:
_ButtonPressedState.setdPadRight(true); _ButtonPressedState.setdPadRight(true);
break; break;
case _GAMEPAD_A_BUTTON_INDEX: case _GAMEPAD_A_BUTTON_INDEX:
_ButtonPressedState.setgamepadA(true); _ButtonPressedState.setgamepadA(true);
break; break;
case _GAMEPAD_B_BUTTON_INDEX: case _GAMEPAD_B_BUTTON_INDEX:
_ButtonPressedState.setgamepadB(true); _ButtonPressedState.setgamepadB(true);
break; break;
default: default:
// No-op // No-op
break; break;
} }
} else { } else {
switch (j) { switch (j) {
case _GAMEPAD_DPAD_UP_BUTTON_INDEX: case _GAMEPAD_DPAD_UP_BUTTON_INDEX:
if (_ButtonPressedState.getdPadUp()) { if (_ButtonPressedState.getdPadUp()) {
_ButtonPressedState.setdPadUp(false); _ButtonPressedState.setdPadUp(false);
} }
break; break;
case _GAMEPAD_DPAD_DOWN_BUTTON_INDEX: case _GAMEPAD_DPAD_DOWN_BUTTON_INDEX:
if (_ButtonPressedState.getdPadDown()) { if (_ButtonPressedState.getdPadDown()) {
_ButtonPressedState.setdPadDown(false); _ButtonPressedState.setdPadDown(false);
} }
break; break;
case _GAMEPAD_DPAD_LEFT_BUTTON_INDEX: case _GAMEPAD_DPAD_LEFT_BUTTON_INDEX:
if (_ButtonPressedState.getdPadLeft()) { if (_ButtonPressedState.getdPadLeft()) {
_ButtonPressedState.setdPadLeft(false); _ButtonPressedState.setdPadLeft(false);
} }
break; break;
case _GAMEPAD_DPAD_RIGHT_BUTTON_INDEX: case _GAMEPAD_DPAD_RIGHT_BUTTON_INDEX:
if (_ButtonPressedState.getdPadRight()) { if (_ButtonPressedState.getdPadRight()) {
_ButtonPressedState.setdPadRight(false); _ButtonPressedState.setdPadRight(false);
} }
break; break;
case _GAMEPAD_A_BUTTON_INDEX: case _GAMEPAD_A_BUTTON_INDEX:
if (_ButtonPressedState.getgamepadA()) { if (_ButtonPressedState.getgamepadA()) {
_ButtonPressedState.setgamepadA(false); _ButtonPressedState.setgamepadA(false);
} }
break; break;
case _GAMEPAD_B_BUTTON_INDEX: case _GAMEPAD_B_BUTTON_INDEX:
if (_ButtonPressedState.getgamepadB()) { if (_ButtonPressedState.getgamepadB()) {
_ButtonPressedState.setgamepadB(false); _ButtonPressedState.setgamepadB(false);
} }
break; break;
default: default:
// No-op // No-op
break; break;
}
} }
} }
} }
} }
// Schedule the next one
inputLoopTimer = requestAnimationFrame(runInputLoop);
} }
// Schedule the next one
inputLoopTimer = requestAnimationFrame(runInputLoop);
}
function startInputLoop() { function startInputLoop() {
if (!inputLoopTimer) { if (!inputLoopTimer) {
runInputLoop(); runInputLoop();
}
}
function stopInputLoop() {
cancelAnimationFrame(inputLoopTimer);
inputLoopTimer = undefined;
}
function isGamepadConnected() {
var gamepads = navigator.getGamepads(); /* eslint-disable-line compat/compat */
for (var i = 0, len = gamepads.length; i < len; i++) {
var gamepad = gamepads[i];
if (gamepad && gamepad.connected) {
return true;
} }
} }
return false;
}
function stopInputLoop() { function onFocusOrGamepadAttach(e) {
cancelAnimationFrame(inputLoopTimer); /* eslint-disable-next-line compat/compat */
inputLoopTimer = undefined; if (isGamepadConnected() && document.hasFocus()) {
console.log('Gamepad connected! Starting input loop');
startInputLoop();
} }
}
function isGamepadConnected() { function onFocusOrGamepadDetach(e) {
var gamepads = navigator.getGamepads(); /* eslint-disable-line compat/compat */ /* eslint-disable-next-line compat/compat */
for (var i = 0, len = gamepads.length; i < len; i++) { if (!isGamepadConnected() || !document.hasFocus()) {
var gamepad = gamepads[i]; console.log('Gamepad disconnected! No other gamepads are connected, stopping input loop');
if (gamepad && gamepad.connected) { stopInputLoop();
return true; } else {
} console.log('Gamepad disconnected! There are gamepads still connected.');
}
return false;
} }
}
function onFocusOrGamepadAttach(e) { // Event listeners for any change in gamepads' state.
/* eslint-disable-next-line compat/compat */ window.addEventListener('gamepaddisconnected', onFocusOrGamepadDetach);
if (isGamepadConnected() && document.hasFocus()) { window.addEventListener('gamepadconnected', onFocusOrGamepadAttach);
console.log('Gamepad connected! Starting input loop'); window.addEventListener('blur', onFocusOrGamepadDetach);
startInputLoop(); window.addEventListener('focus', onFocusOrGamepadAttach);
}
}
function onFocusOrGamepadDetach(e) { onFocusOrGamepadAttach();
/* eslint-disable-next-line compat/compat */
if (!isGamepadConnected() || !document.hasFocus()) {
console.log('Gamepad disconnected! No other gamepads are connected, stopping input loop');
stopInputLoop();
} else {
console.log('Gamepad disconnected! There are gamepads still connected.');
}
}
// Event listeners for any change in gamepads' state. // The gamepadInputEmulation is a string property that exists in JavaScript UWAs and in WebViews in UWAs.
window.addEventListener('gamepaddisconnected', onFocusOrGamepadDetach); // It won't exist in Win8.1 style apps or browsers.
window.addEventListener('gamepadconnected', onFocusOrGamepadAttach); if (window.navigator && typeof window.navigator.gamepadInputEmulation === 'string') {
window.addEventListener('blur', onFocusOrGamepadDetach); // We want the gamepad to provide gamepad VK keyboard events rather than moving a
window.addEventListener('focus', onFocusOrGamepadAttach); // mouse like cursor. Set to "keyboard", the gamepad will provide such keyboard events
// and provide input to the DOM navigator.getGamepads API.
onFocusOrGamepadAttach(); window.navigator.gamepadInputEmulation = 'gamepad';
}
// The gamepadInputEmulation is a string property that exists in JavaScript UWAs and in WebViews in UWAs.
// It won't exist in Win8.1 style apps or browsers.
if (window.navigator && typeof window.navigator.gamepadInputEmulation === 'string') {
// We want the gamepad to provide gamepad VK keyboard events rather than moving a
// mouse like cursor. Set to "keyboard", the gamepad will provide such keyboard events
// and provide input to the DOM navigator.getGamepads API.
window.navigator.gamepadInputEmulation = 'gamepad';
}
});

View file

@ -689,7 +689,8 @@ function initClient() {
'events', 'events',
'credentialprovider', 'credentialprovider',
'connectionManagerFactory', 'connectionManagerFactory',
'appStorage' 'appStorage',
'focus-options-polyfill'
] ]
}, },
urlArgs: urlArgs, urlArgs: urlArgs,
@ -698,7 +699,7 @@ function initClient() {
}); });
promise = require(['fetch']) promise = require(['fetch'])
.then(() => require(['jQuery', 'polyfill', 'fast-text-encoding', 'intersection-observer', 'classlist-polyfill', 'css!assets/css/site', 'jellyfin-noto'], (jQuery) => { .then(() => require(['jQuery', 'polyfill', 'fast-text-encoding', 'intersection-observer', 'classlist-polyfill', 'focus-options-polyfill', 'css!assets/css/site', 'jellyfin-noto'], (jQuery) => {
// Expose jQuery globally // Expose jQuery globally
window.$ = jQuery; window.$ = jQuery;
window.jQuery = jQuery; window.jQuery = jQuery;

View file

@ -4608,6 +4608,11 @@ flv.js@^1.5.0:
es6-promise "^4.2.5" es6-promise "^4.2.5"
webworkify "^1.5.0" webworkify "^1.5.0"
focus-options-polyfill@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/focus-options-polyfill/-/focus-options-polyfill-1.5.0.tgz#fd2e6a0d6d8f828346726a5adf02f3d2bdfece5f"
integrity sha512-HiMSaXGUz2OFjOuoGTWXlp+YjZCGnVXPu6vPeccgaSOzGmqLVz8tJRcKXWfMJnj16LXf/IM1rJI0zrZMVc9q7g==
follow-redirects@1.5.10: follow-redirects@1.5.10:
version "1.5.10" version "1.5.10"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a"