├── icons ├── dc.png ├── gb.png ├── gg.png ├── md.png ├── ss.png ├── 32x.png ├── 3ds.png ├── gba.png ├── gbc.png ├── n64.png ├── nds.png ├── nes.png ├── ps1.png ├── ps2.png ├── sms.png └── snes.png ├── additional_icons.7z ├── app ├── Roboto-Black.ttf ├── Roboto-Light.ttf ├── img │ ├── add_sys.png │ └── controllerMapping.svg ├── Roboto-Medium.ttf ├── NotoSansJP-Regular.otf ├── controllerInput.css ├── package.json ├── index.js ├── upgradeSettings.js ├── controllerInput.js ├── theme.css ├── index.html ├── logic.js └── package-lock.json ├── readme ├── romlauncher_screen_1.png ├── romlauncher_screen_2.png ├── romlauncher_screen_3.png └── readme.txt ├── README.md └── LICENSE /icons/dc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ju-am/ROMLauncher/HEAD/icons/dc.png -------------------------------------------------------------------------------- /icons/gb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ju-am/ROMLauncher/HEAD/icons/gb.png -------------------------------------------------------------------------------- /icons/gg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ju-am/ROMLauncher/HEAD/icons/gg.png -------------------------------------------------------------------------------- /icons/md.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ju-am/ROMLauncher/HEAD/icons/md.png -------------------------------------------------------------------------------- /icons/ss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ju-am/ROMLauncher/HEAD/icons/ss.png -------------------------------------------------------------------------------- /icons/32x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ju-am/ROMLauncher/HEAD/icons/32x.png -------------------------------------------------------------------------------- /icons/3ds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ju-am/ROMLauncher/HEAD/icons/3ds.png -------------------------------------------------------------------------------- /icons/gba.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ju-am/ROMLauncher/HEAD/icons/gba.png -------------------------------------------------------------------------------- /icons/gbc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ju-am/ROMLauncher/HEAD/icons/gbc.png -------------------------------------------------------------------------------- /icons/n64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ju-am/ROMLauncher/HEAD/icons/n64.png -------------------------------------------------------------------------------- /icons/nds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ju-am/ROMLauncher/HEAD/icons/nds.png -------------------------------------------------------------------------------- /icons/nes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ju-am/ROMLauncher/HEAD/icons/nes.png -------------------------------------------------------------------------------- /icons/ps1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ju-am/ROMLauncher/HEAD/icons/ps1.png -------------------------------------------------------------------------------- /icons/ps2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ju-am/ROMLauncher/HEAD/icons/ps2.png -------------------------------------------------------------------------------- /icons/sms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ju-am/ROMLauncher/HEAD/icons/sms.png -------------------------------------------------------------------------------- /icons/snes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ju-am/ROMLauncher/HEAD/icons/snes.png -------------------------------------------------------------------------------- /additional_icons.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ju-am/ROMLauncher/HEAD/additional_icons.7z -------------------------------------------------------------------------------- /app/Roboto-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ju-am/ROMLauncher/HEAD/app/Roboto-Black.ttf -------------------------------------------------------------------------------- /app/Roboto-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ju-am/ROMLauncher/HEAD/app/Roboto-Light.ttf -------------------------------------------------------------------------------- /app/img/add_sys.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ju-am/ROMLauncher/HEAD/app/img/add_sys.png -------------------------------------------------------------------------------- /app/Roboto-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ju-am/ROMLauncher/HEAD/app/Roboto-Medium.ttf -------------------------------------------------------------------------------- /app/NotoSansJP-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ju-am/ROMLauncher/HEAD/app/NotoSansJP-Regular.otf -------------------------------------------------------------------------------- /readme/romlauncher_screen_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ju-am/ROMLauncher/HEAD/readme/romlauncher_screen_1.png -------------------------------------------------------------------------------- /readme/romlauncher_screen_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ju-am/ROMLauncher/HEAD/readme/romlauncher_screen_2.png -------------------------------------------------------------------------------- /readme/romlauncher_screen_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ju-am/ROMLauncher/HEAD/readme/romlauncher_screen_3.png -------------------------------------------------------------------------------- /app/controllerInput.css: -------------------------------------------------------------------------------- 1 | .system:focus { 2 | outline: none; 3 | border: 1px solid white; 4 | background: rgba(0,0,0,0.4); 5 | } 6 | 7 | .rom:focus > td { 8 | border: 1px solid white; 9 | background: rgba(0,0,0,0.4); 10 | } -------------------------------------------------------------------------------- /app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ROML", 3 | "version": "1.0.0", 4 | "description": "ROM Launcher", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "electron ." 8 | }, 9 | "author": "TLOZ", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "electron": "^4.0.6" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ROM Launcher 2 | 3 | **ROM Launcher** is a minimal Electron desktop app to launch emulators with basic arguments (yes, *minimal* and *electron* in one sentence, I know). It's compatible with *pretty much* every emulator that supports command-line arguments. 4 | 5 | ### Download 6 | [Download version 1.2 (Win/Linux x64) here](https://github.com/ButcheredGenre/ROMLauncher/releases/tag/1.2) 7 | 8 | NEW: ROM Launcher v.1.2! Version 1.2 adds optional subfolder search and experimental controller support, amongst a number of smaller fixes and additions. 9 | 10 | ### Installation & Help 11 | ROM Launcher is portable and cannot be installed - just drop the /ROMLauncher/ folder in some appropriate directory. For help and basic reference, read through 'readme.txt' in the /readme/ directory. 12 | 13 | ### Screenshots 14 | ![ROM Launcher screenshot](/readme/romlauncher_screen_3.png) 15 | 16 | ![ROM Launcher screenshot](/readme/romlauncher_screen_2.png) 17 | 18 | ![ROM Launcher screenshot](/readme/romlauncher_screen_1.png) 19 | 20 | (...don't worry, you can adjust the colors if this is a bit too much for you.) 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 ButcheredGenre 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /app/index.js: -------------------------------------------------------------------------------- 1 | const { app, dialog, BrowserWindow } = require('electron'); 2 | 3 | let win; 4 | 5 | function createWindow() { 6 | 7 | win = new BrowserWindow({ 8 | width: 600, 9 | height: 750, 10 | frame: false, 11 | webPreferences: { 12 | nodeIntegration: true 13 | } 14 | }); 15 | win.loadFile('./index.html'); 16 | // win.webContents.openDevTools(); 17 | 18 | win.on('closed', () => { 19 | win = null 20 | }); 21 | 22 | win.on('maximize', (e) => { 23 | win.webContents.executeJavaScript('$("#titlebar").css({"margin" : "0", "padding" : "0 4px 36px 12px"})'); 24 | win.webContents.executeJavaScript('$("#wrapper").css({"left" : 0, "right" : 0, "bottom" : 0})'); 25 | win.webContents.executeJavaScript('$("#wrapper_settings").css({"left" : 0, "right" : 0, "bottom" : 0})'); 26 | }); 27 | 28 | win.on('move', (e) => { 29 | win.webContents.executeJavaScript('$("#titlebar").css({"margin" : "4px", "padding" : "0 0 0 12px"})'); 30 | win.webContents.executeJavaScript('$("#wrapper").css({"left" : "4px", "right" : "4px", "bottom" : "4px"})'); 31 | win.webContents.executeJavaScript('$("#wrapper_settings").css({"left" : "4px", "right" : "4px", "bottom" : "4px"})'); 32 | }); 33 | 34 | win.on('blur', function(){ 35 | win.webContents.executeJavaScript('controllerInputBlurred();'); 36 | }); 37 | 38 | win.on('focus', function(){ 39 | win.webContents.executeJavaScript('controllerInputFocused();'); 40 | }); 41 | } 42 | 43 | app.on('ready', createWindow); 44 | 45 | app.on('window-all-closed', () => { 46 | if (process.platform !== 'darwin') { 47 | app.quit(); 48 | } 49 | }); 50 | 51 | app.on('activate', () => { 52 | if (win === null) { 53 | createWindow(); 54 | } 55 | }); -------------------------------------------------------------------------------- /app/upgradeSettings.js: -------------------------------------------------------------------------------- 1 | const currentSettingsVersion = 1; 2 | 3 | function upgradeSettings() { 4 | 5 | readSettingsFile(); 6 | readSettingsVersion(); 7 | upradeOldSettings(); 8 | writeUpgradedSettings(); 9 | 10 | } 11 | 12 | var oldSettings; 13 | function readSettingsFile() { 14 | let oldSettingsFile; 15 | try { 16 | oldSettingsFile = fs.readFileSync(exePath+'/../settings.json') 17 | } 18 | catch (e) { 19 | console.log("upgrade-settings : settings.json is missing") 20 | } 21 | if (typeof oldSettingsFile !== 'undefined') { 22 | oldSettings = JSON.parse(oldSettingsFile); 23 | } 24 | } 25 | 26 | var oldSettingsVersion; 27 | function readSettingsVersion() { 28 | if (oldSettings !== undefined) { 29 | if (oldSettings.hasOwnProperty('general')) { 30 | if (oldSettings.general.hasOwnProperty('settingsVersion')) { 31 | oldSettingsVersion = oldSettings.general.settingsVersion; 32 | } 33 | else { 34 | oldSettingsVersion = 0; 35 | } 36 | } 37 | else { 38 | oldSettingsVersion = 0; 39 | } 40 | } 41 | } 42 | 43 | var oldSettingsUpgraded = false; 44 | function upradeOldSettings() { 45 | 46 | if (oldSettings !== undefined) { 47 | if (oldSettingsVersion === 0) { 48 | 49 | console.log("upgrade-settings : version 0 to version 1"); 50 | // settings.json version 0: 51 | 52 | /* 53 | { 54 | "systems": [ 55 | { 56 | "tabName": "", 57 | "systemId": "", 58 | "emuPath": "", 59 | "emuArgs": [""], 60 | "romPaths": [""], 61 | "fileTypes": [""], 62 | "gradient": "", 63 | "order": "" 64 | } 65 | ] 66 | } 67 | */ 68 | 69 | // upgrade to version 1: 70 | 71 | /* 72 | -> add general.settingsVersion 73 | */ 74 | oldSettings.general = new Object(); 75 | oldSettings.general.settingsVersion = 1; 76 | 77 | /* 78 | -> convert systems[system].romPaths from 79 | "romPaths" : [""] 80 | -> to 81 | "romPaths" : [{ 82 | "directory" : "", 83 | "recursive" : false 84 | }] 85 | */ 86 | oldSettings.systems.forEach(function(systemOptions) { 87 | let newRomPaths = []; 88 | systemOptions.romPaths.forEach(function(romPath) { 89 | let newRomPathEntry = new Object(); 90 | newRomPathEntry.directory = romPath; 91 | newRomPathEntry.recursive = false; 92 | newRomPaths.push(newRomPathEntry); 93 | }); 94 | systemOptions.romPaths = newRomPaths; 95 | }); 96 | 97 | console.log(oldSettings); 98 | oldSettingsUpgraded = true; 99 | } 100 | } 101 | } 102 | 103 | function writeUpgradedSettings() { 104 | if (oldSettingsUpgraded) { 105 | fs.writeFileSync( exePath+'/../settings.json', JSON.stringify(oldSettings, null, 4), 'utf-8' ); 106 | } 107 | } -------------------------------------------------------------------------------- /readme/readme.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | ButcheredGenre ROM Launcher----------------------------------------------------- 3 | https://github.com/ButcheredGenre/ROMLauncher----------------------------------- 4 | -------------------------------------------------------------------------------- 5 | 6 | SETUP 7 | CONFIGURATION 8 | IN CASE OF ERROR 9 | MISSING OR INCORRECT IMAGES 10 | LINUX VERSION 11 | KNOWN ISSUES 12 | CONTACT / TROUBLESHOOTING 13 | 14 | -------------------------------------------------------------------------------- 15 | SETUP--------------------------------------------------------------------------- 16 | -------------------------------------------------------------------------------- 17 | 18 | Place this folder anywhere you like. If you want to use relative paths, you 19 | should place this folder near your emulators. The ROM Launcher executable is in 20 | the /bin/ folder (launcher.exe). A settings file (if not already created) will 21 | be created in the executable's parent directory. 22 | 23 | -------------------------------------------------------------------------------- 24 | CONFIGURATION------------------------------------------------------------------- 25 | -------------------------------------------------------------------------------- 26 | 27 | On first start, you'll see an empty launcher. Press the EDIT button (top right) 28 | to enter the 'Configuration Editor'. You'll have to fill in the following 29 | options for every new emulator/ system you want to configure: 30 | 31 | >---[Launcher Prefix] : The name shown before 'Launcher' in the title. If you 32 | input e.g. 'GBA' here, it will later show up as 'GBA Launcher'. 33 | 34 | >---[Unique ID] : A unique system ID. Should be short, unique, and sensible. 35 | Sticking with the GBA example, a suitable system ID might be 'gba'. This name 36 | is used for the system icon. The ROM Launcher will search for e.g. gba.png in 37 | the /icons/ directory. 38 | 39 | >---[Emulator Executable Path] : The path (absolute/relative) or command to 40 | launch the emulator. Use the 'Search File' button to search for the emulator 41 | directly. You can then use the 'Make Relative' button to convert the path to 42 | a relative path, if possible (this is optional). 43 | 44 | >---[Emulator Arguments] : Optional arguments to use with the emulator. Enter 45 | every argument individually. When arguments are used, the ROM-file should be 46 | added as its own argument with %ROMFILE%. Again, this is usually not needed 47 | unless you want to launch e.g. retroarch with a specific core. 48 | 49 | >---[ROM Directories] : One or more directories containing ROMs for this system. 50 | You can use the 'Search Directory' button to search for directories, then use 51 | the 'Make Relative' button to convert the paths to relative paths, if possible 52 | (this is optional). Click on the moon icon next to the directory to toggle 53 | between a flat (include only the specified directory) or deep (include this, 54 | as well as all of this folder's subdirectories) search for this directory. 55 | 56 | >---[ROM File Types] : File-types of ROMs. Add without punctuation. 57 | 58 | >---[CSS Background] : Optional CSS background value. You can enter a CSS 59 | gradient here, like: 60 | 61 | linear-gradient(0deg, rgba(0,164,113,1) 0%, rgba(0,129,156,1) 100%) 62 | 63 | ...there are plenty of websites that can generate a CSS gradient for you. Make 64 | sure to enter only the value, without the 'background:'-part, or final 65 | semicolon. Enter nothing for a default gradient, or pick from one of the presets 66 | below the input field. 67 | 68 | >---[Order] : List order of the system relative to other systems. This isn't the 69 | most elegant solution to sorting things, but it ... kind of works, so there you 70 | go. Lowest numbers go first. If you leave enough space between numbers 71 | (e.g. start with 0 and go up in steps of 100), you shouldn't have any issues 72 | when inserting a new system between existing systems. 73 | 74 | ...then hit 'Save' and hope that the ROM Launcher doesn't complain. 75 | If things do not load as expected even though you've entered everything 76 | correctly, try restarting the ROM Launcher. If everything fails, try restoring 77 | a previous configuration from the settings_backup.json file. 78 | 79 | (If you feel like it, you can edit the settings.json directly. This makes it 80 | easier to e.g. find & replace strings, if needed. Overall, this is not 81 | recommended, as a single wrong character will break the configuration.) 82 | 83 | -------------------------------------------------------------------------------- 84 | IN CASE OF ERROR---------------------------------------------------------------- 85 | -------------------------------------------------------------------------------- 86 | 87 | If, for some reason, your current configuration should be messed up, you can 88 | restore your previous configuration by renaming settings_backup.json to 89 | settings.json (and deleting the messed up settings.json). 90 | 91 | Keep in mind that settings_backup.json only exists if you saved your 92 | configuration at least twice. 93 | 94 | -------------------------------------------------------------------------------- 95 | MISSING OR INCORRECT IMAGES----------------------------------------------------- 96 | -------------------------------------------------------------------------------- 97 | 98 | You'll very likely see missing images after setting up a new system. 99 | System icons need to be placed in the /icons/ folder. Icons need to be PNGs and 100 | named after the system's unique ID. E.g. if your unique system ID is 'gba', your 101 | icon should be called 'gba.png'. Icons need to be 32x32 pixels in size. 102 | 103 | The 'additional_icons.7z' archive contains a great number of freeware icons by 104 | 'Yoshi'. You'll likely find something in the archive -- just rename it and 105 | chuck it into the /icons/ folder (please note that not every icon in the 106 | archive is of appropriate 32x32 pixel size). 107 | 108 | You'll need an application like '7-zip' to un-archive the icons archive: 109 | https://www.7-zip.org/ 110 | 111 | -------------------------------------------------------------------------------- 112 | LINUX VERSION------------------------------------------------------------------- 113 | -------------------------------------------------------------------------------- 114 | 115 | Text-rendering of certain unicode characters will be different in the Linux 116 | release. Please note that I do most of my testing with the Windows version of 117 | the launcher. Whatever testing I do for the Linux release, I do with Linux Mint. 118 | 119 | -------------------------------------------------------------------------------- 120 | KNOWN ISSUES-------------------------------------------------------------------- 121 | -------------------------------------------------------------------------------- 122 | 123 | >---Some emulators will show the 'Failed to launch' message on normal exit. 124 | 125 | This is expected behavior on emulators exiting with a non-zero value. ROM 126 | Launcher will think it failed to find, or successfully launch the emulator. 127 | This won't affect the vast majority of emulators, however. 128 | 129 | >---Some changes don't take immediate effect after hitting 'Save' 130 | 131 | Yeah, that's poor programming on my part. Just switch systems back and forth or 132 | restart the application if that doesn't help. 133 | 134 | -------------------------------------------------------------------------------- 135 | CONTACT / TROUBLESHOOTING------------------------------------------------------- 136 | -------------------------------------------------------------------------------- 137 | 138 | If - after some time - you're unable to resolve an issue -- or if you have a 139 | feature request, feel free to contact me on reddit (/u/TLOZ). 140 | Please check out the KNOWN ISSUES first. I can't guarantee a fast response. 141 | You can also open up issues on the GitHub page. 142 | 143 | -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- /app/controllerInput.js: -------------------------------------------------------------------------------- 1 | var controllerInputEnabled = false; 2 | var controllerForceQuit = false; 3 | 4 | // default x-input button mappings for emulator force quit 5 | var forceQuitButtons = []; 6 | 7 | // default x-input button mappings 8 | var gpUpButton = 12; 9 | var gpDownButton = 13; 10 | var gpLeftButton = 14; 11 | var gpRightButton = 15; 12 | var gpAButton = 0; 13 | var gpBButton = 1; 14 | var gpLButton = 4; 15 | var gpRButton = 5; 16 | 17 | var gpUpButtonFrames = -1; 18 | var gpDownButtonFrames = -1; 19 | var gpLeftButtonFrames = -1; 20 | var gpRightButtonFrames = -1; 21 | var gpAButtonFrames = -1; 22 | var gpBButtonFrames = -1; 23 | var gpLButtonFrames = -1; 24 | var gpRButtonFrames = -1; 25 | 26 | window.addEventListener("gamepadconnected", (event) => { 27 | console.log("Gamepad connected."); 28 | console.log(event.gamepad); 29 | }); 30 | 31 | window.addEventListener("gamepaddisconnected", (event) => { 32 | console.log("Gamepad disconnected."); 33 | console.log(event.gamepad); 34 | }); 35 | 36 | var gamepad; 37 | var windowRaf = window.requestAnimationFrame; 38 | 39 | windowRaf(controllerUpdate); 40 | function controllerUpdate() { 41 | 42 | gamepad = navigator.getGamepads()[0]; 43 | 44 | if (gamepad !== null && controllerInputEnabled) { 45 | if (!controllerStyle) enableControllerStyle(); 46 | 47 | // Force quit : Step 1 -> Create an array with booleans for every button required to force quit 48 | let forceQuitCheck = []; 49 | if (controllerForceQuit) { 50 | forceQuitButtons.forEach(function() { 51 | forceQuitCheck.push(false); 52 | }); 53 | } 54 | 55 | gamepad.buttons.forEach((button, index) => { 56 | 57 | if (button.pressed) { 58 | 59 | // Force Quit : Step 2 -> For every required button pressed, set the corresponding check flag true 60 | if (controllerForceQuit) { 61 | for (let i = 0; i < forceQuitButtons.length; ++i) { 62 | if (index === forceQuitButtons[i]) { 63 | forceQuitCheck[i] = true; 64 | } 65 | } 66 | } 67 | 68 | // Button Mapping : Set value if current element is button mapping input 69 | if ($(document.activeElement).hasClass('controller_mapping')) { 70 | let alreadyMapped = false; 71 | $('.controller_mapping').each(function() { 72 | if ($(this).val().toString() === index.toString()) { 73 | alreadyMapped = true; 74 | } 75 | }); 76 | if (!alreadyMapped) { 77 | $(document.activeElement).val(index); 78 | $(document.activeElement).next().focus(); 79 | } 80 | } 81 | 82 | if (index === gpUpButton && controllerInputFocus) { 83 | gpUpButtonFrames++; 84 | if (gpUpButtonFrames < 60) { 85 | if (gpUpButtonFrames % 30 === 0) focusPrevious(true); 86 | } 87 | else if (gpUpButtonFrames < 180) { 88 | if (gpUpButtonFrames % 10 === 0) focusPrevious(false); 89 | } 90 | else if (gpUpButtonFrames < 360) { 91 | if (gpUpButtonFrames % 3 === 0) focusPrevious(false); 92 | } 93 | else focusPrevious(false); 94 | } 95 | else if (index === gpDownButton && controllerInputFocus) { 96 | gpDownButtonFrames++; 97 | if (gpDownButtonFrames < 60) { 98 | if (gpDownButtonFrames % 30 === 0) focusNext(true); 99 | } 100 | else if (gpDownButtonFrames < 180) { 101 | if (gpDownButtonFrames % 10 === 0) focusNext(false); 102 | } 103 | else if (gpDownButtonFrames < 360) { 104 | if (gpDownButtonFrames % 3 === 0) focusNext(false); 105 | } 106 | else focusNext(false); 107 | } 108 | else if (index === gpLeftButton && controllerInputFocus) { 109 | gpLeftButtonFrames++; 110 | if (gpLeftButtonFrames === 0) focusSystem(); 111 | } 112 | else if (index === gpRightButton && controllerInputFocus) { 113 | gpRightButtonFrames++; 114 | if (gpRightButtonFrames === 0) focusRomList(); 115 | } 116 | else if (index === gpAButton && controllerInputFocus) { 117 | gpAButtonFrames++; 118 | if (gpAButtonFrames === 0) { 119 | 120 | // Launch emulator if ROM is selected 121 | if ($(document.activeElement).hasClass('rom')) { 122 | onGamepadConfirmOverRom($(document.activeElement)); 123 | } 124 | // Load and go to to ROM list if system list is currently active 125 | else if ($(document.activeElement).hasClass('system')) { 126 | changeSystemFromSystemsList($(document.activeElement)); 127 | focusRomList(); 128 | } 129 | } 130 | } 131 | else if (index === gpBButton && controllerInputFocus) { 132 | gpBButtonFrames++; 133 | if (gpBButtonFrames === 0) { 134 | 135 | // Go back to system list if ROM is selected 136 | if ($(document.activeElement).hasClass('rom')) { 137 | focusSystem(); 138 | } 139 | } 140 | } 141 | else if (index === gpLButton && controllerInputFocus) { 142 | gpLButtonFrames++; 143 | if (gpLButtonFrames === 0) { 144 | skipBack(); 145 | } 146 | } 147 | else if (index === gpRButton && controllerInputFocus) { 148 | gpRButtonFrames++; 149 | if (gpRButtonFrames === 0) { 150 | skipForward(); 151 | } 152 | } 153 | } 154 | 155 | if (!button.pressed) { 156 | if (index === gpUpButton) { 157 | if (gpUpButtonFrames > -1) gpUpButtonFrames = -1; 158 | } 159 | else if (index === gpDownButton) { 160 | if (gpDownButtonFrames > -1) gpDownButtonFrames = -1; 161 | } 162 | else if (index === gpLeftButton) { 163 | if (gpLeftButtonFrames > -1) gpLeftButtonFrames = -1; 164 | } 165 | else if (index === gpRightButton) { 166 | if (gpRightButtonFrames > -1) gpRightButtonFrames = -1; 167 | } 168 | else if (index === gpAButton) { 169 | if (gpAButtonFrames > -1) gpAButtonFrames = -1; 170 | } 171 | else if (index === gpBButton) { 172 | if (gpBButtonFrames > -1) gpBButtonFrames = -1; 173 | } 174 | else if (index === gpLButton) { 175 | if (gpLButtonFrames > -1) gpLButtonFrames = -1; 176 | } 177 | else if (index === gpRButton) { 178 | if (gpRButtonFrames > -1) gpRButtonFrames = -1; 179 | } 180 | } 181 | }) 182 | 183 | // Force Quit : Final Step 3 -> Check the forceQuitCheckArray - If all true, quit emulator 184 | if (controllerForceQuit) { 185 | let forceQuitEmulator = true; 186 | for (let i = 0; i < forceQuitButtons.length; ++i) { 187 | if (forceQuitCheck[i] === false) { 188 | forceQuitEmulator = false; 189 | } 190 | } 191 | if (forceQuitEmulator) { 192 | if (forceQuitButtons.length > 1) { 193 | killEmulator(); 194 | } 195 | } 196 | } 197 | 198 | /* 199 | gamepad.axes.forEach((axe, index) => { 200 | if (axe != 0){ 201 | 202 | } 203 | }) 204 | */ 205 | 206 | } else { 207 | if (controllerStyle) disableControllerStyle(); 208 | } 209 | windowRaf(controllerUpdate); 210 | } 211 | 212 | function clearControllerMapping() { 213 | $('.controller_mapping').each(function() { 214 | $(this).val(''); 215 | }); 216 | } 217 | 218 | var controllerInputFocus = true; 219 | 220 | function controllerInputBlurred() { 221 | controllerInputFocus = false; 222 | } 223 | 224 | function controllerInputFocused() { 225 | controllerInputFocus = true; 226 | } 227 | 228 | var lastSystemSelected = null; 229 | var lastRomSelected = null; 230 | 231 | function focusSystem() { 232 | if (editMode) return; 233 | if ($(document.activeElement).hasClass('rom')) { 234 | lastRomSelected = $(document.activeElement); 235 | if (lastSystemSelected !== null) { 236 | lastSystemSelected.focus(); 237 | } else { 238 | $('.system').first().focus(); 239 | } 240 | } 241 | } 242 | 243 | function focusRomList() { 244 | if (editMode) return; 245 | if ($(document.activeElement).hasClass('system')) { 246 | lastSystemSelected = $(document.activeElement); 247 | if (lastRomSelected !== null) { 248 | lastRomSelected.focus(); 249 | if ($(document.activeElement).hasClass('system')) { 250 | $('.rom').first().focus(); 251 | } 252 | } else { 253 | if ($('.rom').first().length > 0) $('.rom').first().focus(); 254 | } 255 | } 256 | } 257 | 258 | function focusNext(smooth) { 259 | if (editMode) return; 260 | $(document.activeElement).next().focus(); 261 | document.activeElement.scrollIntoView({ 262 | behavior: (smooth ? 'smooth' : 'auto'), 263 | block: 'center', 264 | inline: 'center' 265 | }); 266 | } 267 | 268 | function focusPrevious(smooth) { 269 | if (editMode) return; 270 | $(document.activeElement).prev().focus(); 271 | document.activeElement.scrollIntoView({ 272 | behavior: (smooth ? 'smooth' : 'auto'), 273 | block: 'center', 274 | inline: 'center' 275 | }); 276 | } 277 | 278 | function skipForward() { 279 | if (editMode) return; 280 | if ($(document.activeElement).hasClass('rom')) { 281 | let firstLetter = $(document.activeElement).text().charAt(0); 282 | $('.rom').each(function() { 283 | if ($(this).text().charAt(0) > firstLetter) { 284 | $(this).focus(); 285 | return false; 286 | } 287 | }); 288 | } 289 | } 290 | 291 | function skipBack() { 292 | if (editMode) return; 293 | if ($(document.activeElement).hasClass('rom')) { 294 | let firstLetter = $(document.activeElement).text().charAt(0); 295 | $($('.rom').get().reverse()).each(function() { 296 | if ($(this).text().charAt(0) < firstLetter) { 297 | $(this).focus(); 298 | return false; 299 | } 300 | }); 301 | } 302 | } 303 | 304 | var controllerStyle = false; 305 | 306 | function enableControllerStyle() { 307 | console.log("controller-input : controller style enabled"); 308 | $('link[href="./controllerInput.css"]').prop('disabled', false); 309 | controllerStyle = true; 310 | if (!$(document.activeElement).hasClass('system') && !$(document.activeElement).hasClass('rom')) { 311 | if (!editMode) $('.system').first().focus(); 312 | // console.log($(document.activeElement)); 313 | } 314 | } 315 | 316 | function disableControllerStyle() { 317 | console.log("controller-input : controller style disabled"); 318 | $('link[href="./controllerInput.css"]').prop('disabled', true); 319 | controllerStyle = false; 320 | } -------------------------------------------------------------------------------- /app/theme.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: roboto-med; 3 | src: url(./Roboto-Medium.ttf); 4 | } 5 | 6 | @font-face { 7 | font-family: roboto-lite; 8 | src: url(./Roboto-Light.ttf); 9 | } 10 | 11 | @font-face { 12 | font-family: roboto-blk; 13 | src: url(./Roboto-Black.ttf); 14 | } 15 | 16 | @font-face { 17 | font-family: notojp-reg; 18 | src: url(./NotoSansJP-Regular.otf); 19 | } 20 | 21 | *, *::after, *::before { 22 | -webkit-user-select: none; 23 | } 24 | 25 | html, body { 26 | margin: 0; 27 | padding: 0; 28 | color: white; 29 | font-family: roboto-lite; 30 | text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.5); 31 | } 32 | 33 | #resize_area { 34 | display: grid; 35 | grid-template-rows: 32px max-content; 36 | 37 | -webkit-app-region: no-drag; 38 | box-sizing: border-box; 39 | 40 | position: absolute; 41 | top: 0; 42 | left: 0; 43 | right: 0; 44 | bottom: 0; 45 | background: transparent; 46 | } 47 | 48 | .background { 49 | position: absolute; 50 | top: 0; 51 | left: 0; 52 | right: 0; 53 | bottom: 0; 54 | overflow: hidden; 55 | } 56 | 57 | #foreground0 { 58 | /*backdrop-filter: blur(4px);*/ 59 | display: none; 60 | opacity: 0.5; 61 | } 62 | 63 | #background0 { 64 | background-color: grey; 65 | } 66 | 67 | #background1 { 68 | background: linear-gradient(0deg, rgba(255,43,181,1) 0%, rgba(63,255,253,1) 100%); 69 | opacity: 0.0; 70 | } 71 | 72 | #background2 { 73 | background: linear-gradient(0deg, rgba(255,43,181,1) 0%, rgba(63,255,253,1) 100%); 74 | opacity: 0.0; 75 | } 76 | 77 | #titlebar { 78 | -webkit-app-region: drag; 79 | padding: 0 0 0 12px; 80 | margin: 4px; 81 | height: 100%; 82 | line-height: 32px; 83 | background-color: rgba(0, 0, 0, 0.2); 84 | box-sizing: border-box; 85 | font-family: roboto-med; 86 | font-size: 13px; 87 | position: relative; 88 | } 89 | 90 | .titlebar_control { 91 | position: absolute; 92 | height: 32px; 93 | width: 48px; 94 | line-height: 26px; 95 | text-align: center; 96 | box-sizing: border-box; 97 | -webkit-app-region: no-drag; 98 | background-color: rgba( 0, 0, 0, 0.2); 99 | font-size: 10px; 100 | text-shadow: none; 101 | outline: none; 102 | border: 0; 103 | opacity: 0.6; 104 | } 105 | 106 | .titlebar_control:hover { 107 | background-color: rgba( 0, 0, 0, 0.4); 108 | } 109 | 110 | #titlebar_name { 111 | position: absolute; 112 | text-shadow: none; 113 | color: rgba(0, 0, 0, 0.2); 114 | font-size: 18px; 115 | top: 4px; 116 | } 117 | 118 | #titlebar_close { 119 | right: 0; 120 | top: 0; 121 | font-family: notojp-reg; 122 | } 123 | 124 | #titlebar_close:hover { 125 | background-color: #CF3434; 126 | color: white; 127 | } 128 | 129 | #titlebar_max { 130 | right: 48px; 131 | top: 0; 132 | font-family: notojp-reg; 133 | } 134 | 135 | #titlebar_min { 136 | right: 96px; 137 | top: 0; 138 | font-family: notojp-reg; 139 | } 140 | 141 | #titlebar_settings { 142 | right: 144px; 143 | top: 0; 144 | font-size: 9px; 145 | } 146 | 147 | #titlebar_settings:hover { 148 | cursor: pointer; 149 | } 150 | 151 | .content_wrapper { 152 | position: absolute; 153 | top: 36px; 154 | left: 4px; 155 | right: 4px; 156 | bottom: 4px; 157 | 158 | display: grid; 159 | grid-template-columns: max-content minmax(1px, 1fr); 160 | padding: 12px; 161 | box-sizing: border-box; 162 | border: 8px solid rgba(0, 0, 0, 0.2); 163 | -webkit-app-region: drag; 164 | } 165 | 166 | #wrapper { 167 | 168 | } 169 | 170 | #wrapper_settings { 171 | -webkit-app-region: no-drag; 172 | position: absolute; 173 | top: 36px; 174 | left: 4px; 175 | right: 4px; 176 | bottom: 4px; 177 | overflow-y: scroll; 178 | border: 8px solid rgba(0, 0, 0, 0.2); 179 | } 180 | 181 | #wrapper_settings::-webkit-scrollbar { 182 | width: 16px; 183 | } 184 | 185 | #wrapper_settings::-webkit-scrollbar-track { 186 | background: rgba(0, 0, 0, 0.2); 187 | } 188 | 189 | #wrapper_settings::-webkit-scrollbar-thumb { 190 | background: rgba(0, 0, 0, 0.2); 191 | } 192 | 193 | #wrapper_settings::-webkit-scrollbar-thumb:hover { 194 | background: rgba(0, 0, 0, 0.4); 195 | } 196 | 197 | #systems { 198 | display: grid; 199 | grid-template-columns: 64px; 200 | grid-auto-rows: 64px; 201 | grid-gap: 2px; 202 | overflow: hidden; 203 | overflow-y: scroll; 204 | -webkit-app-region: no-drag; 205 | will-change: transform; 206 | margin-right: 6px; 207 | } 208 | 209 | #systems::-webkit-scrollbar { 210 | width: 0px; 211 | } 212 | 213 | #sys_empty { 214 | background-color: rgb(0, 0, 0, 0.2); 215 | text-align: center; 216 | line-height: 84px; 217 | border: 1px solid rgba(0, 0, 0, 0); 218 | -webkit-app-region: no-drag; 219 | box-sizing: border-box; 220 | height: 64px; 221 | display: none; 222 | } 223 | 224 | #sys_empty:hover { 225 | cursor: pointer; 226 | background-color: rgb(0, 0, 0, 0.4); 227 | border: 1px solid white; 228 | } 229 | 230 | .system { 231 | background-color: rgb(0, 0, 0, 0.2); 232 | text-align: center; 233 | line-height: 83px; 234 | border: 1px solid rgba(0, 0, 0, 0); 235 | -webkit-app-region: no-drag; 236 | height: 64px; 237 | box-sizing: border-box; 238 | } 239 | 240 | .system:first-child { 241 | margin-top: 0; 242 | } 243 | 244 | .system:last-child { 245 | margin-bottom: 0; 246 | } 247 | 248 | .system:hover { 249 | cursor: pointer; 250 | background-color: rgb(0, 0, 0, 0.4); 251 | border: 1px solid white; 252 | } 253 | 254 | .system:focus { 255 | outline: none; 256 | } 257 | 258 | .system_icon { 259 | image-rendering: crisp-edges; 260 | width: 32px; 261 | height:32px; 262 | /*image-rendering: pixelated;*/ 263 | filter: drop-shadow(0 4px 0 rgb(0, 0, 0, 0.1)); 264 | } 265 | 266 | .settings_tab { 267 | height: 36px; 268 | line-height: 36px; 269 | text-align: center; 270 | background-color: rgba(0, 0, 0, 0.2); 271 | font-size: 13px; 272 | /*border-top-right-radius: 16px; 273 | border-top-left-radius: 16px;*/ 274 | } 275 | 276 | .settings_tab_blur { 277 | background-color: rgba(0, 0, 0, 0.4); 278 | } 279 | 280 | .settings_tab_blur:hover { 281 | background-color: rgba(0, 0, 0, 0.3); 282 | cursor: pointer; 283 | } 284 | 285 | #settings_wrapper { 286 | -webkit-app-region: no-drag; 287 | overflow-y: scroll; 288 | } 289 | 290 | #settings_wrapper::-webkit-scrollbar { 291 | width: 16px; 292 | } 293 | 294 | #settings_wrapper::-webkit-scrollbar-track { 295 | background: rgba(0, 0, 0, 0.2); 296 | } 297 | 298 | #settings_wrapper::-webkit-scrollbar-thumb { 299 | background: rgba(0, 0, 0, 0.2); 300 | } 301 | 302 | #settings_wrapper::-webkit-scrollbar-thumb:hover { 303 | background: rgba(0, 0, 0, 0.4); 304 | } 305 | 306 | #settings_editor { 307 | margin-right: 2px; 308 | } 309 | 310 | #general_editor { 311 | margin-right: 2px; 312 | } 313 | 314 | .controller_mapping { 315 | height: 28px; 316 | width: 28px; 317 | box-sizing: border-box; 318 | text-align: center; 319 | background: rgba(0, 0, 0, 0.2); 320 | border-radius: 6px; 321 | outline: none; 322 | border: 2px solid white; 323 | color: white; 324 | } 325 | 326 | .controller_mapping:hover { 327 | background: rgba(0, 0, 0, 0.4); 328 | } 329 | 330 | .controller_mapping::placeholder { 331 | font-family: notojp-reg; 332 | font-weight: bold; 333 | color: rgba(255, 255, 255, 0.2); 334 | } 335 | 336 | .controller_mapping:focus { 337 | background: rgba(0, 0, 0, 0.4); 338 | border: 4px solid white; 339 | } 340 | 341 | #settings_editor_message { 342 | padding: 18px 44px 18px 18px; 343 | font-size: 13px; 344 | position: relative; 345 | background-color: #CF3434; 346 | margin-bottom: 2px; 347 | box-sizing: border-box; 348 | min-height: 36px; 349 | border: 4px solid rgba(0, 0, 0, 0.2); 350 | display: none; 351 | } 352 | 353 | #settings_editor_message_close { 354 | position: absolute; 355 | top: 10px; 356 | right: 10px; 357 | background-color: rgba(0, 0, 0, 0.2); 358 | outline: none; 359 | border: none; 360 | color: white; 361 | font-size: 9px; 362 | width: 32px; 363 | height: 32px; 364 | } 365 | 366 | #settings_editor_message_close:hover { 367 | cursor: pointer; 368 | background-color: rgba(0, 0, 0, 0.4); 369 | } 370 | 371 | #settings_delete_system:hover { 372 | background-color: #CF3434 !important; 373 | } 374 | 375 | .settings_up_down { 376 | display: inline-block; 377 | height: 32px; 378 | font-size: 16px; 379 | background-color: rgba(0, 0, 0, 0.2); 380 | color: white; 381 | border: none; 382 | } 383 | 384 | .settings_add_rem { 385 | font-size: 16px; 386 | background-color: rgba(0, 0, 0, 0.2); 387 | color: white; 388 | border: 2px solid transparent; 389 | cursor: pointer; 390 | box-sizing: border-box; 391 | height: 28px; 392 | line-height: 26px; 393 | width: 28px; 394 | padding: 0; 395 | } 396 | 397 | .settings_add_rem:hover { 398 | background-color: rgba(0, 0, 0, 0.4); 399 | } 400 | 401 | .settings_add_rem:focus { 402 | border: 2px solid rgba(0, 0, 0, 0.2); 403 | outline: none; 404 | } 405 | 406 | .settings_category { 407 | background-color: rgba(0, 0, 0, 0.2); 408 | padding: 12px; 409 | box-sizing: border-box; 410 | width: 100%; 411 | } 412 | 413 | .settings_label { 414 | font-size: 13px; 415 | padding-bottom: 6px; 416 | } 417 | 418 | .settings_input { 419 | border: none; 420 | box-sizing: border-box; 421 | width: 100%; 422 | padding: 6px; 423 | outline: none; 424 | height: 28px; 425 | } 426 | 427 | .settings_checkbox { 428 | font-size: 16px; 429 | background-color: rgba(0, 0, 0, 0.2); 430 | color: white; 431 | border: none; 432 | cursor: pointer; 433 | box-sizing: border-box; 434 | height: 28px; 435 | width: 28px; 436 | line-height: 24px; 437 | border: 2px solid rgba(0, 0, 0, 0.2); 438 | } 439 | 440 | .settings_checkbox:hover { 441 | background-color: rgba(0, 0, 0, 0.4); 442 | } 443 | 444 | .settings_checkbox:focus { 445 | outline: none; 446 | } 447 | 448 | .settings_currently_editing { 449 | border-radius: 14px; 450 | text-align: center; 451 | } 452 | 453 | .settings_css_gradient_preset { 454 | height: 26px; 455 | } 456 | 457 | .settings_css_gradient_preset:hover { 458 | cursor: pointer; 459 | box-sizing: border-box; 460 | } 461 | 462 | #roms_wrapper { 463 | grid-template-columns: 100%; 464 | grid-auto-rows: minmax(1px, 1fr) 42px; 465 | max-height: 100%; 466 | overflow: hidden; 467 | } 468 | 469 | #roms_error_message { 470 | padding: 18px 44px 18px 18px; 471 | font-size: 13px; 472 | position: relative; 473 | background-color: #CF3434; 474 | margin-bottom: 2px; 475 | box-sizing: border-box; 476 | min-height: 36px; 477 | border: 4px solid rgba(0, 0, 0, 0.2); 478 | -webkit-app-region: no-drag; 479 | display: none; 480 | white-space: pre-line; 481 | } 482 | 483 | #roms_error_message_close { 484 | position: absolute; 485 | top: 10px; 486 | right: 10px; 487 | background-color: rgba(0, 0, 0, 0.2); 488 | outline: none; 489 | border: none; 490 | color: white; 491 | font-size: 9px; 492 | width: 32px; 493 | height: 32px; 494 | } 495 | 496 | #roms_error_message_close:hover { 497 | cursor: pointer; 498 | background-color: rgba(0, 0, 0, 0.4); 499 | } 500 | 501 | #roms_container { 502 | /* 503 | display: grid; 504 | grid-template-rows: auto 1fr auto; 505 | grid-template-columns: 1fr; 506 | margin: 6px 6px 6px 0; 507 | */ 508 | margin-bottom: 6px; 509 | overflow: hidden; 510 | overflow-y: scroll; 511 | -webkit-app-region: no-drag; 512 | } 513 | 514 | #roms_container::-webkit-scrollbar { 515 | width: 16px; 516 | } 517 | 518 | #roms_container::-webkit-scrollbar-track { 519 | background: rgba(0, 0, 0, 0.2); 520 | } 521 | 522 | #roms_container::-webkit-scrollbar-thumb { 523 | background: rgba(0, 0, 0, 0.2); 524 | } 525 | 526 | #roms_container::-webkit-scrollbar-thumb:hover { 527 | background: rgba(0, 0, 0, 0.4); 528 | } 529 | 530 | #roms { 531 | border-bottom: 2px solid rgba(0, 0, 0, 0.2); 532 | /*max-height: 100%;*/ 533 | transform: translate3d(0, 0, 0); 534 | display: table; 535 | margin-top: -2px; 536 | margin-bottom: -2px; 537 | border-spacing: 0 2px; 538 | border-collapse: separate; 539 | width: 100%; 540 | table-layout: fixed; 541 | /*-webkit-mask: linear-gradient(90deg, rgba(0,0,0,1) 85%, rgba(0,0,0,0) 100%);*/ 542 | } 543 | 544 | #roms > tr:first-child > td { 545 | 546 | } 547 | 548 | .rom { 549 | height: 32px; 550 | box-sizing: border-box; 551 | font-size: 13px; 552 | margin-right: 1px; 553 | display: table-row; 554 | table-layout: fixed; 555 | } 556 | 557 | 558 | .rom > td { 559 | overflow: hidden; 560 | white-space: nowrap; 561 | padding-left: 12px; 562 | padding-right: 12px; 563 | border: 1px solid transparent; 564 | background: linear-gradient(315deg, rgba(0,0,0,0.0) 0%, rgba(0,0,0,0.2) 100%); 565 | line-height: 32px; 566 | box-sizing: border-box; 567 | } 568 | 569 | .rom > td:hover { 570 | cursor: pointer; 571 | border: 1px solid white; 572 | background: rgba(0,0,0,0.4); 573 | } 574 | 575 | .rom:focus { 576 | outline: none; 577 | } 578 | 579 | #search_area { 580 | position: relative; 581 | } 582 | #search_bar_wrapper { 583 | display: inline-block; 584 | width: 100%; 585 | } 586 | 587 | #search_bar { 588 | width: 100%; 589 | height: 100%; 590 | box-sizing: border-box; 591 | border: none; 592 | border-bottom: 2px solid rgba(0, 0, 0, 0.2); 593 | background-color: rgba(0, 0, 0, 0.2); 594 | color: white; 595 | font-family: roboto-med; 596 | text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.5); 597 | -webkit-app-region: no-drag; 598 | padding: 0 0 0 12px; 599 | } 600 | 601 | #search_bar:hover { 602 | border: 1px solid white; 603 | background-color: rgba(0, 0, 0, 0.4); 604 | padding-left: 10px; 605 | } 606 | 607 | #search_bar:focus { 608 | padding-left: 10px; 609 | outline: none; 610 | background-color: rgba(0, 0, 0, 0.2); 611 | border: 1px solid white; 612 | } 613 | 614 | #search_bar::placeholder { 615 | color: rgba(0, 0, 0, 0.2); 616 | text-shadow: none; 617 | } 618 | 619 | #clear_search_input { 620 | border: 1px solid rgba(0, 0, 0, 0.2); 621 | padding: 4.5px; 622 | padding-left: 6px; 623 | font-size: 8px; 624 | font-family: roboto-blk; 625 | color: rgba(0, 0, 0, 0.2); 626 | text-shadow: none; 627 | letter-spacing: 2px; 628 | background-color: transparent; 629 | position: absolute; 630 | top: 6px; 631 | right: 6px; 632 | height: 28px; 633 | width: 64px; 634 | } 635 | 636 | #clear_search_input:hover { 637 | cursor: pointer; 638 | color: white; 639 | border: 1px solid white; 640 | background-color: rgba(0, 0, 0, 0.2); 641 | } 642 | 643 | #clear_search_input:focus { 644 | outline: none; 645 | } 646 | 647 | @keyframes spinner { 648 | to {transform: rotate(360deg);} 649 | } 650 | 651 | .spinner:before { 652 | content: ''; 653 | box-sizing: border-box; 654 | position: absolute; 655 | top: 50%; 656 | left: 50%; 657 | width: 64px; 658 | height: 64px; 659 | margin-top: -32px; 660 | margin-left: -32px; 661 | border-radius: 50%; 662 | border: 8px solid rgba(0, 0, 0, 0.4); 663 | border-top-color: rgba(0, 0, 0, 0.8); 664 | animation: spinner 0.7s linear infinite; 665 | } 666 | 667 | a { 668 | color: #97CEFF; 669 | text-decoration: none; 670 | } 671 | 672 | a:visited { 673 | color: #97CEFF; 674 | text-decoration: none; 675 | } -------------------------------------------------------------------------------- /app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ROM Launcher 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 |
20 |
21 |
22 |
ROM Launcher
23 | 24 | 25 | 26 | 27 |
28 |
29 |
30 | 31 |
32 |
33 | 34 |
35 | DEBUG_MESSAGE 36 | 37 |
38 | 39 |
40 |
41 | 42 |
43 |
44 | 45 |
46 | 47 | 48 | 49 | 50 |
51 |
52 | 287 |
288 |
289 |
290 |
291 |
292 | 293 | -------------------------------------------------------------------------------- /app/img/controllerMapping.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/logic.js: -------------------------------------------------------------------------------- 1 | $(document).ready(() => { 2 | 3 | // Upgrade settings file to latest version 4 | upgradeSettings(); 5 | 6 | // Load settings, systems, configuration editor 7 | initializeLauncher(); 8 | 9 | $(".titlebar_control").mouseover(function() { 10 | $(this).stop(); 11 | $(this).animate({"opacity" : "1.0"}, 200); 12 | }); 13 | 14 | $(".titlebar_control").mouseleave(function() { 15 | $(this).stop(); 16 | $(this).animate({"opacity" : "0.6"}, 200); 17 | }); 18 | 19 | //$('#systems').disableSelection(); 20 | }); 21 | 22 | 23 | var spawn = require('child_process').spawn; 24 | var emulator; 25 | 26 | function launchEmulator(romInfo) { 27 | // emulator 28 | // args 29 | // cwd 30 | 31 | let spawnEmulator = ''; 32 | 33 | let addedEmulator = false; 34 | try { 35 | let stat = fs.statSync(romInfo.emulator.replace(/"/g, '')); 36 | if (stat.isFile()) { 37 | console.log('launch-emulator : resolved emulator path...'); 38 | spawnEmulator = path.resolve(romInfo.emulator.replace(/"/g, '')); 39 | addedEmulator = true; 40 | } 41 | } catch(e) { 42 | // errors here are expected whenever a non-path emulator command is given 43 | } 44 | if (!addedEmulator) { 45 | console.log('launch-emulator : failed to resolve emulator path, add as command instead...'); 46 | spawnEmulator = romInfo.emulator.replace(/"/g, ''); 47 | } 48 | console.log('launch-emulator : emulator path ' + spawnEmulator); 49 | 50 | let spawnArgs = []; 51 | 52 | for (var i = 0; i < romInfo.args.length; ++i) { 53 | let added = false; 54 | try { 55 | let stat = fs.statSync(romInfo.args[i].replace(/"/g, '')); 56 | if (stat.isFile()) { 57 | console.log('launch-emulator : convert emulator argument file path...'); 58 | spawnArgs.push(path.resolve(romInfo.args[i].replace(/"/g, ''))); 59 | console.log('launch-emulator : converted to ' + spawnArgs[i]); 60 | added = true; 61 | } 62 | if (stat.isDirectory()) { 63 | console.log('launch-emulator : convert emulator argument directory...'); 64 | spawnArgs.push(path.resolve(romInfo.args[i].replace(/"/g, ''))); 65 | console.log('launch-emulator : converted to ' + spawnArgs[i]); 66 | added = true; 67 | } 68 | } 69 | catch(e) { 70 | // errors here are expected whenever a non-path/directory argument is given, just ignore it 71 | } 72 | if (!added) { 73 | spawnArgs.push(romInfo.args[i]); 74 | } 75 | } 76 | 77 | console.log('launch-emulator : try running ' + spawnEmulator + ' ' + spawnArgs.join()); 78 | 79 | displayLoadingScreen(); 80 | emulator = spawn(spawnEmulator, spawnArgs); 81 | emulator.on('error', onEmulatorError); 82 | } 83 | 84 | function onEmulatorError(err) { 85 | showRomListError("Failed to launch emulator. Please review your settings."); 86 | $('#foreground0').css({'display':'none'}); 87 | console.log('on-emulator-error : ' + err); 88 | } 89 | 90 | function killEmulator() { 91 | if (emulator) { 92 | console.log('kill-emulator : attempting to kill emulator'); 93 | emulator.kill(); 94 | } 95 | } 96 | 97 | function showRomListError(message) { 98 | $('#roms_wrapper').css({ 99 | 'grid-auto-rows' : 'min-content minmax(1px, 1fr) 42px' 100 | }); 101 | $('#roms_error_message').css({ 102 | 'display' : 'block' 103 | }) 104 | $('#roms_error_message').get(0).childNodes[0].nodeValue = message; 105 | } 106 | 107 | $(document).on('click', '#roms_error_message_close', function() { 108 | hideRomListError(); 109 | }); 110 | 111 | function hideRomListError() { 112 | $('#roms_wrapper').css({ 113 | 'grid-auto-rows' : 'minmax(1px, 1fr) 42px' 114 | }); 115 | $('#roms_error_message').css({ 116 | 'display' : 'none' 117 | }) 118 | } 119 | 120 | function displayLoadingScreen() { 121 | $('#foreground0').css({'display':'block'}); 122 | $('#foreground0').css({'opacity':0.5}); 123 | $('#foreground0').css({'background':$('#background'+activeBackground).css('background')}); 124 | setTimeout(function() { 125 | $('#foreground0').animate({'opacity':0}, 350); 126 | }, 1500); 127 | setTimeout(function() { 128 | $('#foreground0').css({'display':'none'}); 129 | }, 1850); 130 | } 131 | 132 | function writeError(message) { 133 | $('#roms').append(''+message+''); 134 | } 135 | 136 | $(document).on('click', '.rom', function(){ 137 | launchEmulatorFromRomList($(this)); 138 | }); 139 | 140 | function onGamepadConfirmOverRom(element) { 141 | launchEmulatorFromRomList(element); 142 | } 143 | 144 | function launchEmulatorFromRomList(element) { 145 | let fadeMin = 0.75; 146 | let fadeMax = 1.0; 147 | let fadeFreq = 100; 148 | element.stop(); 149 | element.fadeTo(fadeFreq, fadeMin).fadeTo(fadeFreq, fadeMax).fadeTo(fadeFreq, fadeMin).fadeTo(fadeFreq, fadeMax); 150 | let romInfo = element.data('rom_info'); 151 | launchEmulator(romInfo); 152 | } 153 | 154 | $(document).on('click', '#settings_editor_message_close', function() { 155 | $('#settings_editor_message').css({'display' : 'none'}); 156 | }); 157 | 158 | function showEditorMessage(message, isWarning) { 159 | if (isWarning) { 160 | $('#settings_editor_message').css({'background-color' : '#CF3434'}); 161 | } 162 | else { 163 | $('#settings_editor_message').css({'background-color' : '#34cf81'}); 164 | } 165 | $('#settings_editor_message').css({'display' : 'block'}); 166 | 167 | $('#settings_editor_message').get(0).childNodes[0].nodeValue = message; 168 | } 169 | 170 | $(document).on('click', '#settings_save_system', function() { 171 | saveDelCurrentSettings(true); 172 | }); 173 | 174 | $(document).on('click', '#settings_delete_system', function() { 175 | saveDelCurrentSettings(false); 176 | }); 177 | 178 | $(document).on('click', '.settings_css_gradient_preset', function() { 179 | $(this).parent().siblings('input').val($(this).attr('style').replace('background: ', '').replace(';', '')); 180 | }); 181 | 182 | $(document).on('click', '#display_system_settings', function() { 183 | if ($(this).hasClass('settings_tab_blur')) { 184 | $('#display_general_settings').addClass('settings_tab_blur'); 185 | $(this).removeClass('settings_tab_blur'); 186 | 187 | $('#general_editor').css({'display' : 'none'}); 188 | $('#settings_editor').css({'display' : 'block'}); 189 | 190 | let editorMessage = $('#settings_editor_message'); 191 | editorMessage.css({'display' : 'none'}); 192 | editorMessage.detach(); 193 | $('#settings_system_save_options').before(editorMessage); 194 | } 195 | }); 196 | 197 | $(document).on('click', '#display_general_settings', function() { 198 | if ($(this).hasClass('settings_tab_blur')) { 199 | $('#display_system_settings').addClass('settings_tab_blur'); 200 | $(this).removeClass('settings_tab_blur'); 201 | 202 | $('#general_editor').css({'display' : 'block'}); 203 | $('#settings_editor').css({'display' : 'none'}); 204 | 205 | let editorMessage = $('#settings_editor_message'); 206 | editorMessage.css({'display' : 'none'}); 207 | editorMessage.detach(); 208 | $('#settings_general_save_options').before(editorMessage); 209 | } 210 | }); 211 | 212 | $(document).on('click', '#titlebar_close', function(){ 213 | var window = remote.getCurrentWindow(); 214 | window.close(); 215 | }); 216 | 217 | $(document).on('click', '#titlebar_max', function(){ 218 | var window = remote.getCurrentWindow(); 219 | if (!window.isMaximized()) { 220 | window.maximize(); 221 | } else { 222 | window.unmaximize(); 223 | } 224 | }); 225 | 226 | $(document).on('click', '#titlebar_min', function(){ 227 | var window = remote.getCurrentWindow(); 228 | window.minimize(); 229 | }); 230 | 231 | 232 | $(document).on('click', '.settings_checkbox', function() { 233 | if ($(this).text() === '') { 234 | $(this).text('✓'); 235 | } 236 | else { 237 | $(this).text(''); 238 | } 239 | }); 240 | 241 | var editMode = false; 242 | $(document).on('click', '#titlebar_settings', function(){ 243 | if (!editMode) { 244 | enterSettingsEditor(); 245 | } 246 | else { 247 | leaveSettingsEditor(); 248 | } 249 | }); 250 | 251 | function enterSettingsEditor() { 252 | $('#roms_wrapper').css({'display':'none'}); 253 | $('#settings_wrapper').css({'display':'grid'}); 254 | $('#titlebar_settings').html('BACK'); 255 | $('#sys_empty').css({'display':'block'}); 256 | changeTitlebarName('Configuration Editor', false); 257 | editMode = true; 258 | /*$('#systems').sortable(); 259 | $('#systems').sortable( "option", "disabled", false ); 260 | $('#systems').sortable({ axis: 'y' });*/ 261 | 262 | // loadSettingsEditor(); 263 | } 264 | 265 | function leaveSettingsEditor() { 266 | $('#roms_wrapper').css({'display':'grid'}); 267 | $('#settings_wrapper').css({'display':'none'}); 268 | $('#titlebar_settings').html('EDIT'); 269 | $('#sys_empty').css({'display':'none'}); 270 | changeTitlebarName(bufferedTitlebarName === '' ? 'ROM Launcher' : bufferedTitlebarName, false); 271 | editMode = false; 272 | /*$('#systems').sortable('disable');*/ 273 | } 274 | 275 | $(document).on('click', '.settings_input_add', function(){ 276 | let parent = $(this).parent().clone(); 277 | parent.find('input').val(''); 278 | 279 | // if this is the rom directory input, reset recursive option to default 280 | if (parent.find('.settings_input_directory_recursive')) { 281 | parent.find('.settings_input_directory_recursive').text(recursive_off); 282 | parent.find('.settings_input_directory_recursive').attr('title', recursive_off_title); 283 | } 284 | 285 | $(this).parent().parent().append(parent); 286 | }); 287 | 288 | $(document).on('click', '.settings_input_rem', function(){ 289 | if ($(this).parent().parent().find('input').length >= 2) { 290 | $(this).parent().remove(); 291 | } 292 | // if this is the last input line, don't delete element, but value instead 293 | else { 294 | $(this).parent().parent().find('input').val(''); 295 | } 296 | }); 297 | 298 | function saveDelCurrentSettings(save) { 299 | try { 300 | settingsFile = fs.readFileSync(exePath+'/../settings.json') 301 | } 302 | catch (e) { 303 | console.log("save-del-settings : couldn't load settings.json!") 304 | } 305 | if (typeof settingsFile !== 'undefined') { 306 | // Found settings, store in settings var 307 | console.log("save-del-settings : load existing settings.json") 308 | settings = JSON.parse(settingsFile); 309 | console.log("save-del-settings : write backup to settings_backup.json") 310 | try { 311 | fs.writeFileSync( exePath+'/../settings_backup.json', JSON.stringify(settings, null, 4), 'utf-8' ); 312 | } 313 | catch (e) { 314 | console.log("save-del-settings : failed to write backup file!") 315 | } 316 | } else { 317 | // otherwise, create empty object in setting var 318 | console.log("save-del-settings : creating new empty configuration") 319 | settings = new Object(); 320 | settings.general = {}; 321 | settings.general.settingsVersion = currentSettingsVersion; 322 | settings.systems = []; 323 | } 324 | 325 | // Parse settings to userSettings, current ID to userSettingsId (empty if new system) 326 | if (!parseUserSettings(save)) { 327 | return; 328 | } 329 | 330 | // Print new settings for new/ existing system 331 | console.log(userSettings); 332 | 333 | // Write system changes to memory 334 | if (userSettingsId === '') { 335 | // New system 336 | if (save) settings.systems.push(userSettings); 337 | } else { 338 | // Changes to existing system 339 | let systemIndex = settings.systems.findIndex(function(system) { 340 | return userSettingsId == system.systemId; 341 | }); 342 | if (save) settings.systems[systemIndex] = userSettings; 343 | else settings.systems.splice(systemIndex, 1); 344 | } 345 | 346 | // Write system changes to disk 347 | try { 348 | fs.writeFileSync( exePath+'/../settings.json', JSON.stringify(settings, null, 4), 'utf-8' ); 349 | } 350 | catch(e) { 351 | console.log("save-del-settings : failed to write to settings.json") 352 | } 353 | 354 | showEditorMessage('Changes saved! You might need to restart the launcher for the changes to take effect.', false); 355 | 356 | if (!save) { 357 | $('.settings_currently_editing').val('New System'); 358 | } 359 | 360 | initializeLauncher(); 361 | } 362 | 363 | var userSettingsId; 364 | var userSettings; 365 | 366 | // Get the changes made to an existing or new system, also give basic user feedback 367 | function parseUserSettings(save) { 368 | 369 | let settingsCurrentlyEditing = $('.settings_currently_editing').val(); 370 | 371 | if (settingsCurrentlyEditing === 'New System') { 372 | userSettingsId = ''; 373 | } else { 374 | userSettingsId = settingsCurrentlyEditing; 375 | } 376 | 377 | if (!save && settingsCurrentlyEditing === 'New System' || settingsCurrentlyEditing === '') { 378 | showEditorMessage('Cannot delete new system.', true); 379 | return false; 380 | } 381 | 382 | userSettings = new Object(); 383 | 384 | let settingsDisplayName = $('.settings_display_name').val(); 385 | 386 | if (settingsDisplayName === '') { 387 | showEditorMessage('Launcher prefix may not be empty.', true); 388 | return false; 389 | } 390 | 391 | userSettings.tabName = settingsDisplayName; 392 | 393 | let settingsUniqueId = $('.settings_unique_id').val(); 394 | 395 | if (settingsUniqueId === '') { 396 | showEditorMessage('Unique ID may not be empty.', true); 397 | return false; 398 | } else if (settingsUniqueId === 'New System') { 399 | showEditorMessage('Unique ID may not be "New System".', true); 400 | return false; 401 | } else if (settingsUniqueId.includes(' ')) { 402 | showEditorMessage('Unique ID may not include spaces.', true); 403 | return false; 404 | } 405 | 406 | userSettings.systemId = settingsUniqueId; 407 | 408 | let settingsEmuPath = $('.settings_emu_path').val(); 409 | 410 | if (settingsEmuPath === '') { 411 | showEditorMessage('Emulator path may not be empty.', true); 412 | return false; 413 | } 414 | 415 | userSettings.emuPath = settingsEmuPath; 416 | 417 | let arrayIndex; 418 | let returnWithError = ''; // This is a work-around, since return false doesn't work in .each() -- if this is non-empty after .each(), return false 419 | 420 | // If we're creating a new system, but specified ID already exists, cancel 421 | if (settingsCurrentlyEditing === ('New System')) { 422 | $('.system').each(function() { 423 | if ($(this).attr('id') === ('system_'+settingsUniqueId)) { 424 | returnWithError = 'Cannot create new system - A system with this ID already exists.'; 425 | return false; 426 | } 427 | }); 428 | } 429 | 430 | arrayIndex = 0; 431 | let settingsEmuArg = []; 432 | $('.settings_emu_arg').each(function() { 433 | let emuArg = $(this).val(); 434 | 435 | if (emuArg === '') { 436 | // This isn't an error, just skip loop on empty arguments 437 | return false; 438 | } 439 | 440 | settingsEmuArg[arrayIndex] = emuArg; 441 | arrayIndex++; 442 | }); 443 | 444 | userSettings.emuArgs = settingsEmuArg; 445 | 446 | arrayIndex = 0; 447 | let settingsRomDir = []; 448 | $('.settings_rom_dir').each(function() { 449 | let romDir = $(this).val(); 450 | let romDirRecursive = $(this).siblings('.settings_input_directory_recursive').text() === recursive_off ? false : true; 451 | 452 | if (romDir === '') { 453 | returnWithError = 'ROM directories may not be empty.'; 454 | return false; 455 | } 456 | else if (!romDir.includes('"')) { 457 | returnWithError = 'Please wrap all directories in double quotation marks "..."'; 458 | console.log(returnWithError); 459 | return false; 460 | } 461 | 462 | let romDirOptions = new Object(); 463 | romDirOptions.directory = romDir; 464 | romDirOptions.recursive = romDirRecursive; 465 | 466 | settingsRomDir[arrayIndex] = romDirOptions; 467 | arrayIndex++; 468 | }); 469 | 470 | userSettings.romPaths = settingsRomDir; 471 | 472 | arrayIndex = 0; 473 | let settingsFileType = []; 474 | $('.settings_file_type').each(function() { 475 | let fileType = $(this).val(); 476 | 477 | if (fileType.includes('.')) { 478 | returnWithError = 'Please remove any dots from file-types.'; 479 | return false; 480 | } else if (fileType === '') { 481 | returnWithError = 'ROM file-types may not be empty.'; 482 | return false; 483 | } 484 | 485 | settingsFileType[arrayIndex] = fileType; 486 | arrayIndex++; 487 | }); 488 | 489 | userSettings.fileTypes = settingsFileType; 490 | 491 | if (returnWithError !== '') { 492 | showEditorMessage(returnWithError, true); 493 | return false; 494 | } 495 | 496 | let settingsCssGradient = $('.settings_css_gradient').val(); 497 | 498 | if (settingsCssGradient.includes(';')) { 499 | showEditorMessage('Please remove the semicolon from the CSS background value.', true); 500 | return false; 501 | } else if (settingsCssGradient === '') { 502 | settingsCssGradient = "linear-gradient(0deg, rgba(59,58,83,1) 0%, rgba(137,159,163,1) 100%)"; 503 | } else if (settingsCssGradient.includes('background')) { 504 | showEditorMessage('Please enter only the value in the CSS background field (the text after the colon, exluding the final semicolon).', true); 505 | return false; 506 | } 507 | 508 | userSettings.gradient = settingsCssGradient; 509 | 510 | let settingsOrder = $('.settings_order').val(); 511 | 512 | if (isNaN(settingsOrder)) { 513 | showEditorMessage('Order needs to be a number.', true); 514 | return false; 515 | } else if (settingsOrder === '') { 516 | showEditorMessage('Order may not be empty.', true); 517 | return false; 518 | } 519 | 520 | userSettings.order = settingsOrder; 521 | 522 | return true; 523 | } 524 | 525 | $(document).on('click', '#settings_save_general', function() { 526 | saveGeneralSettings(); 527 | }); 528 | 529 | function saveGeneralSettings() { 530 | try { 531 | settingsFile = fs.readFileSync(exePath+'/../settings.json') 532 | } 533 | catch (e) { 534 | console.log("save-general-settings : couldn't load settings.json!") 535 | } 536 | if (typeof settingsFile !== 'undefined') { 537 | // Found settings, store in settings var 538 | console.log("save-general-settings : load existing settings.json") 539 | settings = JSON.parse(settingsFile); 540 | console.log("save-general-settings : write backup to settings_backup.json") 541 | try { 542 | fs.writeFileSync( exePath+'/../settings_backup.json', JSON.stringify(settings, null, 4), 'utf-8' ); 543 | } 544 | catch (e) { 545 | console.log("save-general-settings : failed to write backup file!") 546 | } 547 | } else { 548 | // otherwise, create empty object in setting var 549 | console.log("save-general-settings : creating new empty configuration") 550 | settings = new Object(); 551 | settings.general = {}; 552 | settings.general.settingsVersion = currentSettingsVersion; 553 | settings.systems = []; 554 | } 555 | 556 | // Parse general options here 557 | if ($('.settings_controller_enabled').text() !== '') { 558 | settings.general.controllerEnabled = true; 559 | } else { 560 | settings.general.controllerEnabled = false; 561 | } 562 | if ($('.settings_controller_forcequit').text() !== '') { 563 | settings.general.controllerForceQuit = true; 564 | } else { 565 | settings.general.controllerForceQuit = false; 566 | } 567 | settings.general.buttonMapping = []; 568 | $('.controller_mapping').each(function() { 569 | settings.general.buttonMapping.push($(this).val().toString()); 570 | }); 571 | 572 | 573 | // Write system changes to disk 574 | try { 575 | fs.writeFileSync( exePath+'/../settings.json', JSON.stringify(settings, null, 4), 'utf-8' ); 576 | } 577 | catch(e) { 578 | console.log("save-general-settings : failed to write to settings.json") 579 | } 580 | 581 | showEditorMessage('Changes saved! Restart the launcher for the changes to take effect.', false); 582 | } 583 | 584 | function loadGeneralSettings() { 585 | try { 586 | settingsFile = fs.readFileSync(exePath+'/../settings.json') 587 | } 588 | catch (e) { 589 | console.log("load-general-settings : couldn't load settings.json!") 590 | return; 591 | } 592 | if (typeof settingsFile !== 'undefined') { 593 | // Found settings, store in settings var 594 | console.log("load-general-settings : load existing settings.json") 595 | settings = JSON.parse(settingsFile); 596 | } 597 | 598 | if (settings.hasOwnProperty('general')) { 599 | if (settings.general.hasOwnProperty('controllerEnabled')) { 600 | $('.settings_controller_enabled').text(settings.general.controllerEnabled ? '✓' : ''); 601 | // Enable controller input 602 | controllerInputEnabled = settings.general.controllerEnabled; 603 | } 604 | if (settings.general.hasOwnProperty('controllerForceQuit')) { 605 | $('.settings_controller_forcequit').text(settings.general.controllerForceQuit ? '✓' : ''); 606 | // Enable controller force quit 607 | controllerForceQuit = settings.general.controllerForceQuit; 608 | } 609 | let mappingIndex = 0; 610 | if (settings.general.hasOwnProperty('buttonMapping')) { 611 | 612 | settings.general.buttonMapping.forEach(function(value) { 613 | let buttonValue = parseInt(value); 614 | if (!isNaN(buttonValue)) { 615 | $('.controller_mapping').eq(mappingIndex).val(buttonValue); 616 | 617 | switch(mappingIndex) { 618 | case 0: 619 | gpUpButton = buttonValue; 620 | break; 621 | case 1: 622 | gpDownButton = buttonValue; 623 | break; 624 | case 2: 625 | gpLeftButton = buttonValue; 626 | break; 627 | case 3: 628 | gpRightButton = buttonValue; 629 | break; 630 | case 4: 631 | gpAButton = buttonValue; 632 | break; 633 | case 5: 634 | gpBButton = buttonValue; 635 | break; 636 | case 6: 637 | gpLButton = buttonValue; 638 | break; 639 | case 7: 640 | gpRButton = buttonValue; 641 | break; 642 | case 8: 643 | forceQuitButtons.push(buttonValue); 644 | break; 645 | case 9: 646 | forceQuitButtons.push(buttonValue); 647 | break; 648 | case 10: 649 | forceQuitButtons.push(buttonValue); 650 | break; 651 | case 11: 652 | forceQuitButtons.push(buttonValue); 653 | break; 654 | default: 655 | break; 656 | } 657 | } 658 | mappingIndex++; 659 | }); 660 | } 661 | } 662 | } 663 | 664 | const fs = require('fs'); 665 | const path = require ('path'); 666 | const app = require('electron'); 667 | const remote = app.remote; 668 | const dialog = remote.dialog; 669 | 670 | // selectPath : select path (true) or directory (false) 671 | // title : window title 672 | // button : button label 673 | // callback : callback function 674 | function getPathDialog(jQueryElement, selectPath, title, button, callback) { 675 | dialog.showOpenDialog({ 676 | title : title, 677 | buttonLabel : button, 678 | properties: [(selectPath ? 'openFile' : 'openDirectory')], 679 | filters : [ 680 | { name : 'All Files', extensions : ['*'] } 681 | ] 682 | }, (file) => { 683 | callback(file, jQueryElement); 684 | }); 685 | } 686 | 687 | $(document).on('click', '.settings_input_get_directory', function() { 688 | 689 | getPathDialog($(this), false, "Select Directory", "Select Directory", function(file, button) { 690 | console.log(file); 691 | if (file !== undefined) { 692 | button.siblings('input').val(formatFilePath(file)); 693 | } 694 | }); 695 | }); 696 | 697 | $(document).on('click', '.settings_input_get_path', function() { 698 | 699 | getPathDialog($(this), true, "Select File", "Select File", function(file, button) { 700 | console.log(file); 701 | if (file !== undefined) { 702 | button.siblings('input').val(formatFilePath(file)); 703 | } 704 | }); 705 | }); 706 | 707 | const recursive_off = '🌜'; // emojis in code 708 | const recursive_off_title = "Half: Include Only This Folder (Default)"; 709 | const recursive_on = '🌝'; // looks about right, let's hope this doesn't break 710 | const recursive_on_title = "Full: Include All Subdirectories"; 711 | $(document).on('click', '.settings_input_directory_recursive', function() { 712 | if ($(this).text() === recursive_off) { 713 | $(this).text(recursive_on); 714 | $(this).attr('title', recursive_on_title); 715 | } 716 | else { 717 | $(this).text(recursive_off); 718 | $(this).attr('title', recursive_off_title); 719 | } 720 | }); 721 | 722 | $(document).on('click', '.settings_input_make_relative', function() { 723 | let filePath = $(this).siblings('input').val(); 724 | if (filePath !== '') { 725 | console.log("Trying to make path relative..."); 726 | console.log(filePath); 727 | console.log(exePath); 728 | filePath = path.relative(exePath.toString().replace(/"/g, ''), filePath.toString().replace(/"/g, '')); 729 | console.log(filePath); 730 | $(this).siblings('input').val(addSpaces(filePath)); 731 | } 732 | }); 733 | 734 | function formatFilePath(input) { 735 | // input = input.toString().replace(/\\/g, '/'); 736 | // anchorPath = exePath.toString().replace(/\\/g, '/'); 737 | return addSpaces(input); 738 | } 739 | 740 | // Load settings from settings.json systems entry 741 | function loadSettingsEditor(systemOptions) { 742 | 743 | let arrayIndex; 744 | 745 | let currentlyEditing = systemOptions && systemOptions.hasOwnProperty('systemId') ? systemOptions.systemId : 'New System'; 746 | $('.settings_currently_editing').val(currentlyEditing); 747 | 748 | let displayName = systemOptions && systemOptions.hasOwnProperty('tabName') ? systemOptions.tabName : ''; 749 | $('.settings_display_name').val(displayName); 750 | 751 | let uniqueId = systemOptions && systemOptions.hasOwnProperty('systemId') ? systemOptions.systemId : ''; 752 | $('.settings_unique_id').val(uniqueId); 753 | 754 | let emuPath = systemOptions && systemOptions.hasOwnProperty('emuPath') ? systemOptions.emuPath : ''; 755 | $('.settings_emu_path').val(emuPath); 756 | 757 | let emuArgs = systemOptions && systemOptions.hasOwnProperty('emuArgs') ? systemOptions.emuArgs : undefined; 758 | if (emuArgs !== undefined) { 759 | loadSettingsEditorEmptyInputRows('.settings_emu_arg', emuArgs.length); 760 | arrayIndex = 0; 761 | $('.settings_emu_arg').each(function() { 762 | $(this).val(emuArgs[arrayIndex]); 763 | arrayIndex++; 764 | }); 765 | } else { 766 | loadSettingsEditorEmptyInputRows('.settings_emu_arg', 1); 767 | } 768 | 769 | let romPaths = systemOptions && systemOptions.hasOwnProperty('romPaths') ? systemOptions.romPaths : undefined; 770 | if (romPaths !== undefined) { 771 | loadSettingsEditorEmptyInputRows('.settings_rom_dir', romPaths.length); 772 | arrayIndex = 0; 773 | $('.settings_rom_dir').each(function() { 774 | $(this).val(romPaths[arrayIndex].directory); 775 | $(this).siblings('.settings_input_directory_recursive').text(romPaths[arrayIndex].recursive ? recursive_on : recursive_off); 776 | $(this).siblings('.settings_input_directory_recursive').attr('title', romPaths[arrayIndex].recursive ? recursive_on_title : recursive_off_title); 777 | arrayIndex++; 778 | }); 779 | } else { 780 | loadSettingsEditorEmptyInputRows('.settings_rom_dir', 1); 781 | $('.settings_rom_dir').each(function() { 782 | $(this).siblings('.settings_input_directory_recursive').text(recursive_off); 783 | $(this).siblings('.settings_input_directory_recursive').attr('title', recursive_off_title); 784 | }); 785 | } 786 | 787 | let fileTypes = systemOptions && systemOptions.hasOwnProperty('fileTypes') ? systemOptions.fileTypes : undefined; 788 | if (fileTypes !== undefined) { 789 | loadSettingsEditorEmptyInputRows('.settings_file_type', fileTypes.length); 790 | arrayIndex = 0; 791 | $('.settings_file_type').each(function() { 792 | $(this).val(fileTypes[arrayIndex]); 793 | arrayIndex++; 794 | }); 795 | } else { 796 | loadSettingsEditorEmptyInputRows('.settings_file_type', 1); 797 | } 798 | 799 | let gradient = systemOptions && systemOptions.hasOwnProperty('gradient') ? systemOptions.gradient : ''; 800 | $('.settings_css_gradient').val(gradient); 801 | 802 | let order = systemOptions && systemOptions.hasOwnProperty('order') ? systemOptions.order : '0'; 803 | $('.settings_order').val(order); 804 | } 805 | 806 | // Load empty input rows for certain array settings (rom directories, file types, etc.) 807 | function loadSettingsEditorEmptyInputRows(inputClass, inputRowCount) { 808 | if (inputRowCount == 0) inputRowCount = 1; 809 | let container = $(inputClass).parent().parent(); 810 | let inputLine = $(inputClass).parent().first().clone() 811 | inputLine.find('input').val(''); 812 | container.html(''); 813 | for (let i = 0; i < inputRowCount; ++i) { 814 | container.append(inputLine.clone()); 815 | } 816 | } 817 | 818 | $(document).on('click', '.e_add', function(){ 819 | var input = $(this).parent().children('input').first().clone(); 820 | input.val(''); 821 | $(this).parent().append(input); 822 | }); 823 | 824 | $(document).on('click', '.e_rem', function(){ 825 | if ($(this).parent().children('input').length <= 1) return; 826 | var input = $(this).parent().children('input').last(); 827 | input.remove(); 828 | }); 829 | 830 | $(document).on('click', '.system', function() { 831 | changeSystemFromSystemsList($(this)); 832 | }); 833 | 834 | function changeSystemFromSystemsList(element) { 835 | systemOptions = element.data(element.attr('id')); 836 | changeSystem(systemOptions); 837 | loadSettingsEditor(systemOptions); 838 | $('#settings_editor_message').css({'display' : 'none'}); 839 | } 840 | 841 | $(document).on('click', '#sys_empty', function() { 842 | loadSettingsEditor(undefined); 843 | }); 844 | 845 | var activeBackground = 1; 846 | var lastSystem = ""; 847 | function changeSystem(systemOptions) { 848 | 849 | // Only proceed if this system wasn't already loaded 850 | if (lastSystem !== systemOptions.systemId) { 851 | 852 | // Reload ROM list 853 | loadRomList(systemOptions); 854 | // Sort ROM list 855 | sortRomList(); 856 | 857 | // Animate background color 858 | $('#background'+activeBackground).stop(); 859 | $('#background'+activeBackground).animate({ 860 | 'opacity' : 0 861 | }, 1500); 862 | if (activeBackground === 1) { 863 | activeBackground = 2; 864 | } else if (activeBackground === 2) { 865 | activeBackground = 1; 866 | } 867 | $('#background'+activeBackground).stop(); 868 | $('#background'+activeBackground).css({'background' : systemOptions.gradient}) 869 | $('#background'+activeBackground).animate({ 870 | 'opacity' : 1.0 871 | }, 1500); 872 | 873 | // Clear any active searches 874 | $('#search_bar').val(''); 875 | 876 | // Fade in ROM display 877 | $('#roms').css({'opacity' : '0'}) 878 | $('#roms').animate({ 879 | 'opacity' : '1.0' 880 | },200) 881 | 882 | if (!editMode) { 883 | bufferedTitlebarName = changeTitlebarName(systemOptions.tabName, true); 884 | } else { 885 | bufferedTitlebarName = systemOptions.tabName + ' Launcher'; 886 | } 887 | 888 | lastSystem = systemOptions.systemId; 889 | } 890 | } 891 | 892 | var bufferedTitlebarName = ''; 893 | function changeTitlebarName(launcherPrefix, showSuffix) { 894 | // Change title name to tabName Launcher 895 | $('#titlebar_name').stop().animate({'opacity':0.0}, 250).animate({'opacity':1.0}, 250); 896 | var newname = launcherPrefix + (showSuffix ? ' Launcher' : ''); 897 | setTimeout(function() { 898 | $('#titlebar_name').html(newname); 899 | }, 250); 900 | return newname; 901 | } 902 | 903 | //const settings = require('./settings.json') 904 | 905 | // const settingsFile = fs.readFileSync('./settings.json'); 906 | const exePath = path.dirname(remote.app.getPath('exe')); 907 | var settingsFile; 908 | var settings; 909 | // console.log(exePath); 910 | 911 | function initializeLauncher() { 912 | try { 913 | settingsFile = fs.readFileSync(exePath+'/../settings.json') 914 | } 915 | catch (e) { 916 | console.log("initialize-launcher : couldn't load settings.json!") 917 | } 918 | 919 | if (typeof settingsFile !== 'undefined') { 920 | settings = JSON.parse(settingsFile); 921 | } 922 | 923 | $('#systems').html(''); 924 | if (typeof settings !== 'undefined') { 925 | settings.systems.forEach(function(systemOptions) { 926 | // var iconPath = exePath.replace(/\\/g, '/'); 927 | // iconPath = iconPath.replace(/"/g, ''); 928 | // iconPath = iconPath + '/../icons/' + systemOptions.systemId + '.png'; 929 | var iconPath = './../../../icons/' + systemOptions.systemId + '.png'; 930 | 931 | // console.log(iconPath); 932 | 933 | $('#systems').append('
'); 934 | $('#system_'+systemOptions.systemId).data('system_'+systemOptions.systemId, systemOptions); 935 | }); 936 | } 937 | // $('#systems').append('
'); 938 | $('#systems').append('
'); 939 | 940 | sortSystemList(); 941 | 942 | if (editMode) { 943 | // leaveSettingsEditor(); 944 | $('#sys_empty').css({'display':'block'}); 945 | } else { 946 | loadSettingsEditor(undefined); 947 | loadGeneralSettings(); 948 | } 949 | } 950 | 951 | function loadRomList(systemOptions) { 952 | 953 | // wipe ROM display 954 | $('#roms').html(''); 955 | 956 | // sync read roms from rom-paths 957 | systemOptions.romPaths.forEach(function(pathOptions) { 958 | 959 | romPath = pathOptions.directory.replace(/\\/g, '/'); 960 | romPath = romPath.replace(/"/g, ''); 961 | 962 | try { 963 | let files = readDirectory(romPath, systemOptions.fileTypes, pathOptions.recursive); 964 | files.forEach(function(file) { 965 | appendRomToList(systemOptions, file); 966 | }); 967 | } 968 | catch(e) { 969 | console.log(e); 970 | showRomListError("Failed to read ROM directory:\n" + romPath + "\nPlease review your settings file.") 971 | } 972 | 973 | }); 974 | } 975 | 976 | var romPath; 977 | 978 | function readDirectory(dir, fileTypes, recursive) { 979 | var results = []; 980 | var list = fs.readdirSync(dir); 981 | list.forEach(function(file) { 982 | file = dir + '/' + file; 983 | var stat = fs.statSync(file); 984 | if (stat && stat.isDirectory() && recursive) { 985 | // recursion 986 | results = results.concat(readDirectory(file, fileTypes, recursive)); 987 | } else { 988 | // filter current file, add to results if valid 989 | let fileType = file.split(".").pop(); 990 | fileTypes.forEach(function(fileTypeFilter) { 991 | if (fileType.toLowerCase() === fileTypeFilter.toLowerCase()) { 992 | results.push(file); 993 | return false; 994 | } 995 | }); 996 | } 997 | }); 998 | console.log('read-directory : loaded ' + results.length + ' files to list'); 999 | return results; 1000 | } 1001 | 1002 | function appendRomToList(systemOptions, file) { 1003 | 1004 | // file type without punctuation 1005 | let fileType = file.split(".").pop(); 1006 | // file name with extension 1007 | let fullFileName = file.split(/(\\|\/)/g).pop(); 1008 | // file name without extension 1009 | let fileName = fullFileName.slice(0, -(fileType.length+1)); 1010 | 1011 | var romInfo = new Object(); 1012 | romInfo.displayName = fileName; 1013 | romInfo.emulator = addSpaces(systemOptions.emuPath); 1014 | romInfo.cwd = addSpaces(systemOptions.emuPath.replace(systemOptions.emuPath.split(/(\\|\/)/g).pop(), '')); 1015 | romInfo.args = []; 1016 | systemOptions.emuArgs.forEach(function(arg){ 1017 | if (arg === '%ROMFILE%') { 1018 | arg = addSpaces(file); 1019 | } 1020 | romInfo.args.push(arg); 1021 | }); 1022 | // if args is still empty, push only ROM-file 1023 | if (systemOptions.emuArgs.length === 0) { 1024 | romInfo.args.push(addSpaces(file)); 1025 | } 1026 | 1027 | // playlist styling 1028 | if (fileType === "m3u") { 1029 | // romInfo.displayName = "※ " + romInfo.displayName + " (Playlist)"; 1030 | romInfo.displayName = romInfo.displayName + " (Playlist)"; 1031 | } 1032 | 1033 | let entry = $(''+romInfo.displayName+''); 1034 | entry.data('rom_info', romInfo); 1035 | $('#roms').append(entry); 1036 | } 1037 | 1038 | 1039 | function addSpaces(path) { 1040 | 1041 | if (path.toString().charAt(0) !== '"') { 1042 | path = '"'+path; 1043 | } 1044 | if (path.toString().charAt(path.length-1) !== '"') { 1045 | path = path+'"'; 1046 | } 1047 | return path; 1048 | } 1049 | 1050 | function sortRomList() { 1051 | $('.rom').sort(function(a, b) { 1052 | if (a.textContent < b.textContent) { 1053 | return -1; 1054 | } else { 1055 | return 1; 1056 | } 1057 | }).appendTo('#roms'); 1058 | // console.log("Sorted ROM list."); 1059 | } 1060 | 1061 | function sortSystemList() { 1062 | $('.system').sort(function(a, b) { 1063 | if (parseFloat($(a).data($(a).attr('id')).order) < parseFloat($(b).data($(b).attr('id')).order)) { 1064 | return -1; 1065 | } else { 1066 | return 1; 1067 | } 1068 | }).appendTo('#systems'); 1069 | // console.log("Sorted system list."); 1070 | } 1071 | 1072 | function filterRomList() { 1073 | let query = $('#search_bar').val(); 1074 | $('.rom').each(function() { 1075 | let name = $(this).html(); 1076 | if (!name.toLowerCase().includes(query.toLowerCase())) { 1077 | $(this).css({'display' : 'none'}); 1078 | } else { 1079 | $(this).css({'display' : ''}); 1080 | } 1081 | }); 1082 | } 1083 | 1084 | var shell = require('electron').shell; 1085 | 1086 | $(document).on('click', 'a[href^="http"]', function(event) { 1087 | event.preventDefault(); 1088 | shell.openExternal(this.href); 1089 | }); -------------------------------------------------------------------------------- /app/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ROML", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/node": { 8 | "version": "10.12.29", 9 | "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.29.tgz", 10 | "integrity": "sha512-J/tnbnj8HcsBgCe2apZbdUpQ7hs4d7oZNTYA5bekWdP0sr2NGsOpI/HRdDroEi209tEvTcTtxhD0FfED3DhEcw==", 11 | "dev": true 12 | }, 13 | "ajv": { 14 | "version": "6.10.0", 15 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", 16 | "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", 17 | "dev": true, 18 | "requires": { 19 | "fast-deep-equal": "^2.0.1", 20 | "fast-json-stable-stringify": "^2.0.0", 21 | "json-schema-traverse": "^0.4.1", 22 | "uri-js": "^4.2.2" 23 | } 24 | }, 25 | "ansi-regex": { 26 | "version": "2.1.1", 27 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 28 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", 29 | "dev": true 30 | }, 31 | "array-find-index": { 32 | "version": "1.0.2", 33 | "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", 34 | "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", 35 | "dev": true 36 | }, 37 | "asn1": { 38 | "version": "0.2.4", 39 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", 40 | "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", 41 | "dev": true, 42 | "requires": { 43 | "safer-buffer": "~2.1.0" 44 | } 45 | }, 46 | "assert-plus": { 47 | "version": "1.0.0", 48 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 49 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", 50 | "dev": true 51 | }, 52 | "asynckit": { 53 | "version": "0.4.0", 54 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 55 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", 56 | "dev": true 57 | }, 58 | "aws-sign2": { 59 | "version": "0.7.0", 60 | "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", 61 | "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", 62 | "dev": true 63 | }, 64 | "aws4": { 65 | "version": "1.8.0", 66 | "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", 67 | "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", 68 | "dev": true 69 | }, 70 | "bcrypt-pbkdf": { 71 | "version": "1.0.2", 72 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", 73 | "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", 74 | "dev": true, 75 | "requires": { 76 | "tweetnacl": "^0.14.3" 77 | } 78 | }, 79 | "buffer-from": { 80 | "version": "1.1.1", 81 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", 82 | "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", 83 | "dev": true 84 | }, 85 | "camelcase": { 86 | "version": "2.1.1", 87 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", 88 | "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", 89 | "dev": true 90 | }, 91 | "camelcase-keys": { 92 | "version": "2.1.0", 93 | "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", 94 | "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", 95 | "dev": true, 96 | "requires": { 97 | "camelcase": "^2.0.0", 98 | "map-obj": "^1.0.0" 99 | } 100 | }, 101 | "caseless": { 102 | "version": "0.12.0", 103 | "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", 104 | "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", 105 | "dev": true 106 | }, 107 | "code-point-at": { 108 | "version": "1.1.0", 109 | "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", 110 | "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", 111 | "dev": true 112 | }, 113 | "combined-stream": { 114 | "version": "1.0.7", 115 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", 116 | "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", 117 | "dev": true, 118 | "requires": { 119 | "delayed-stream": "~1.0.0" 120 | } 121 | }, 122 | "concat-stream": { 123 | "version": "1.6.2", 124 | "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", 125 | "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", 126 | "dev": true, 127 | "requires": { 128 | "buffer-from": "^1.0.0", 129 | "inherits": "^2.0.3", 130 | "readable-stream": "^2.2.2", 131 | "typedarray": "^0.0.6" 132 | }, 133 | "dependencies": { 134 | "isarray": { 135 | "version": "1.0.0", 136 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 137 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", 138 | "dev": true 139 | }, 140 | "readable-stream": { 141 | "version": "2.3.6", 142 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", 143 | "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", 144 | "dev": true, 145 | "requires": { 146 | "core-util-is": "~1.0.0", 147 | "inherits": "~2.0.3", 148 | "isarray": "~1.0.0", 149 | "process-nextick-args": "~2.0.0", 150 | "safe-buffer": "~5.1.1", 151 | "string_decoder": "~1.1.1", 152 | "util-deprecate": "~1.0.1" 153 | } 154 | }, 155 | "string_decoder": { 156 | "version": "1.1.1", 157 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 158 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 159 | "dev": true, 160 | "requires": { 161 | "safe-buffer": "~5.1.0" 162 | } 163 | } 164 | } 165 | }, 166 | "core-util-is": { 167 | "version": "1.0.2", 168 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 169 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", 170 | "dev": true 171 | }, 172 | "currently-unhandled": { 173 | "version": "0.4.1", 174 | "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", 175 | "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", 176 | "dev": true, 177 | "requires": { 178 | "array-find-index": "^1.0.1" 179 | } 180 | }, 181 | "dashdash": { 182 | "version": "1.14.1", 183 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", 184 | "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", 185 | "dev": true, 186 | "requires": { 187 | "assert-plus": "^1.0.0" 188 | } 189 | }, 190 | "debug": { 191 | "version": "3.2.6", 192 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", 193 | "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", 194 | "dev": true, 195 | "requires": { 196 | "ms": "^2.1.1" 197 | } 198 | }, 199 | "decamelize": { 200 | "version": "1.2.0", 201 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 202 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", 203 | "dev": true 204 | }, 205 | "deep-extend": { 206 | "version": "0.6.0", 207 | "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", 208 | "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", 209 | "dev": true 210 | }, 211 | "delayed-stream": { 212 | "version": "1.0.0", 213 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 214 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", 215 | "dev": true 216 | }, 217 | "ecc-jsbn": { 218 | "version": "0.1.2", 219 | "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", 220 | "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", 221 | "dev": true, 222 | "requires": { 223 | "jsbn": "~0.1.0", 224 | "safer-buffer": "^2.1.0" 225 | } 226 | }, 227 | "electron": { 228 | "version": "4.0.6", 229 | "resolved": "https://registry.npmjs.org/electron/-/electron-4.0.6.tgz", 230 | "integrity": "sha512-r2ow/EmDibjoCNJp35mB1CcPs2xBF9fp2eoWuUOJmpVQyzdba6EnqsSD1BxwXzF9vQ5WkQ2UbfXBIvpomrdioQ==", 231 | "dev": true, 232 | "requires": { 233 | "@types/node": "^10.12.18", 234 | "electron-download": "^4.1.0", 235 | "extract-zip": "^1.0.3" 236 | } 237 | }, 238 | "electron-download": { 239 | "version": "4.1.1", 240 | "resolved": "https://registry.npmjs.org/electron-download/-/electron-download-4.1.1.tgz", 241 | "integrity": "sha512-FjEWG9Jb/ppK/2zToP+U5dds114fM1ZOJqMAR4aXXL5CvyPE9fiqBK/9YcwC9poIFQTEJk/EM/zyRwziziRZrg==", 242 | "dev": true, 243 | "requires": { 244 | "debug": "^3.0.0", 245 | "env-paths": "^1.0.0", 246 | "fs-extra": "^4.0.1", 247 | "minimist": "^1.2.0", 248 | "nugget": "^2.0.1", 249 | "path-exists": "^3.0.0", 250 | "rc": "^1.2.1", 251 | "semver": "^5.4.1", 252 | "sumchecker": "^2.0.2" 253 | } 254 | }, 255 | "env-paths": { 256 | "version": "1.0.0", 257 | "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-1.0.0.tgz", 258 | "integrity": "sha1-QWgTO0K7BcOKNbGuQ5fIKYqzaeA=", 259 | "dev": true 260 | }, 261 | "error-ex": { 262 | "version": "1.3.2", 263 | "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", 264 | "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", 265 | "dev": true, 266 | "requires": { 267 | "is-arrayish": "^0.2.1" 268 | } 269 | }, 270 | "extend": { 271 | "version": "3.0.2", 272 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", 273 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", 274 | "dev": true 275 | }, 276 | "extract-zip": { 277 | "version": "1.6.7", 278 | "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz", 279 | "integrity": "sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=", 280 | "dev": true, 281 | "requires": { 282 | "concat-stream": "1.6.2", 283 | "debug": "2.6.9", 284 | "mkdirp": "0.5.1", 285 | "yauzl": "2.4.1" 286 | }, 287 | "dependencies": { 288 | "debug": { 289 | "version": "2.6.9", 290 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 291 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 292 | "dev": true, 293 | "requires": { 294 | "ms": "2.0.0" 295 | } 296 | }, 297 | "ms": { 298 | "version": "2.0.0", 299 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 300 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 301 | "dev": true 302 | } 303 | } 304 | }, 305 | "extsprintf": { 306 | "version": "1.3.0", 307 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", 308 | "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", 309 | "dev": true 310 | }, 311 | "fast-deep-equal": { 312 | "version": "2.0.1", 313 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", 314 | "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", 315 | "dev": true 316 | }, 317 | "fast-json-stable-stringify": { 318 | "version": "2.0.0", 319 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", 320 | "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", 321 | "dev": true 322 | }, 323 | "fd-slicer": { 324 | "version": "1.0.1", 325 | "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", 326 | "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", 327 | "dev": true, 328 | "requires": { 329 | "pend": "~1.2.0" 330 | } 331 | }, 332 | "find-up": { 333 | "version": "1.1.2", 334 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", 335 | "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", 336 | "dev": true, 337 | "requires": { 338 | "path-exists": "^2.0.0", 339 | "pinkie-promise": "^2.0.0" 340 | }, 341 | "dependencies": { 342 | "path-exists": { 343 | "version": "2.1.0", 344 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", 345 | "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", 346 | "dev": true, 347 | "requires": { 348 | "pinkie-promise": "^2.0.0" 349 | } 350 | } 351 | } 352 | }, 353 | "forever-agent": { 354 | "version": "0.6.1", 355 | "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", 356 | "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", 357 | "dev": true 358 | }, 359 | "form-data": { 360 | "version": "2.3.3", 361 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", 362 | "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", 363 | "dev": true, 364 | "requires": { 365 | "asynckit": "^0.4.0", 366 | "combined-stream": "^1.0.6", 367 | "mime-types": "^2.1.12" 368 | } 369 | }, 370 | "fs-extra": { 371 | "version": "4.0.3", 372 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", 373 | "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", 374 | "dev": true, 375 | "requires": { 376 | "graceful-fs": "^4.1.2", 377 | "jsonfile": "^4.0.0", 378 | "universalify": "^0.1.0" 379 | } 380 | }, 381 | "get-stdin": { 382 | "version": "4.0.1", 383 | "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", 384 | "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", 385 | "dev": true 386 | }, 387 | "getpass": { 388 | "version": "0.1.7", 389 | "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", 390 | "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", 391 | "dev": true, 392 | "requires": { 393 | "assert-plus": "^1.0.0" 394 | } 395 | }, 396 | "graceful-fs": { 397 | "version": "4.1.15", 398 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", 399 | "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", 400 | "dev": true 401 | }, 402 | "har-schema": { 403 | "version": "2.0.0", 404 | "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", 405 | "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", 406 | "dev": true 407 | }, 408 | "har-validator": { 409 | "version": "5.1.3", 410 | "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", 411 | "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", 412 | "dev": true, 413 | "requires": { 414 | "ajv": "^6.5.5", 415 | "har-schema": "^2.0.0" 416 | } 417 | }, 418 | "hosted-git-info": { 419 | "version": "2.7.1", 420 | "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", 421 | "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", 422 | "dev": true 423 | }, 424 | "http-signature": { 425 | "version": "1.2.0", 426 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", 427 | "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", 428 | "dev": true, 429 | "requires": { 430 | "assert-plus": "^1.0.0", 431 | "jsprim": "^1.2.2", 432 | "sshpk": "^1.7.0" 433 | } 434 | }, 435 | "indent-string": { 436 | "version": "2.1.0", 437 | "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", 438 | "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", 439 | "dev": true, 440 | "requires": { 441 | "repeating": "^2.0.0" 442 | } 443 | }, 444 | "inherits": { 445 | "version": "2.0.3", 446 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 447 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", 448 | "dev": true 449 | }, 450 | "ini": { 451 | "version": "1.3.5", 452 | "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", 453 | "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", 454 | "dev": true 455 | }, 456 | "is-arrayish": { 457 | "version": "0.2.1", 458 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", 459 | "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", 460 | "dev": true 461 | }, 462 | "is-finite": { 463 | "version": "1.0.2", 464 | "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", 465 | "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", 466 | "dev": true, 467 | "requires": { 468 | "number-is-nan": "^1.0.0" 469 | } 470 | }, 471 | "is-fullwidth-code-point": { 472 | "version": "1.0.0", 473 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", 474 | "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", 475 | "dev": true, 476 | "requires": { 477 | "number-is-nan": "^1.0.0" 478 | } 479 | }, 480 | "is-typedarray": { 481 | "version": "1.0.0", 482 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", 483 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", 484 | "dev": true 485 | }, 486 | "is-utf8": { 487 | "version": "0.2.1", 488 | "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", 489 | "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", 490 | "dev": true 491 | }, 492 | "isarray": { 493 | "version": "0.0.1", 494 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 495 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", 496 | "dev": true 497 | }, 498 | "isstream": { 499 | "version": "0.1.2", 500 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", 501 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", 502 | "dev": true 503 | }, 504 | "jsbn": { 505 | "version": "0.1.1", 506 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", 507 | "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", 508 | "dev": true 509 | }, 510 | "json-schema": { 511 | "version": "0.2.3", 512 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", 513 | "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", 514 | "dev": true 515 | }, 516 | "json-schema-traverse": { 517 | "version": "0.4.1", 518 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 519 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 520 | "dev": true 521 | }, 522 | "json-stringify-safe": { 523 | "version": "5.0.1", 524 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", 525 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", 526 | "dev": true 527 | }, 528 | "jsonfile": { 529 | "version": "4.0.0", 530 | "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", 531 | "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", 532 | "dev": true, 533 | "requires": { 534 | "graceful-fs": "^4.1.6" 535 | } 536 | }, 537 | "jsprim": { 538 | "version": "1.4.1", 539 | "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", 540 | "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", 541 | "dev": true, 542 | "requires": { 543 | "assert-plus": "1.0.0", 544 | "extsprintf": "1.3.0", 545 | "json-schema": "0.2.3", 546 | "verror": "1.10.0" 547 | } 548 | }, 549 | "load-json-file": { 550 | "version": "1.1.0", 551 | "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", 552 | "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", 553 | "dev": true, 554 | "requires": { 555 | "graceful-fs": "^4.1.2", 556 | "parse-json": "^2.2.0", 557 | "pify": "^2.0.0", 558 | "pinkie-promise": "^2.0.0", 559 | "strip-bom": "^2.0.0" 560 | } 561 | }, 562 | "loud-rejection": { 563 | "version": "1.6.0", 564 | "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", 565 | "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", 566 | "dev": true, 567 | "requires": { 568 | "currently-unhandled": "^0.4.1", 569 | "signal-exit": "^3.0.0" 570 | } 571 | }, 572 | "map-obj": { 573 | "version": "1.0.1", 574 | "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", 575 | "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", 576 | "dev": true 577 | }, 578 | "meow": { 579 | "version": "3.7.0", 580 | "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", 581 | "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", 582 | "dev": true, 583 | "requires": { 584 | "camelcase-keys": "^2.0.0", 585 | "decamelize": "^1.1.2", 586 | "loud-rejection": "^1.0.0", 587 | "map-obj": "^1.0.1", 588 | "minimist": "^1.1.3", 589 | "normalize-package-data": "^2.3.4", 590 | "object-assign": "^4.0.1", 591 | "read-pkg-up": "^1.0.1", 592 | "redent": "^1.0.0", 593 | "trim-newlines": "^1.0.0" 594 | } 595 | }, 596 | "mime-db": { 597 | "version": "1.38.0", 598 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz", 599 | "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==", 600 | "dev": true 601 | }, 602 | "mime-types": { 603 | "version": "2.1.22", 604 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz", 605 | "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==", 606 | "dev": true, 607 | "requires": { 608 | "mime-db": "~1.38.0" 609 | } 610 | }, 611 | "minimist": { 612 | "version": "1.2.0", 613 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", 614 | "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", 615 | "dev": true 616 | }, 617 | "mkdirp": { 618 | "version": "0.5.1", 619 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 620 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 621 | "dev": true, 622 | "requires": { 623 | "minimist": "0.0.8" 624 | }, 625 | "dependencies": { 626 | "minimist": { 627 | "version": "0.0.8", 628 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 629 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 630 | "dev": true 631 | } 632 | } 633 | }, 634 | "ms": { 635 | "version": "2.1.1", 636 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 637 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", 638 | "dev": true 639 | }, 640 | "normalize-package-data": { 641 | "version": "2.5.0", 642 | "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", 643 | "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", 644 | "dev": true, 645 | "requires": { 646 | "hosted-git-info": "^2.1.4", 647 | "resolve": "^1.10.0", 648 | "semver": "2 || 3 || 4 || 5", 649 | "validate-npm-package-license": "^3.0.1" 650 | } 651 | }, 652 | "nugget": { 653 | "version": "2.0.1", 654 | "resolved": "https://registry.npmjs.org/nugget/-/nugget-2.0.1.tgz", 655 | "integrity": "sha1-IBCVpIfhrTYIGzQy+jytpPjQcbA=", 656 | "dev": true, 657 | "requires": { 658 | "debug": "^2.1.3", 659 | "minimist": "^1.1.0", 660 | "pretty-bytes": "^1.0.2", 661 | "progress-stream": "^1.1.0", 662 | "request": "^2.45.0", 663 | "single-line-log": "^1.1.2", 664 | "throttleit": "0.0.2" 665 | }, 666 | "dependencies": { 667 | "debug": { 668 | "version": "2.6.9", 669 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 670 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 671 | "dev": true, 672 | "requires": { 673 | "ms": "2.0.0" 674 | } 675 | }, 676 | "ms": { 677 | "version": "2.0.0", 678 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 679 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 680 | "dev": true 681 | } 682 | } 683 | }, 684 | "number-is-nan": { 685 | "version": "1.0.1", 686 | "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", 687 | "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", 688 | "dev": true 689 | }, 690 | "oauth-sign": { 691 | "version": "0.9.0", 692 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", 693 | "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", 694 | "dev": true 695 | }, 696 | "object-assign": { 697 | "version": "4.1.1", 698 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 699 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", 700 | "dev": true 701 | }, 702 | "object-keys": { 703 | "version": "0.4.0", 704 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", 705 | "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=", 706 | "dev": true 707 | }, 708 | "parse-json": { 709 | "version": "2.2.0", 710 | "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", 711 | "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", 712 | "dev": true, 713 | "requires": { 714 | "error-ex": "^1.2.0" 715 | } 716 | }, 717 | "path-exists": { 718 | "version": "3.0.0", 719 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", 720 | "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", 721 | "dev": true 722 | }, 723 | "path-parse": { 724 | "version": "1.0.6", 725 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", 726 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", 727 | "dev": true 728 | }, 729 | "path-type": { 730 | "version": "1.1.0", 731 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", 732 | "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", 733 | "dev": true, 734 | "requires": { 735 | "graceful-fs": "^4.1.2", 736 | "pify": "^2.0.0", 737 | "pinkie-promise": "^2.0.0" 738 | } 739 | }, 740 | "pend": { 741 | "version": "1.2.0", 742 | "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", 743 | "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", 744 | "dev": true 745 | }, 746 | "performance-now": { 747 | "version": "2.1.0", 748 | "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", 749 | "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", 750 | "dev": true 751 | }, 752 | "pify": { 753 | "version": "2.3.0", 754 | "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", 755 | "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", 756 | "dev": true 757 | }, 758 | "pinkie": { 759 | "version": "2.0.4", 760 | "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", 761 | "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", 762 | "dev": true 763 | }, 764 | "pinkie-promise": { 765 | "version": "2.0.1", 766 | "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", 767 | "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", 768 | "dev": true, 769 | "requires": { 770 | "pinkie": "^2.0.0" 771 | } 772 | }, 773 | "pretty-bytes": { 774 | "version": "1.0.4", 775 | "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-1.0.4.tgz", 776 | "integrity": "sha1-CiLoIQYJrTVUL4yNXSFZr/B1HIQ=", 777 | "dev": true, 778 | "requires": { 779 | "get-stdin": "^4.0.1", 780 | "meow": "^3.1.0" 781 | } 782 | }, 783 | "process-nextick-args": { 784 | "version": "2.0.0", 785 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", 786 | "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", 787 | "dev": true 788 | }, 789 | "progress-stream": { 790 | "version": "1.2.0", 791 | "resolved": "https://registry.npmjs.org/progress-stream/-/progress-stream-1.2.0.tgz", 792 | "integrity": "sha1-LNPP6jO6OonJwSHsM0er6asSX3c=", 793 | "dev": true, 794 | "requires": { 795 | "speedometer": "~0.1.2", 796 | "through2": "~0.2.3" 797 | } 798 | }, 799 | "psl": { 800 | "version": "1.1.31", 801 | "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", 802 | "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==", 803 | "dev": true 804 | }, 805 | "punycode": { 806 | "version": "2.1.1", 807 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 808 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", 809 | "dev": true 810 | }, 811 | "qs": { 812 | "version": "6.5.2", 813 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", 814 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", 815 | "dev": true 816 | }, 817 | "rc": { 818 | "version": "1.2.8", 819 | "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", 820 | "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", 821 | "dev": true, 822 | "requires": { 823 | "deep-extend": "^0.6.0", 824 | "ini": "~1.3.0", 825 | "minimist": "^1.2.0", 826 | "strip-json-comments": "~2.0.1" 827 | } 828 | }, 829 | "read-pkg": { 830 | "version": "1.1.0", 831 | "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", 832 | "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", 833 | "dev": true, 834 | "requires": { 835 | "load-json-file": "^1.0.0", 836 | "normalize-package-data": "^2.3.2", 837 | "path-type": "^1.0.0" 838 | } 839 | }, 840 | "read-pkg-up": { 841 | "version": "1.0.1", 842 | "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", 843 | "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", 844 | "dev": true, 845 | "requires": { 846 | "find-up": "^1.0.0", 847 | "read-pkg": "^1.0.0" 848 | } 849 | }, 850 | "readable-stream": { 851 | "version": "1.1.14", 852 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", 853 | "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", 854 | "dev": true, 855 | "requires": { 856 | "core-util-is": "~1.0.0", 857 | "inherits": "~2.0.1", 858 | "isarray": "0.0.1", 859 | "string_decoder": "~0.10.x" 860 | } 861 | }, 862 | "redent": { 863 | "version": "1.0.0", 864 | "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", 865 | "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", 866 | "dev": true, 867 | "requires": { 868 | "indent-string": "^2.1.0", 869 | "strip-indent": "^1.0.1" 870 | } 871 | }, 872 | "repeating": { 873 | "version": "2.0.1", 874 | "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", 875 | "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", 876 | "dev": true, 877 | "requires": { 878 | "is-finite": "^1.0.0" 879 | } 880 | }, 881 | "request": { 882 | "version": "2.88.0", 883 | "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", 884 | "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", 885 | "dev": true, 886 | "requires": { 887 | "aws-sign2": "~0.7.0", 888 | "aws4": "^1.8.0", 889 | "caseless": "~0.12.0", 890 | "combined-stream": "~1.0.6", 891 | "extend": "~3.0.2", 892 | "forever-agent": "~0.6.1", 893 | "form-data": "~2.3.2", 894 | "har-validator": "~5.1.0", 895 | "http-signature": "~1.2.0", 896 | "is-typedarray": "~1.0.0", 897 | "isstream": "~0.1.2", 898 | "json-stringify-safe": "~5.0.1", 899 | "mime-types": "~2.1.19", 900 | "oauth-sign": "~0.9.0", 901 | "performance-now": "^2.1.0", 902 | "qs": "~6.5.2", 903 | "safe-buffer": "^5.1.2", 904 | "tough-cookie": "~2.4.3", 905 | "tunnel-agent": "^0.6.0", 906 | "uuid": "^3.3.2" 907 | } 908 | }, 909 | "resolve": { 910 | "version": "1.10.0", 911 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", 912 | "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", 913 | "dev": true, 914 | "requires": { 915 | "path-parse": "^1.0.6" 916 | } 917 | }, 918 | "safe-buffer": { 919 | "version": "5.1.2", 920 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 921 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", 922 | "dev": true 923 | }, 924 | "safer-buffer": { 925 | "version": "2.1.2", 926 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 927 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", 928 | "dev": true 929 | }, 930 | "semver": { 931 | "version": "5.6.0", 932 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", 933 | "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", 934 | "dev": true 935 | }, 936 | "signal-exit": { 937 | "version": "3.0.2", 938 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", 939 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", 940 | "dev": true 941 | }, 942 | "single-line-log": { 943 | "version": "1.1.2", 944 | "resolved": "https://registry.npmjs.org/single-line-log/-/single-line-log-1.1.2.tgz", 945 | "integrity": "sha1-wvg/Jzo+GhbtsJlWYdoO1e8DM2Q=", 946 | "dev": true, 947 | "requires": { 948 | "string-width": "^1.0.1" 949 | } 950 | }, 951 | "spdx-correct": { 952 | "version": "3.1.0", 953 | "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", 954 | "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", 955 | "dev": true, 956 | "requires": { 957 | "spdx-expression-parse": "^3.0.0", 958 | "spdx-license-ids": "^3.0.0" 959 | } 960 | }, 961 | "spdx-exceptions": { 962 | "version": "2.2.0", 963 | "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", 964 | "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", 965 | "dev": true 966 | }, 967 | "spdx-expression-parse": { 968 | "version": "3.0.0", 969 | "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", 970 | "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", 971 | "dev": true, 972 | "requires": { 973 | "spdx-exceptions": "^2.1.0", 974 | "spdx-license-ids": "^3.0.0" 975 | } 976 | }, 977 | "spdx-license-ids": { 978 | "version": "3.0.3", 979 | "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz", 980 | "integrity": "sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g==", 981 | "dev": true 982 | }, 983 | "speedometer": { 984 | "version": "0.1.4", 985 | "resolved": "https://registry.npmjs.org/speedometer/-/speedometer-0.1.4.tgz", 986 | "integrity": "sha1-mHbb0qFp0xFUAtSObqYynIgWpQ0=", 987 | "dev": true 988 | }, 989 | "sshpk": { 990 | "version": "1.16.1", 991 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", 992 | "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", 993 | "dev": true, 994 | "requires": { 995 | "asn1": "~0.2.3", 996 | "assert-plus": "^1.0.0", 997 | "bcrypt-pbkdf": "^1.0.0", 998 | "dashdash": "^1.12.0", 999 | "ecc-jsbn": "~0.1.1", 1000 | "getpass": "^0.1.1", 1001 | "jsbn": "~0.1.0", 1002 | "safer-buffer": "^2.0.2", 1003 | "tweetnacl": "~0.14.0" 1004 | } 1005 | }, 1006 | "string-width": { 1007 | "version": "1.0.2", 1008 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", 1009 | "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", 1010 | "dev": true, 1011 | "requires": { 1012 | "code-point-at": "^1.0.0", 1013 | "is-fullwidth-code-point": "^1.0.0", 1014 | "strip-ansi": "^3.0.0" 1015 | } 1016 | }, 1017 | "string_decoder": { 1018 | "version": "0.10.31", 1019 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 1020 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", 1021 | "dev": true 1022 | }, 1023 | "strip-ansi": { 1024 | "version": "3.0.1", 1025 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 1026 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 1027 | "dev": true, 1028 | "requires": { 1029 | "ansi-regex": "^2.0.0" 1030 | } 1031 | }, 1032 | "strip-bom": { 1033 | "version": "2.0.0", 1034 | "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", 1035 | "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", 1036 | "dev": true, 1037 | "requires": { 1038 | "is-utf8": "^0.2.0" 1039 | } 1040 | }, 1041 | "strip-indent": { 1042 | "version": "1.0.1", 1043 | "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", 1044 | "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", 1045 | "dev": true, 1046 | "requires": { 1047 | "get-stdin": "^4.0.1" 1048 | } 1049 | }, 1050 | "strip-json-comments": { 1051 | "version": "2.0.1", 1052 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 1053 | "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", 1054 | "dev": true 1055 | }, 1056 | "sumchecker": { 1057 | "version": "2.0.2", 1058 | "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-2.0.2.tgz", 1059 | "integrity": "sha1-D0LBDl0F2l1C7qPlbDOZo31sWz4=", 1060 | "dev": true, 1061 | "requires": { 1062 | "debug": "^2.2.0" 1063 | }, 1064 | "dependencies": { 1065 | "debug": { 1066 | "version": "2.6.9", 1067 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 1068 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 1069 | "dev": true, 1070 | "requires": { 1071 | "ms": "2.0.0" 1072 | } 1073 | }, 1074 | "ms": { 1075 | "version": "2.0.0", 1076 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 1077 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 1078 | "dev": true 1079 | } 1080 | } 1081 | }, 1082 | "throttleit": { 1083 | "version": "0.0.2", 1084 | "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-0.0.2.tgz", 1085 | "integrity": "sha1-z+34jmDADdlpe2H90qg0OptoDq8=", 1086 | "dev": true 1087 | }, 1088 | "through2": { 1089 | "version": "0.2.3", 1090 | "resolved": "https://registry.npmjs.org/through2/-/through2-0.2.3.tgz", 1091 | "integrity": "sha1-6zKE2k6jEbbMis42U3SKUqvyWj8=", 1092 | "dev": true, 1093 | "requires": { 1094 | "readable-stream": "~1.1.9", 1095 | "xtend": "~2.1.1" 1096 | } 1097 | }, 1098 | "tough-cookie": { 1099 | "version": "2.4.3", 1100 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", 1101 | "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", 1102 | "dev": true, 1103 | "requires": { 1104 | "psl": "^1.1.24", 1105 | "punycode": "^1.4.1" 1106 | }, 1107 | "dependencies": { 1108 | "punycode": { 1109 | "version": "1.4.1", 1110 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", 1111 | "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", 1112 | "dev": true 1113 | } 1114 | } 1115 | }, 1116 | "trim-newlines": { 1117 | "version": "1.0.0", 1118 | "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", 1119 | "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", 1120 | "dev": true 1121 | }, 1122 | "tunnel-agent": { 1123 | "version": "0.6.0", 1124 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", 1125 | "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", 1126 | "dev": true, 1127 | "requires": { 1128 | "safe-buffer": "^5.0.1" 1129 | } 1130 | }, 1131 | "tweetnacl": { 1132 | "version": "0.14.5", 1133 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", 1134 | "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", 1135 | "dev": true 1136 | }, 1137 | "typedarray": { 1138 | "version": "0.0.6", 1139 | "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", 1140 | "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", 1141 | "dev": true 1142 | }, 1143 | "universalify": { 1144 | "version": "0.1.2", 1145 | "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", 1146 | "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", 1147 | "dev": true 1148 | }, 1149 | "uri-js": { 1150 | "version": "4.2.2", 1151 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", 1152 | "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", 1153 | "dev": true, 1154 | "requires": { 1155 | "punycode": "^2.1.0" 1156 | } 1157 | }, 1158 | "util-deprecate": { 1159 | "version": "1.0.2", 1160 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1161 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", 1162 | "dev": true 1163 | }, 1164 | "uuid": { 1165 | "version": "3.3.2", 1166 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", 1167 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", 1168 | "dev": true 1169 | }, 1170 | "validate-npm-package-license": { 1171 | "version": "3.0.4", 1172 | "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", 1173 | "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", 1174 | "dev": true, 1175 | "requires": { 1176 | "spdx-correct": "^3.0.0", 1177 | "spdx-expression-parse": "^3.0.0" 1178 | } 1179 | }, 1180 | "verror": { 1181 | "version": "1.10.0", 1182 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", 1183 | "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", 1184 | "dev": true, 1185 | "requires": { 1186 | "assert-plus": "^1.0.0", 1187 | "core-util-is": "1.0.2", 1188 | "extsprintf": "^1.2.0" 1189 | } 1190 | }, 1191 | "xtend": { 1192 | "version": "2.1.2", 1193 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", 1194 | "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", 1195 | "dev": true, 1196 | "requires": { 1197 | "object-keys": "~0.4.0" 1198 | } 1199 | }, 1200 | "yauzl": { 1201 | "version": "2.4.1", 1202 | "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", 1203 | "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", 1204 | "dev": true, 1205 | "requires": { 1206 | "fd-slicer": "~1.0.1" 1207 | } 1208 | } 1209 | } 1210 | } 1211 | --------------------------------------------------------------------------------