├── 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 | 
15 |
16 | 
17 |
18 | 
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 |
EDIT
24 |
╲
25 |
╱
26 |
╳
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | DEBUG_MESSAGE
36 | ╳
37 |
38 |
39 |
44 |
45 |
46 |
47 |
48 | CLEAR
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
System
57 |
General
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | ROM Launcher Version 1.2c
67 |
68 |
69 | By ButcheredGenre / TLOZ
70 |
71 |
72 |
73 | If you've gotten some use out of this launcher and happen to have some money to throw away -- then why not throw it at me? Donations are never required, but I greatly appreciate any contributions 🌝
74 |
75 |
76 |
77 | PayPal Donation Link (opens in your browser)
78 |
79 |
80 |
81 |
82 | You can change some general and experimental settings below. You'll have to restart the launcher for any of these settings to take effect.
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 | Enable Controller Input
93 |
94 |
95 | Navigate the interface using a controller.
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 | Controller Mapping
104 |
105 |
106 | Make sure controller input is enabled before remapping. You may need to save & restart the launcher if you've just enabled controller input.
107 | Click on the button you want to map, then press a button on your controller.
108 |
109 | A : Confirm. B : Back. L and R : Skip through ROM list.
110 | Q : Up to 4 buttons to quit the running emulator, if the feature is enabled.
111 |
112 |
113 |
131 |
132 |
133 |
Clear Custom Mapping
134 |
135 |
136 |
137 |
138 |
139 |
140 | Quit Emulator Using Button Combo
141 |
142 |
143 | Quit the launched emulator using the button combo specified in the controller mapping section. You need to map at least two buttons. An exotic button combo (e.g. something including L3 or R3) is recommended. Requires controller input to be enabled.
144 |
145 |
146 |
147 |
148 |
149 |
150 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
Currently Editing System ID
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
Launcher Prefix
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
Unique ID
182 |
183 |
184 |
185 |
186 |
187 |
188 |
Emulator Executable Path
189 |
190 |
191 | ⇄
192 | 📄
193 |
194 |
195 |
196 |
197 |
198 |
Emulator Arguments
199 |
200 |
201 |
202 |
203 |
204 | ⇄
205 | 📁
206 | 📄
207 | +
208 | -
209 |
210 |
211 |
212 |
213 |
214 |
215 |
ROM Directories
216 |
217 |
218 |
219 |
220 |
221 | ⇄
222 | 📁
223 | 🌜
224 | +
225 | -
226 |
227 |
228 |
229 |
230 |
231 |
232 |
ROM File Types
233 |
234 |
235 |
236 |
237 |
238 | +
239 | -
240 |
241 |
242 |
243 |
244 |
245 |
246 |
CSS Background
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
272 |
273 |
274 | DEBUG_MESSAGE
275 | ╳
276 |
277 |
278 |
279 |
280 | Delete
281 | Save
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
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 |
--------------------------------------------------------------------------------