Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>pull/1/head
@ -1,28 +0,0 @@ | 
				
			||||
<?xml version="1.0" encoding="UTF-8"?> | 
				
			||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | 
				
			||||
<plist version="1.0"> | 
				
			||||
<dict> | 
				
			||||
    <!-- Entitlements from electron-builder's defaults | 
				
			||||
         (https://github.com/electron-userland/electron-builder/blob/master/packages/app-builder-lib/templates/entitlements.mac.plist) | 
				
			||||
         nb. This does *not* include the app sandbox: at the time of adding this file, | 
				
			||||
         we were using electron-builder 21.2.0 which does not have the sandbox entitlement. | 
				
			||||
         Latest electron-builder does, but it appears to be causing issues: | 
				
			||||
         (https://github.com/electron-userland/electron-builder/issues/4390) | 
				
			||||
    --> | 
				
			||||
    <!-- https://github.com/electron/electron-notarize#prerequisites --> | 
				
			||||
    <key>com.apple.security.cs.allow-jit</key> | 
				
			||||
    <true/> | 
				
			||||
    <key>com.apple.security.cs.allow-unsigned-executable-memory</key> | 
				
			||||
    <true/> | 
				
			||||
    <!-- https://github.com/electron-userland/electron-builder/issues/3940 --> | 
				
			||||
    <key>com.apple.security.cs.disable-library-validation</key> | 
				
			||||
    <true/> | 
				
			||||
 | 
				
			||||
    <!-- Our own additional entitlements (we need to access the camera and | 
				
			||||
        mic for VoIP calls --> | 
				
			||||
    <key>com.apple.security.device.camera</key> | 
				
			||||
    <true/> | 
				
			||||
    <key>com.apple.security.device.audio-input</key> | 
				
			||||
    <true/> | 
				
			||||
</dict> | 
				
			||||
</plist> | 
				
			||||
| 
		 Before Width: | Height: | Size: 36 KiB  | 
| 
		 Before Width: | Height: | Size: 2.7 KiB  | 
| 
		 Before Width: | Height: | Size: 581 B  | 
| 
		 Before Width: | Height: | Size: 824 B  | 
| 
		 Before Width: | Height: | Size: 4.7 KiB  | 
| 
		 Before Width: | Height: | Size: 1.4 KiB  | 
| 
		 Before Width: | Height: | Size: 8.3 KiB  | 
| 
		 Before Width: | Height: | Size: 1.8 KiB  | 
| 
		 Before Width: | Height: | Size: 2.2 KiB  | 
| 
		 Before Width: | Height: | Size: 5.4 KiB  | 
| 
		 Before Width: | Height: | Size: 36 KiB  | 
| 
		 Before Width: | Height: | Size: 4.7 KiB  | 
@ -1,15 +0,0 @@ | 
				
			||||
{ | 
				
			||||
  "name": "riot-web", | 
				
			||||
  "productName": "Riot", | 
				
			||||
  "main": "src/electron-main.js", | 
				
			||||
  "version": "1.6.0", | 
				
			||||
  "description": "A feature-rich client for Matrix.org", | 
				
			||||
  "author": "New Vector Ltd.", | 
				
			||||
  "dependencies": { | 
				
			||||
    "auto-launch": "^5.0.1", | 
				
			||||
    "electron-store": "^2.0.0", | 
				
			||||
    "electron-window-state": "^4.1.0", | 
				
			||||
    "minimist": "^1.2.0", | 
				
			||||
    "png-to-ico": "^1.0.2" | 
				
			||||
  } | 
				
			||||
} | 
				
			||||
@ -1,34 +0,0 @@ | 
				
			||||
-----BEGIN CERTIFICATE----- | 
				
			||||
MIIF0jCCBLqgAwIBAgIRAISYBqZi3VvCUeSfHXF+cbwwDQYJKoZIhvcNAQELBQAw | 
				
			||||
gZExCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO | 
				
			||||
BgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMTcwNQYD | 
				
			||||
VQQDEy5DT01PRE8gUlNBIEV4dGVuZGVkIFZhbGlkYXRpb24gQ29kZSBTaWduaW5n | 
				
			||||
IENBMB4XDTE3MDgyMzAwMDAwMFoXDTIwMDgyMjIzNTk1OVowgdgxETAPBgNVBAUT | 
				
			||||
CDEwODczNjYxMRMwEQYLKwYBBAGCNzwCAQMTAkdCMR0wGwYDVQQPExRQcml2YXRl | 
				
			||||
IE9yZ2FuaXphdGlvbjELMAkGA1UEBhMCR0IxETAPBgNVBBEMCFdDMVIgNEFHMQ8w | 
				
			||||
DQYDVQQIDAZMb25kb24xDzANBgNVBAcMBkxvbmRvbjEbMBkGA1UECQwSMjYgUmVk | 
				
			||||
IExpb24gU3F1YXJlMRcwFQYDVQQKDA5OZXcgVmVjdG9yIEx0ZDEXMBUGA1UEAwwO | 
				
			||||
TmV3IFZlY3RvciBMdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7 | 
				
			||||
X0HP3oM/SVr6PboD03ndtYTONZDcJ/GJ3EyYi6UNrcbKjuDHwPktx9hjAhNjcVkG | 
				
			||||
lmuTEPluPj9DbvjaTrers0cQsAS1vJ0RHjLfA93Flg1ys9Q6OThUMw77FtFPtiJU | 
				
			||||
z5cSYzfFAhn/4dv7BcgGptn+Mv/8CaTu+RUZJUgoSlRWcT1TREmxkzWotbblqsHO | 
				
			||||
zjDmUg20tL5/qpt6BSWsNespf5udKQFXMtqkczBcLvBLmql0vurVcQy8BibB+Q89 | 
				
			||||
QKwRzwLgaIa7O8WEssFcW8uJe9s0SNtUy8ehbuoSxpA/DbHFwsiDbNA78vp7HrqM | 
				
			||||
qY6t6OIgLtDYBFCfe/btAgMBAAGjggHaMIIB1jAfBgNVHSMEGDAWgBTfj/MgDOnK | 
				
			||||
pgTYW1g3Kj2rRtyDSTAdBgNVHQ4EFgQUH+mDOdRkF3bYDxCWEaGB4lxiCxcwDgYD | 
				
			||||
VR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwMw | 
				
			||||
EQYJYIZIAYb4QgEBBAQDAgQQMEYGA1UdIAQ/MD0wOwYMKwYBBAGyMQECAQYBMCsw | 
				
			||||
KQYIKwYBBQUHAgEWHWh0dHBzOi8vc2VjdXJlLmNvbW9kby5jb20vQ1BTMFUGA1Ud | 
				
			||||
HwROMEwwSqBIoEaGRGh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL0NPTU9ET1JTQUV4 | 
				
			||||
dGVuZGVkVmFsaWRhdGlvbkNvZGVTaWduaW5nQ0EuY3JsMIGGBggrBgEFBQcBAQR6 | 
				
			||||
MHgwUAYIKwYBBQUHMAKGRGh0dHA6Ly9jcnQuY29tb2RvY2EuY29tL0NPTU9ET1JT | 
				
			||||
QUV4dGVuZGVkVmFsaWRhdGlvbkNvZGVTaWduaW5nQ0EuY3J0MCQGCCsGAQUFBzAB | 
				
			||||
hhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20wJgYDVR0RBB8wHaAbBggrBgEFBQcI | 
				
			||||
A6APMA0MC0dCLTEwODczNjYxMA0GCSqGSIb3DQEBCwUAA4IBAQBJ2aH4aixh0aiz | 
				
			||||
4WKlK+LMVLHpQ2POE3FZYNpAW7o1q2YDGEADXdGrygPE9NCGNBXKo0CAemCYNWfX | 
				
			||||
Ov/jdoiMfeqW3vrZ66oEy8OqbvJSwK1xmomWuYw3wYPWcPVG+YbWYD2CGdQu8jTz | 
				
			||||
fzAJCpvAuY3Wji3fQjiecAC7JCSB4fBHa0ALJOmiSqKQUUpkXs5kW7O0lPBnHzNF | 
				
			||||
2tQGltXMSIrq1QfFtcreMyKlwDOxPIh360dv5aHhaeSRDRKxq7uq5ikQF2gjKx4k | 
				
			||||
ieg2HRbAW6fVPpFr4zRS5umpeZV3i06i11VQQPS/mA/OBEXyaqzx4mr6B7U6ptrp | 
				
			||||
jMqiUv2w | 
				
			||||
-----END CERTIFICATE----- | 
				
			||||
@ -1,6 +0,0 @@ | 
				
			||||
This directory contains the config file for the official riot.im distribution | 
				
			||||
of Riot Desktop. | 
				
			||||
 | 
				
			||||
You probably do not want to build with this config unless you're building the | 
				
			||||
official riot.im distribution, or you'll find your builds will replace | 
				
			||||
themselves with the riot.im build. | 
				
			||||
@ -1,32 +0,0 @@ | 
				
			||||
{ | 
				
			||||
    "update_base_url": "https://packages.riot.im/desktop/update/", | 
				
			||||
    "default_server_name": "matrix.org", | 
				
			||||
    "brand": "Riot", | 
				
			||||
    "integrations_ui_url": "https://scalar.vector.im/", | 
				
			||||
    "integrations_rest_url": "https://scalar.vector.im/api", | 
				
			||||
    "integrations_widgets_urls": [ | 
				
			||||
        "https://scalar.vector.im/_matrix/integrations/v1", | 
				
			||||
        "https://scalar.vector.im/api", | 
				
			||||
        "https://scalar-staging.vector.im/_matrix/integrations/v1", | 
				
			||||
        "https://scalar-staging.vector.im/api", | 
				
			||||
        "https://scalar-staging.riot.im/scalar/api" | 
				
			||||
    ], | 
				
			||||
    "hosting_signup_link": "https://modular.im/?utm_source=riot-web&utm_medium=web", | 
				
			||||
    "bug_report_endpoint_url": "https://riot.im/bugreports/submit", | 
				
			||||
    "roomDirectory": { | 
				
			||||
        "servers": [ | 
				
			||||
            "matrix.org" | 
				
			||||
        ] | 
				
			||||
    }, | 
				
			||||
    "piwik": { | 
				
			||||
        "url": "https://piwik.riot.im/", | 
				
			||||
        "siteId": 1, | 
				
			||||
        "policyUrl": "https://matrix.org/legal/riot-im-cookie-policy" | 
				
			||||
    }, | 
				
			||||
    "features": { | 
				
			||||
    }, | 
				
			||||
    "enable_presence_by_hs_url": { | 
				
			||||
        "https://matrix.org": false, | 
				
			||||
        "https://matrix-client.matrix.org": false | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
@ -1 +0,0 @@ | 
				
			||||
export OSSLSIGNCODE_SIGNARGS='-pkcs11module /Library/Frameworks/eToken.framework/Versions/Current/libeToken.dylib -pkcs11engine /usr/local/lib/engines/engine_pkcs11.so -certs electron_app/riot.im/New_Vector_Ltd.pem -key 0a3271cbc1ec0fd8afb37f6bbe0cd65ba08d3b4d -t http://timestamp.comodoca.com -verbose' | 
				
			||||
@ -1,658 +0,0 @@ | 
				
			||||
/* | 
				
			||||
Copyright 2016 Aviral Dasgupta | 
				
			||||
Copyright 2016 OpenMarket Ltd | 
				
			||||
Copyright 2018, 2019 New Vector Ltd | 
				
			||||
Copyright 2017, 2019 Michael Telatynski <7t3chguy@gmail.com> | 
				
			||||
 | 
				
			||||
Licensed under the Apache License, Version 2.0 (the "License"); | 
				
			||||
you may not use this file except in compliance with the License. | 
				
			||||
You may obtain a copy of the License at | 
				
			||||
 | 
				
			||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||
 | 
				
			||||
Unless required by applicable law or agreed to in writing, software | 
				
			||||
distributed under the License is distributed on an "AS IS" BASIS, | 
				
			||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
				
			||||
See the License for the specific language governing permissions and | 
				
			||||
limitations under the License. | 
				
			||||
*/ | 
				
			||||
 | 
				
			||||
// Squirrel on windows starts the app with various flags
 | 
				
			||||
// as hooks to tell us when we've been installed/uninstalled
 | 
				
			||||
// etc.
 | 
				
			||||
const checkSquirrelHooks = require('./squirrelhooks'); | 
				
			||||
if (checkSquirrelHooks()) return; | 
				
			||||
 | 
				
			||||
const argv = require('minimist')(process.argv, { | 
				
			||||
    alias: {help: "h"}, | 
				
			||||
}); | 
				
			||||
 | 
				
			||||
const {app, ipcMain, powerSaveBlocker, BrowserWindow, Menu, autoUpdater, protocol} = require('electron'); | 
				
			||||
const AutoLaunch = require('auto-launch'); | 
				
			||||
const path = require('path'); | 
				
			||||
 | 
				
			||||
const tray = require('./tray'); | 
				
			||||
const vectorMenu = require('./vectormenu'); | 
				
			||||
const webContentsHandler = require('./webcontents-handler'); | 
				
			||||
const updater = require('./updater'); | 
				
			||||
const {getProfileFromDeeplink, protocolInit, recordSSOSession} = require('./protocol'); | 
				
			||||
 | 
				
			||||
const windowStateKeeper = require('electron-window-state'); | 
				
			||||
const Store = require('electron-store'); | 
				
			||||
 | 
				
			||||
const fs = require('fs'); | 
				
			||||
const afs = fs.promises; | 
				
			||||
 | 
				
			||||
let Seshat = null; | 
				
			||||
 | 
				
			||||
try { | 
				
			||||
    Seshat = require('matrix-seshat'); | 
				
			||||
} catch (e) { | 
				
			||||
    if (e.code === "MODULE_NOT_FOUND") { | 
				
			||||
        console.log("Seshat isn't installed, event indexing is disabled."); | 
				
			||||
    } else { | 
				
			||||
        console.warn("Seshat unexpected error:", e); | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
if (argv["help"]) { | 
				
			||||
    console.log("Options:"); | 
				
			||||
    console.log("  --profile-dir {path}: Path to where to store the profile."); | 
				
			||||
    console.log("  --profile {name}:     Name of alternate profile to use, allows for running multiple accounts."); | 
				
			||||
    console.log("  --devtools:           Install and use react-devtools and react-perf."); | 
				
			||||
    console.log("  --no-update:          Disable automatic updating."); | 
				
			||||
    console.log("  --hidden:             Start the application hidden in the system tray."); | 
				
			||||
    console.log("  --help:               Displays this help message."); | 
				
			||||
    console.log("And more such as --proxy, see:" + | 
				
			||||
        "https://electronjs.org/docs/api/chrome-command-line-switches#supported-chrome-command-line-switches"); | 
				
			||||
    app.exit(); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// check if we are passed a profile in the SSO callback url
 | 
				
			||||
const userDataPathInProtocol = getProfileFromDeeplink(argv["_"]); | 
				
			||||
if (userDataPathInProtocol) { | 
				
			||||
    app.setPath('userData', userDataPathInProtocol); | 
				
			||||
} else if (argv['profile-dir']) { | 
				
			||||
    app.setPath('userData', argv['profile-dir']); | 
				
			||||
} else if (argv['profile']) { | 
				
			||||
    app.setPath('userData', `${app.getPath('userData')}-${argv['profile']}`); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
let vectorConfig = {}; | 
				
			||||
try { | 
				
			||||
    vectorConfig = require('../../webapp/config.json'); | 
				
			||||
} catch (e) { | 
				
			||||
    // it would be nice to check the error code here and bail if the config
 | 
				
			||||
    // is unparseable, but we get MODULE_NOT_FOUND in the case of a missing
 | 
				
			||||
    // file or invalid json, so node is just very unhelpful.
 | 
				
			||||
    // Continue with the defaults (ie. an empty config)
 | 
				
			||||
} | 
				
			||||
 | 
				
			||||
try { | 
				
			||||
    // Load local config and use it to override values from the one baked with the build
 | 
				
			||||
    const localConfig = require(path.join(app.getPath('userData'), 'config.json')); | 
				
			||||
 | 
				
			||||
    // If the local config has a homeserver defined, don't use the homeserver from the build
 | 
				
			||||
    // config. This is to avoid a problem where Riot thinks there are multiple homeservers
 | 
				
			||||
    // defined, and panics as a result.
 | 
				
			||||
    const homeserverProps = ['default_is_url', 'default_hs_url', 'default_server_name', 'default_server_config']; | 
				
			||||
    if (Object.keys(localConfig).find(k => homeserverProps.includes(k))) { | 
				
			||||
        // Rip out all the homeserver options from the vector config
 | 
				
			||||
        vectorConfig = Object.keys(vectorConfig) | 
				
			||||
            .filter(k => !homeserverProps.includes(k)) | 
				
			||||
            .reduce((obj, key) => {obj[key] = vectorConfig[key]; return obj;}, {}); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    vectorConfig = Object.assign(vectorConfig, localConfig); | 
				
			||||
} catch (e) { | 
				
			||||
    // Could not load local config, this is expected in most cases.
 | 
				
			||||
} | 
				
			||||
 | 
				
			||||
const eventStorePath = path.join(app.getPath('userData'), 'EventStore'); | 
				
			||||
const store = new Store({ name: "electron-config" }); | 
				
			||||
 | 
				
			||||
let eventIndex = null; | 
				
			||||
 | 
				
			||||
let mainWindow = null; | 
				
			||||
global.appQuitting = false; | 
				
			||||
 | 
				
			||||
// It's important to call `path.join` so we don't end up with the packaged asar in the final path.
 | 
				
			||||
const iconFile = `riot.${process.platform === 'win32' ? 'ico' : 'png'}`; | 
				
			||||
const iconPath = path.join(__dirname, "..", "..", "img", iconFile); | 
				
			||||
const trayConfig = { | 
				
			||||
    icon_path: iconPath, | 
				
			||||
    brand: vectorConfig.brand || 'Riot', | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
// handle uncaught errors otherwise it displays
 | 
				
			||||
// stack traces in popup dialogs, which is terrible (which
 | 
				
			||||
// it will do any time the auto update poke fails, and there's
 | 
				
			||||
// no other way to catch this error).
 | 
				
			||||
// Assuming we generally run from the console when developing,
 | 
				
			||||
// this is far preferable.
 | 
				
			||||
process.on('uncaughtException', function(error) { | 
				
			||||
    console.log('Unhandled exception', error); | 
				
			||||
}); | 
				
			||||
 | 
				
			||||
let focusHandlerAttached = false; | 
				
			||||
ipcMain.on('setBadgeCount', function(ev, count) { | 
				
			||||
    app.setBadgeCount(count); | 
				
			||||
    if (count === 0 && mainWindow) { | 
				
			||||
        mainWindow.flashFrame(false); | 
				
			||||
    } | 
				
			||||
}); | 
				
			||||
 | 
				
			||||
ipcMain.on('loudNotification', function() { | 
				
			||||
    if (process.platform === 'win32' && mainWindow && !mainWindow.isFocused() && !focusHandlerAttached) { | 
				
			||||
        mainWindow.flashFrame(true); | 
				
			||||
        mainWindow.once('focus', () => { | 
				
			||||
            mainWindow.flashFrame(false); | 
				
			||||
            focusHandlerAttached = false; | 
				
			||||
        }); | 
				
			||||
        focusHandlerAttached = true; | 
				
			||||
    } | 
				
			||||
}); | 
				
			||||
 | 
				
			||||
let powerSaveBlockerId = null; | 
				
			||||
ipcMain.on('app_onAction', function(ev, payload) { | 
				
			||||
    switch (payload.action) { | 
				
			||||
        case 'call_state': | 
				
			||||
            if (powerSaveBlockerId !== null && powerSaveBlocker.isStarted(powerSaveBlockerId)) { | 
				
			||||
                if (payload.state === 'ended') { | 
				
			||||
                    powerSaveBlocker.stop(powerSaveBlockerId); | 
				
			||||
                    powerSaveBlockerId = null; | 
				
			||||
                } | 
				
			||||
            } else { | 
				
			||||
                if (powerSaveBlockerId === null && payload.state === 'connected') { | 
				
			||||
                    powerSaveBlockerId = powerSaveBlocker.start('prevent-display-sleep'); | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
            break; | 
				
			||||
    } | 
				
			||||
}); | 
				
			||||
 | 
				
			||||
autoUpdater.on('update-downloaded', (ev, releaseNotes, releaseName, releaseDate, updateURL) => { | 
				
			||||
    if (!mainWindow) return; | 
				
			||||
    // forward to renderer
 | 
				
			||||
    mainWindow.webContents.send('update-downloaded', { | 
				
			||||
        releaseNotes, | 
				
			||||
        releaseName, | 
				
			||||
        releaseDate, | 
				
			||||
        updateURL, | 
				
			||||
    }); | 
				
			||||
}); | 
				
			||||
 | 
				
			||||
ipcMain.on('ipcCall', async function(ev, payload) { | 
				
			||||
    if (!mainWindow) return; | 
				
			||||
 | 
				
			||||
    const args = payload.args || []; | 
				
			||||
    let ret; | 
				
			||||
 | 
				
			||||
    switch (payload.name) { | 
				
			||||
        case 'getUpdateFeedUrl': | 
				
			||||
            ret = autoUpdater.getFeedURL(); | 
				
			||||
            break; | 
				
			||||
        case 'getAutoLaunchEnabled': | 
				
			||||
            ret = await launcher.isEnabled(); | 
				
			||||
            break; | 
				
			||||
        case 'setAutoLaunchEnabled': | 
				
			||||
            if (args[0]) { | 
				
			||||
                launcher.enable(); | 
				
			||||
            } else { | 
				
			||||
                launcher.disable(); | 
				
			||||
            } | 
				
			||||
            break; | 
				
			||||
        case 'getMinimizeToTrayEnabled': | 
				
			||||
            ret = tray.hasTray(); | 
				
			||||
            break; | 
				
			||||
        case 'setMinimizeToTrayEnabled': | 
				
			||||
            if (args[0]) { | 
				
			||||
                // Create trayIcon icon
 | 
				
			||||
                tray.create(trayConfig); | 
				
			||||
            } else { | 
				
			||||
                tray.destroy(); | 
				
			||||
            } | 
				
			||||
            store.set('minimizeToTray', args[0]); | 
				
			||||
            break; | 
				
			||||
        case 'getAutoHideMenuBarEnabled': | 
				
			||||
            ret = global.mainWindow.isMenuBarAutoHide(); | 
				
			||||
            break; | 
				
			||||
        case 'setAutoHideMenuBarEnabled': | 
				
			||||
            store.set('autoHideMenuBar', args[0]); | 
				
			||||
            global.mainWindow.setAutoHideMenuBar(args[0]); | 
				
			||||
            global.mainWindow.setMenuBarVisibility(!args[0]); | 
				
			||||
            break; | 
				
			||||
        case 'getAppVersion': | 
				
			||||
            ret = app.getVersion(); | 
				
			||||
            break; | 
				
			||||
        case 'focusWindow': | 
				
			||||
            if (mainWindow.isMinimized()) { | 
				
			||||
                mainWindow.restore(); | 
				
			||||
            } else if (!mainWindow.isVisible()) { | 
				
			||||
                mainWindow.show(); | 
				
			||||
            } else { | 
				
			||||
                mainWindow.focus(); | 
				
			||||
            } | 
				
			||||
            break; | 
				
			||||
        case 'getConfig': | 
				
			||||
            ret = vectorConfig; | 
				
			||||
            break; | 
				
			||||
        case 'navigateBack': | 
				
			||||
            if (mainWindow.webContents.canGoBack()) { | 
				
			||||
                mainWindow.webContents.goBack(); | 
				
			||||
            } | 
				
			||||
            break; | 
				
			||||
        case 'navigateForward': | 
				
			||||
            if (mainWindow.webContents.canGoForward()) { | 
				
			||||
                mainWindow.webContents.goForward(); | 
				
			||||
            } | 
				
			||||
            break; | 
				
			||||
        case 'startSSOFlow': | 
				
			||||
            recordSSOSession(args[0]); | 
				
			||||
            break; | 
				
			||||
 | 
				
			||||
        default: | 
				
			||||
            mainWindow.webContents.send('ipcReply', { | 
				
			||||
                id: payload.id, | 
				
			||||
                error: "Unknown IPC Call: " + payload.name, | 
				
			||||
            }); | 
				
			||||
            return; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    mainWindow.webContents.send('ipcReply', { | 
				
			||||
        id: payload.id, | 
				
			||||
        reply: ret, | 
				
			||||
    }); | 
				
			||||
}); | 
				
			||||
 | 
				
			||||
ipcMain.on('seshat', async function(ev, payload) { | 
				
			||||
    if (!mainWindow) return; | 
				
			||||
 | 
				
			||||
    const sendError = (id, e) => { | 
				
			||||
        const error = { | 
				
			||||
            message: e.message | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        mainWindow.webContents.send('seshatReply', { | 
				
			||||
            id:id, | 
				
			||||
            error: error | 
				
			||||
        }); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    const args = payload.args || []; | 
				
			||||
    let ret; | 
				
			||||
 | 
				
			||||
    switch (payload.name) { | 
				
			||||
        case 'supportsEventIndexing': | 
				
			||||
            if (Seshat === null) ret = false; | 
				
			||||
            else ret = true; | 
				
			||||
            break; | 
				
			||||
 | 
				
			||||
        case 'initEventIndex': | 
				
			||||
            if (eventIndex === null) { | 
				
			||||
                try { | 
				
			||||
                    await afs.mkdir(eventStorePath, {recursive: true}); | 
				
			||||
                    eventIndex = new Seshat(eventStorePath, {passphrase: "DEFAULT_PASSPHRASE"}); | 
				
			||||
                } catch (e) { | 
				
			||||
                    sendError(payload.id, e); | 
				
			||||
                    return; | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
            break; | 
				
			||||
 | 
				
			||||
        case 'closeEventIndex': | 
				
			||||
            eventIndex = null; | 
				
			||||
            break; | 
				
			||||
 | 
				
			||||
        case 'deleteEventIndex': | 
				
			||||
            const deleteFolderRecursive = async(p) =>  { | 
				
			||||
                for (let entry of await afs.readdir(p)) { | 
				
			||||
                    const curPath = path.join(p, entry); | 
				
			||||
                    await afs.unlink(curPath); | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
 | 
				
			||||
            try { | 
				
			||||
                await deleteFolderRecursive(eventStorePath); | 
				
			||||
            } catch (e) { | 
				
			||||
            } | 
				
			||||
 | 
				
			||||
            break; | 
				
			||||
 | 
				
			||||
        case 'isEventIndexEmpty': | 
				
			||||
            if (eventIndex === null) ret = true; | 
				
			||||
            else ret = await eventIndex.isEmpty(); | 
				
			||||
            break; | 
				
			||||
 | 
				
			||||
        case 'addEventToIndex': | 
				
			||||
            try { | 
				
			||||
                eventIndex.addEvent(args[0], args[1]); | 
				
			||||
            } catch (e) { | 
				
			||||
                sendError(payload.id, e); | 
				
			||||
                return; | 
				
			||||
            } | 
				
			||||
            break; | 
				
			||||
 | 
				
			||||
        case 'commitLiveEvents': | 
				
			||||
            try { | 
				
			||||
                ret = await eventIndex.commit(); | 
				
			||||
            } catch (e) { | 
				
			||||
                sendError(payload.id, e); | 
				
			||||
                return; | 
				
			||||
            } | 
				
			||||
            break; | 
				
			||||
 | 
				
			||||
        case 'searchEventIndex': | 
				
			||||
            try { | 
				
			||||
                ret = await eventIndex.search(args[0]); | 
				
			||||
            } catch (e) { | 
				
			||||
                sendError(payload.id, e); | 
				
			||||
                return; | 
				
			||||
            } | 
				
			||||
            break; | 
				
			||||
 | 
				
			||||
        case 'addHistoricEvents': | 
				
			||||
            if (eventIndex === null) ret = false; | 
				
			||||
            else { | 
				
			||||
                try { | 
				
			||||
                    ret = await eventIndex.addHistoricEvents( | 
				
			||||
                        args[0], args[1], args[2]); | 
				
			||||
                } catch (e) { | 
				
			||||
                    sendError(payload.id, e); | 
				
			||||
                    return; | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
            break; | 
				
			||||
 | 
				
			||||
        case 'getStats': | 
				
			||||
            if (eventIndex === null) ret = 0; | 
				
			||||
            else { | 
				
			||||
                try { | 
				
			||||
                    ret = await eventIndex.getStats(); | 
				
			||||
                } catch (e) { | 
				
			||||
                    sendError(payload.id, e); | 
				
			||||
                    return; | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
            break; | 
				
			||||
 | 
				
			||||
        case 'removeCrawlerCheckpoint': | 
				
			||||
            if (eventIndex === null) ret = false; | 
				
			||||
            else { | 
				
			||||
                try { | 
				
			||||
                    ret = await eventIndex.removeCrawlerCheckpoint(args[0]); | 
				
			||||
                } catch (e) { | 
				
			||||
                    sendError(payload.id, e); | 
				
			||||
                    return; | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
            break; | 
				
			||||
 | 
				
			||||
        case 'addCrawlerCheckpoint': | 
				
			||||
            if (eventIndex === null) ret = false; | 
				
			||||
            else { | 
				
			||||
                try { | 
				
			||||
                    ret = await eventIndex.addCrawlerCheckpoint(args[0]); | 
				
			||||
                } catch (e) { | 
				
			||||
                    sendError(payload.id, e); | 
				
			||||
                    return; | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
            break; | 
				
			||||
 | 
				
			||||
        case 'loadFileEvents': | 
				
			||||
            if (eventIndex === null) ret = []; | 
				
			||||
            else { | 
				
			||||
                try { | 
				
			||||
                    ret = await eventIndex.loadFileEvents(args[0]); | 
				
			||||
                } catch (e) { | 
				
			||||
                    sendError(payload.id, e); | 
				
			||||
                    return; | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
            break; | 
				
			||||
 | 
				
			||||
        case 'loadCheckpoints': | 
				
			||||
            if (eventIndex === null) ret = []; | 
				
			||||
            else { | 
				
			||||
                try { | 
				
			||||
                    ret = await eventIndex.loadCheckpoints(); | 
				
			||||
                } catch (e) { | 
				
			||||
                    ret = []; | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
            break; | 
				
			||||
 | 
				
			||||
        default: | 
				
			||||
            mainWindow.webContents.send('seshatReply', { | 
				
			||||
                id: payload.id, | 
				
			||||
                error: "Unknown IPC Call: " + payload.name, | 
				
			||||
            }); | 
				
			||||
            return; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    mainWindow.webContents.send('seshatReply', { | 
				
			||||
        id: payload.id, | 
				
			||||
        reply: ret, | 
				
			||||
    }); | 
				
			||||
}); | 
				
			||||
 | 
				
			||||
app.commandLine.appendSwitch('--enable-usermedia-screen-capturing'); | 
				
			||||
 | 
				
			||||
const gotLock = app.requestSingleInstanceLock(); | 
				
			||||
if (!gotLock) { | 
				
			||||
    console.log('Other instance detected: exiting'); | 
				
			||||
    app.exit(); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// do this after we know we are the primary instance of the app
 | 
				
			||||
protocolInit(); | 
				
			||||
 | 
				
			||||
const launcher = new AutoLaunch({ | 
				
			||||
    name: vectorConfig.brand || 'Riot', | 
				
			||||
    isHidden: true, | 
				
			||||
    mac: { | 
				
			||||
        useLaunchAgent: true, | 
				
			||||
    }, | 
				
			||||
}); | 
				
			||||
 | 
				
			||||
// Register the scheme the app is served from as 'standard'
 | 
				
			||||
// which allows things like relative URLs and IndexedDB to
 | 
				
			||||
// work.
 | 
				
			||||
// Also mark it as secure (ie. accessing resources from this
 | 
				
			||||
// protocol and HTTPS won't trigger mixed content warnings).
 | 
				
			||||
protocol.registerSchemesAsPrivileged([{ | 
				
			||||
    scheme: 'vector', | 
				
			||||
    privileges: { | 
				
			||||
        standard: true, | 
				
			||||
        secure: true, | 
				
			||||
        supportFetchAPI: true, | 
				
			||||
    }, | 
				
			||||
}]); | 
				
			||||
 | 
				
			||||
app.on('ready', () => { | 
				
			||||
    if (argv['devtools']) { | 
				
			||||
        try { | 
				
			||||
            const { default: installExt, REACT_DEVELOPER_TOOLS, REACT_PERF } = require('electron-devtools-installer'); | 
				
			||||
            installExt(REACT_DEVELOPER_TOOLS) | 
				
			||||
                .then((name) => console.log(`Added Extension: ${name}`)) | 
				
			||||
                .catch((err) => console.log('An error occurred: ', err)); | 
				
			||||
            installExt(REACT_PERF) | 
				
			||||
                .then((name) => console.log(`Added Extension: ${name}`)) | 
				
			||||
                .catch((err) => console.log('An error occurred: ', err)); | 
				
			||||
        } catch (e) { | 
				
			||||
            console.log(e); | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    protocol.registerFileProtocol('vector', (request, callback) => { | 
				
			||||
        if (request.method !== 'GET') { | 
				
			||||
            callback({error: -322}); // METHOD_NOT_SUPPORTED from chromium/src/net/base/net_error_list.h
 | 
				
			||||
            return null; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        const parsedUrl = new URL(request.url); | 
				
			||||
        if (parsedUrl.protocol !== 'vector:') { | 
				
			||||
            callback({error: -302}); // UNKNOWN_URL_SCHEME
 | 
				
			||||
            return; | 
				
			||||
        } | 
				
			||||
        if (parsedUrl.host !== 'vector') { | 
				
			||||
            callback({error: -105}); // NAME_NOT_RESOLVED
 | 
				
			||||
            return; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        const target = parsedUrl.pathname.split('/'); | 
				
			||||
 | 
				
			||||
        // path starts with a '/'
 | 
				
			||||
        if (target[0] !== '') { | 
				
			||||
            callback({error: -6}); // FILE_NOT_FOUND
 | 
				
			||||
            return; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        if (target[target.length - 1] == '') { | 
				
			||||
            target[target.length - 1] = 'index.html'; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        let baseDir; | 
				
			||||
        // first part of the path determines where we serve from
 | 
				
			||||
        if (target[1] === 'webapp') { | 
				
			||||
            baseDir = __dirname + "/../../webapp"; | 
				
			||||
        } else { | 
				
			||||
            callback({error: -6}); // FILE_NOT_FOUND
 | 
				
			||||
            return; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        // Normalise the base dir and the target path separately, then make sure
 | 
				
			||||
        // the target path isn't trying to back out beyond its root
 | 
				
			||||
        baseDir = path.normalize(baseDir); | 
				
			||||
 | 
				
			||||
        const relTarget = path.normalize(path.join(...target.slice(2))); | 
				
			||||
        if (relTarget.startsWith('..')) { | 
				
			||||
            callback({error: -6}); // FILE_NOT_FOUND
 | 
				
			||||
            return; | 
				
			||||
        } | 
				
			||||
        const absTarget = path.join(baseDir, relTarget); | 
				
			||||
 | 
				
			||||
        callback({ | 
				
			||||
            path: absTarget, | 
				
			||||
        }); | 
				
			||||
    }, (error) => { | 
				
			||||
        if (error) console.error('Failed to register protocol'); | 
				
			||||
    }); | 
				
			||||
 | 
				
			||||
    if (argv['no-update']) { | 
				
			||||
        console.log('Auto update disabled via command line flag "--no-update"'); | 
				
			||||
    } else if (vectorConfig['update_base_url']) { | 
				
			||||
        console.log(`Starting auto update with base URL: ${vectorConfig['update_base_url']}`); | 
				
			||||
        updater.start(vectorConfig['update_base_url']); | 
				
			||||
    } else { | 
				
			||||
        console.log('No update_base_url is defined: auto update is disabled'); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    // Load the previous window state with fallback to defaults
 | 
				
			||||
    const mainWindowState = windowStateKeeper({ | 
				
			||||
        defaultWidth: 1024, | 
				
			||||
        defaultHeight: 768, | 
				
			||||
    }); | 
				
			||||
 | 
				
			||||
    const preloadScript = path.normalize(`${__dirname}/preload.js`); | 
				
			||||
    mainWindow = global.mainWindow = new BrowserWindow({ | 
				
			||||
        icon: iconPath, | 
				
			||||
        show: false, | 
				
			||||
        autoHideMenuBar: store.get('autoHideMenuBar', true), | 
				
			||||
 | 
				
			||||
        x: mainWindowState.x, | 
				
			||||
        y: mainWindowState.y, | 
				
			||||
        width: mainWindowState.width, | 
				
			||||
        height: mainWindowState.height, | 
				
			||||
        webPreferences: { | 
				
			||||
            preload: preloadScript, | 
				
			||||
            nodeIntegration: false, | 
				
			||||
            sandbox: true, | 
				
			||||
            enableRemoteModule: false, | 
				
			||||
            // We don't use this: it's useful for the preload script to
 | 
				
			||||
            // share a context with the main page so we can give select
 | 
				
			||||
            // objects to the main page. The sandbox option isolates the
 | 
				
			||||
            // main page from the background script.
 | 
				
			||||
            contextIsolation: false, | 
				
			||||
            webgl: false, | 
				
			||||
        }, | 
				
			||||
    }); | 
				
			||||
    mainWindow.loadURL('vector://vector/webapp/'); | 
				
			||||
    Menu.setApplicationMenu(vectorMenu); | 
				
			||||
 | 
				
			||||
    // Create trayIcon icon
 | 
				
			||||
    if (store.get('minimizeToTray', true)) tray.create(trayConfig); | 
				
			||||
 | 
				
			||||
    mainWindow.once('ready-to-show', () => { | 
				
			||||
        mainWindowState.manage(mainWindow); | 
				
			||||
 | 
				
			||||
        if (!argv['hidden']) { | 
				
			||||
            mainWindow.show(); | 
				
			||||
        } else { | 
				
			||||
            // hide here explicitly because window manage above sometimes shows it
 | 
				
			||||
            mainWindow.hide(); | 
				
			||||
        } | 
				
			||||
    }); | 
				
			||||
 | 
				
			||||
    mainWindow.on('closed', () => { | 
				
			||||
        mainWindow = global.mainWindow = null; | 
				
			||||
    }); | 
				
			||||
    mainWindow.on('close', (e) => { | 
				
			||||
        // If we are not quitting and have a tray icon then minimize to tray
 | 
				
			||||
        if (!global.appQuitting && (tray.hasTray() || process.platform === 'darwin')) { | 
				
			||||
            // On Mac, closing the window just hides it
 | 
				
			||||
            // (this is generally how single-window Mac apps
 | 
				
			||||
            // behave, eg. Mail.app)
 | 
				
			||||
            e.preventDefault(); | 
				
			||||
            mainWindow.hide(); | 
				
			||||
            return false; | 
				
			||||
        } | 
				
			||||
    }); | 
				
			||||
 | 
				
			||||
    if (process.platform === 'win32') { | 
				
			||||
        // Handle forward/backward mouse buttons in Windows
 | 
				
			||||
        mainWindow.on('app-command', (e, cmd) => { | 
				
			||||
            if (cmd === 'browser-backward' && mainWindow.webContents.canGoBack()) { | 
				
			||||
                mainWindow.webContents.goBack(); | 
				
			||||
            } else if (cmd === 'browser-forward' && mainWindow.webContents.canGoForward()) { | 
				
			||||
                mainWindow.webContents.goForward(); | 
				
			||||
            } | 
				
			||||
        }); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    webContentsHandler(mainWindow.webContents); | 
				
			||||
}); | 
				
			||||
 | 
				
			||||
app.on('window-all-closed', () => { | 
				
			||||
    app.quit(); | 
				
			||||
}); | 
				
			||||
 | 
				
			||||
app.on('activate', () => { | 
				
			||||
    mainWindow.show(); | 
				
			||||
}); | 
				
			||||
 | 
				
			||||
app.on('before-quit', () => { | 
				
			||||
    global.appQuitting = true; | 
				
			||||
    if (mainWindow) { | 
				
			||||
        mainWindow.webContents.send('before-quit'); | 
				
			||||
    } | 
				
			||||
}); | 
				
			||||
 | 
				
			||||
app.on('second-instance', (ev, commandLine, workingDirectory) => { | 
				
			||||
    // If other instance launched with --hidden then skip showing window
 | 
				
			||||
    if (commandLine.includes('--hidden')) return; | 
				
			||||
 | 
				
			||||
    // Someone tried to run a second instance, we should focus our window.
 | 
				
			||||
    if (mainWindow) { | 
				
			||||
        if (!mainWindow.isVisible()) mainWindow.show(); | 
				
			||||
        if (mainWindow.isMinimized()) mainWindow.restore(); | 
				
			||||
        mainWindow.focus(); | 
				
			||||
    } | 
				
			||||
}); | 
				
			||||
 | 
				
			||||
// Set the App User Model ID to match what the squirrel
 | 
				
			||||
// installer uses for the shortcut icon.
 | 
				
			||||
// This makes notifications work on windows 8.1 (and is
 | 
				
			||||
// a noop on other platforms).
 | 
				
			||||
app.setAppUserModelId('com.squirrel.riot-web.Riot'); | 
				
			||||
@ -1,20 +0,0 @@ | 
				
			||||
/* | 
				
			||||
Copyright 2018, 2019 New Vector Ltd | 
				
			||||
 | 
				
			||||
Licensed under the Apache License, Version 2.0 (the "License"); | 
				
			||||
you may not use this file except in compliance with the License. | 
				
			||||
You may obtain a copy of the License at | 
				
			||||
 | 
				
			||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||
 | 
				
			||||
Unless required by applicable law or agreed to in writing, software | 
				
			||||
distributed under the License is distributed on an "AS IS" BASIS, | 
				
			||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
				
			||||
See the License for the specific language governing permissions and | 
				
			||||
limitations under the License. | 
				
			||||
*/ | 
				
			||||
 | 
				
			||||
const { ipcRenderer } = require('electron'); | 
				
			||||
 | 
				
			||||
// expose ipcRenderer to the renderer process
 | 
				
			||||
window.ipcRenderer = ipcRenderer; | 
				
			||||
@ -1,104 +0,0 @@ | 
				
			||||
/* | 
				
			||||
Copyright 2020 The Matrix.org Foundation C.I.C. | 
				
			||||
 | 
				
			||||
Licensed under the Apache License, Version 2.0 (the "License"); | 
				
			||||
you may not use this file except in compliance with the License. | 
				
			||||
You may obtain a copy of the License at | 
				
			||||
 | 
				
			||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||
 | 
				
			||||
Unless required by applicable law or agreed to in writing, software | 
				
			||||
distributed under the License is distributed on an "AS IS" BASIS, | 
				
			||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
				
			||||
See the License for the specific language governing permissions and | 
				
			||||
limitations under the License. | 
				
			||||
*/ | 
				
			||||
 | 
				
			||||
const {app} = require("electron"); | 
				
			||||
const path = require("path"); | 
				
			||||
const fs = require("fs"); | 
				
			||||
 | 
				
			||||
const PROTOCOL = "riot://"; | 
				
			||||
const SEARCH_PARAM = "riot-desktop-ssoid"; | 
				
			||||
const STORE_FILE_NAME = "sso-sessions.json"; | 
				
			||||
 | 
				
			||||
// we getPath userData before electron-main changes it, so this is the default value
 | 
				
			||||
const storePath = path.join(app.getPath("userData"), STORE_FILE_NAME); | 
				
			||||
 | 
				
			||||
const processUrl = (url) => { | 
				
			||||
    if (!global.mainWindow) return; | 
				
			||||
    console.log("Handling link: ", url); | 
				
			||||
    global.mainWindow.loadURL(url.replace(PROTOCOL, "vector://")); | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
const readStore = () => { | 
				
			||||
    try { | 
				
			||||
        const s = fs.readFileSync(storePath, { encoding: "utf8" }); | 
				
			||||
        const o = JSON.parse(s); | 
				
			||||
        return typeof o === "object" ? o : {}; | 
				
			||||
    } catch (e) { | 
				
			||||
        return {}; | 
				
			||||
    } | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
const writeStore = (data) => { | 
				
			||||
    fs.writeFileSync(storePath, JSON.stringify(data)); | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
module.exports = { | 
				
			||||
    recordSSOSession: (sessionID) => { | 
				
			||||
        const userDataPath = app.getPath('userData'); | 
				
			||||
        const store = readStore(); | 
				
			||||
        for (const key in store) { | 
				
			||||
            // ensure each instance only has one (the latest) session ID to prevent the file growing unbounded
 | 
				
			||||
            if (store[key] === userDataPath) { | 
				
			||||
                delete store[key]; | 
				
			||||
                break; | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
        store[sessionID] = userDataPath; | 
				
			||||
        writeStore(store); | 
				
			||||
    }, | 
				
			||||
    getProfileFromDeeplink: (args) => { | 
				
			||||
        // check if we are passed a profile in the SSO callback url
 | 
				
			||||
        const deeplinkUrl = args.find(arg => arg.startsWith('riot://')); | 
				
			||||
        if (deeplinkUrl && deeplinkUrl.includes(SEARCH_PARAM)) { | 
				
			||||
            const parsedUrl = new URL(deeplinkUrl); | 
				
			||||
            if (parsedUrl.protocol === 'riot:') { | 
				
			||||
                const ssoID = parsedUrl.searchParams.get(SEARCH_PARAM); | 
				
			||||
                const store = readStore(); | 
				
			||||
                console.log("Forwarding to profile: ", store[ssoID]); | 
				
			||||
                return store[ssoID]; | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
    }, | 
				
			||||
    protocolInit: () => { | 
				
			||||
        // get all args except `hidden` as it'd mean the app would not get focused
 | 
				
			||||
        // XXX: passing args to protocol handlers only works on Windows, so unpackaged deep-linking
 | 
				
			||||
        // --profile/--profile-dir are passed via the SEARCH_PARAM var in the callback url
 | 
				
			||||
        const args = process.argv.slice(1).filter(arg => arg !== "--hidden" && arg !== "-hidden"); | 
				
			||||
        if (app.isPackaged) { | 
				
			||||
            app.setAsDefaultProtocolClient('riot', process.execPath, args); | 
				
			||||
        } else if (process.platform === 'win32') { // on Mac/Linux this would just cause the electron binary to open
 | 
				
			||||
            // special handler for running without being packaged, e.g `electron .` by passing our app path to electron
 | 
				
			||||
            app.setAsDefaultProtocolClient('riot', process.execPath, [app.getAppPath(), ...args]); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        if (process.platform === 'darwin') { | 
				
			||||
            // Protocol handler for macos
 | 
				
			||||
            app.on('open-url', function(ev, url) { | 
				
			||||
                ev.preventDefault(); | 
				
			||||
                processUrl(url); | 
				
			||||
            }); | 
				
			||||
        } else { | 
				
			||||
            // Protocol handler for win32/Linux
 | 
				
			||||
            app.on('second-instance', (ev, commandLine) => { | 
				
			||||
                const url = commandLine[commandLine.length - 1]; | 
				
			||||
                if (!url.startsWith(PROTOCOL)) return; | 
				
			||||
                processUrl(url); | 
				
			||||
            }); | 
				
			||||
        } | 
				
			||||
    }, | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
@ -1,51 +0,0 @@ | 
				
			||||
/* | 
				
			||||
Copyright 2017 OpenMarket Ltd | 
				
			||||
 | 
				
			||||
Licensed under the Apache License, Version 2.0 (the "License"); | 
				
			||||
you may not use this file except in compliance with the License. | 
				
			||||
You may obtain a copy of the License at | 
				
			||||
 | 
				
			||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||
 | 
				
			||||
Unless required by applicable law or agreed to in writing, software | 
				
			||||
distributed under the License is distributed on an "AS IS" BASIS, | 
				
			||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
				
			||||
See the License for the specific language governing permissions and | 
				
			||||
limitations under the License. | 
				
			||||
*/ | 
				
			||||
 | 
				
			||||
const path = require('path'); | 
				
			||||
const spawn = require('child_process').spawn; | 
				
			||||
const {app} = require('electron'); | 
				
			||||
 | 
				
			||||
function runUpdateExe(args, done) { | 
				
			||||
    // Invokes Squirrel's Update.exe which will do things for us like create shortcuts
 | 
				
			||||
    // Note that there's an Update.exe in the app-x.x.x directory and one in the parent
 | 
				
			||||
    // directory: we need to run the one in the parent directory, because it discovers
 | 
				
			||||
    // information about the app by inspecting the directory it's run from.
 | 
				
			||||
    const updateExe = path.resolve(path.dirname(process.execPath), '..', 'Update.exe'); | 
				
			||||
    console.log(`Spawning '${updateExe}' with args '${args}'`); | 
				
			||||
    spawn(updateExe, args, { | 
				
			||||
      detached: true, | 
				
			||||
    }).on('close', done); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
function checkSquirrelHooks() { | 
				
			||||
    if (process.platform !== 'win32') return false; | 
				
			||||
 | 
				
			||||
    const cmd = process.argv[1]; | 
				
			||||
    const target = path.basename(process.execPath); | 
				
			||||
    if (cmd === '--squirrel-install' || cmd === '--squirrel-updated') { | 
				
			||||
        runUpdateExe(['--createShortcut=' + target + ''], app.quit); | 
				
			||||
        return true; | 
				
			||||
    } else if (cmd === '--squirrel-uninstall') { | 
				
			||||
        runUpdateExe(['--removeShortcut=' + target + ''], app.quit); | 
				
			||||
        return true; | 
				
			||||
    } else if (cmd === '--squirrel-obsolete') { | 
				
			||||
        app.quit(); | 
				
			||||
        return true; | 
				
			||||
    } | 
				
			||||
    return false; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
module.exports = checkSquirrelHooks; | 
				
			||||
@ -1,106 +0,0 @@ | 
				
			||||
/* | 
				
			||||
Copyright 2017 Karl Glatz <karl@glatz.biz> | 
				
			||||
Copyright 2017 OpenMarket Ltd | 
				
			||||
 | 
				
			||||
Licensed under the Apache License, Version 2.0 (the "License"); | 
				
			||||
you may not use this file except in compliance with the License. | 
				
			||||
You may obtain a copy of the License at | 
				
			||||
 | 
				
			||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||
 | 
				
			||||
Unless required by applicable law or agreed to in writing, software | 
				
			||||
distributed under the License is distributed on an "AS IS" BASIS, | 
				
			||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
				
			||||
See the License for the specific language governing permissions and | 
				
			||||
limitations under the License. | 
				
			||||
*/ | 
				
			||||
 | 
				
			||||
const {app, Tray, Menu, nativeImage} = require('electron'); | 
				
			||||
const pngToIco = require('png-to-ico'); | 
				
			||||
const path = require('path'); | 
				
			||||
const fs = require('fs'); | 
				
			||||
 | 
				
			||||
let trayIcon = null; | 
				
			||||
 | 
				
			||||
exports.hasTray = function hasTray() { | 
				
			||||
    return (trayIcon !== null); | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
exports.destroy = function() { | 
				
			||||
    if (trayIcon) { | 
				
			||||
        trayIcon.destroy(); | 
				
			||||
        trayIcon = null; | 
				
			||||
    } | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
exports.create = function(config) { | 
				
			||||
    // no trays on darwin
 | 
				
			||||
    if (process.platform === 'darwin' || trayIcon) return; | 
				
			||||
 | 
				
			||||
    const toggleWin = function() { | 
				
			||||
        if (global.mainWindow.isVisible() && !global.mainWindow.isMinimized()) { | 
				
			||||
            global.mainWindow.hide(); | 
				
			||||
        } else { | 
				
			||||
            if (global.mainWindow.isMinimized()) global.mainWindow.restore(); | 
				
			||||
            if (!global.mainWindow.isVisible()) global.mainWindow.show(); | 
				
			||||
            global.mainWindow.focus(); | 
				
			||||
        } | 
				
			||||
    }; | 
				
			||||
 | 
				
			||||
    const contextMenu = Menu.buildFromTemplate([ | 
				
			||||
        { | 
				
			||||
            label: `Show/Hide ${config.brand}`, | 
				
			||||
            click: toggleWin, | 
				
			||||
        }, | 
				
			||||
        { type: 'separator' }, | 
				
			||||
        { | 
				
			||||
            label: 'Quit', | 
				
			||||
            click: function() { | 
				
			||||
                app.quit(); | 
				
			||||
            }, | 
				
			||||
        }, | 
				
			||||
    ]); | 
				
			||||
 | 
				
			||||
    const defaultIcon = nativeImage.createFromPath(config.icon_path); | 
				
			||||
 | 
				
			||||
    trayIcon = new Tray(defaultIcon); | 
				
			||||
    trayIcon.setToolTip(config.brand); | 
				
			||||
    trayIcon.setContextMenu(contextMenu); | 
				
			||||
    trayIcon.on('click', toggleWin); | 
				
			||||
 | 
				
			||||
    let lastFavicon = null; | 
				
			||||
    global.mainWindow.webContents.on('page-favicon-updated', async function(ev, favicons) { | 
				
			||||
        if (!favicons || favicons.length <= 0 || !favicons[0].startsWith('data:')) { | 
				
			||||
            if (lastFavicon !== null) { | 
				
			||||
                global.mainWindow.setIcon(defaultIcon); | 
				
			||||
                trayIcon.setImage(defaultIcon); | 
				
			||||
                lastFavicon = null; | 
				
			||||
            } | 
				
			||||
            return; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        // No need to change, shortcut
 | 
				
			||||
        if (favicons[0] === lastFavicon) return; | 
				
			||||
        lastFavicon = favicons[0]; | 
				
			||||
 | 
				
			||||
        let newFavicon = nativeImage.createFromDataURL(favicons[0]); | 
				
			||||
 | 
				
			||||
        // Windows likes ico's too much.
 | 
				
			||||
        if (process.platform === 'win32') { | 
				
			||||
            try { | 
				
			||||
                const icoPath = path.join(app.getPath('temp'), 'win32_riot_icon.ico'); | 
				
			||||
                fs.writeFileSync(icoPath, await pngToIco(newFavicon.toPNG())); | 
				
			||||
                newFavicon = nativeImage.createFromPath(icoPath); | 
				
			||||
            } catch (e) { | 
				
			||||
                console.error("Failed to make win32 ico", e); | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        trayIcon.setImage(newFavicon); | 
				
			||||
        global.mainWindow.setIcon(newFavicon); | 
				
			||||
    }); | 
				
			||||
 | 
				
			||||
    global.mainWindow.webContents.on('page-title-updated', function(ev, title) { | 
				
			||||
        trayIcon.setToolTip(title); | 
				
			||||
    }); | 
				
			||||
}; | 
				
			||||
@ -1,84 +0,0 @@ | 
				
			||||
const { app, autoUpdater, ipcMain } = require('electron'); | 
				
			||||
 | 
				
			||||
const UPDATE_POLL_INTERVAL_MS = 60 * 60 * 1000; | 
				
			||||
const INITIAL_UPDATE_DELAY_MS = 30 * 1000; | 
				
			||||
 | 
				
			||||
function installUpdate() { | 
				
			||||
    // for some reason, quitAndInstall does not fire the
 | 
				
			||||
    // before-quit event, so we need to set the flag here.
 | 
				
			||||
    global.appQuitting = true; | 
				
			||||
    autoUpdater.quitAndInstall(); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
function pollForUpdates() { | 
				
			||||
    try { | 
				
			||||
        autoUpdater.checkForUpdates(); | 
				
			||||
    } catch (e) { | 
				
			||||
        console.log('Couldn\'t check for update', e); | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
module.exports = {}; | 
				
			||||
module.exports.start = function startAutoUpdate(updateBaseUrl) { | 
				
			||||
    if (updateBaseUrl.slice(-1) !== '/') { | 
				
			||||
        updateBaseUrl = updateBaseUrl + '/'; | 
				
			||||
    } | 
				
			||||
    try { | 
				
			||||
        let url; | 
				
			||||
        // For reasons best known to Squirrel, the way it checks for updates
 | 
				
			||||
        // is completely different between macOS and windows. On macOS, it
 | 
				
			||||
        // hits a URL that either gives it a 200 with some json or
 | 
				
			||||
        // 204 No Content. On windows it takes a base path and looks for
 | 
				
			||||
        // files under that path.
 | 
				
			||||
        if (process.platform === 'darwin') { | 
				
			||||
            // include the current version in the URL we hit. Electron doesn't add
 | 
				
			||||
            // it anywhere (apart from the User-Agent) so it's up to us. We could
 | 
				
			||||
            // (and previously did) just use the User-Agent, but this doesn't
 | 
				
			||||
            // rely on NSURLConnection setting the User-Agent to what we expect,
 | 
				
			||||
            // and also acts as a convenient cache-buster to ensure that when the
 | 
				
			||||
            // app updates it always gets a fresh value to avoid update-looping.
 | 
				
			||||
            url = `${updateBaseUrl}macos/?localVersion=${encodeURIComponent(app.getVersion())}`; | 
				
			||||
 | 
				
			||||
        } else if (process.platform === 'win32') { | 
				
			||||
            url = `${updateBaseUrl}win32/${process.arch}/`; | 
				
			||||
        } else { | 
				
			||||
            // Squirrel / electron only supports auto-update on these two platforms.
 | 
				
			||||
            // I'm not even going to try to guess which feed style they'd use if they
 | 
				
			||||
            // implemented it on Linux, or if it would be different again.
 | 
				
			||||
            console.log('Auto update not supported on this platform'); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        if (url) { | 
				
			||||
            autoUpdater.setFeedURL(url); | 
				
			||||
            // We check for updates ourselves rather than using 'updater' because we need to
 | 
				
			||||
            // do it in the main process (and we don't really need to check every 10 minutes:
 | 
				
			||||
            // every hour should be just fine for a desktop app)
 | 
				
			||||
            // However, we still let the main window listen for the update events.
 | 
				
			||||
            // We also wait a short time before checking for updates the first time because
 | 
				
			||||
            // of squirrel on windows and it taking a small amount of time to release a
 | 
				
			||||
            // lock file.
 | 
				
			||||
            setTimeout(pollForUpdates, INITIAL_UPDATE_DELAY_MS); | 
				
			||||
            setInterval(pollForUpdates, UPDATE_POLL_INTERVAL_MS); | 
				
			||||
        } | 
				
			||||
    } catch (err) { | 
				
			||||
        // will fail if running in debug mode
 | 
				
			||||
        console.log('Couldn\'t enable update checking', err); | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
ipcMain.on('install_update', installUpdate); | 
				
			||||
ipcMain.on('check_updates', pollForUpdates); | 
				
			||||
 | 
				
			||||
function ipcChannelSendUpdateStatus(status) { | 
				
			||||
    if (global.mainWindow) { | 
				
			||||
        global.mainWindow.webContents.send('check_updates', status); | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
autoUpdater.on('update-available', function() { | 
				
			||||
    ipcChannelSendUpdateStatus(true); | 
				
			||||
}).on('update-not-available', function() { | 
				
			||||
    ipcChannelSendUpdateStatus(false); | 
				
			||||
}).on('error', function(error) { | 
				
			||||
    ipcChannelSendUpdateStatus(error.message); | 
				
			||||
}); | 
				
			||||
@ -1,144 +0,0 @@ | 
				
			||||
/* | 
				
			||||
Copyright 2016 OpenMarket Ltd | 
				
			||||
 | 
				
			||||
Licensed under the Apache License, Version 2.0 (the "License"); | 
				
			||||
you may not use this file except in compliance with the License. | 
				
			||||
You may obtain a copy of the License at | 
				
			||||
 | 
				
			||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||
 | 
				
			||||
Unless required by applicable law or agreed to in writing, software | 
				
			||||
distributed under the License is distributed on an "AS IS" BASIS, | 
				
			||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
				
			||||
See the License for the specific language governing permissions and | 
				
			||||
limitations under the License. | 
				
			||||
*/ | 
				
			||||
 | 
				
			||||
const {app, shell, Menu} = require('electron'); | 
				
			||||
 | 
				
			||||
// Menu template from http://electron.atom.io/docs/api/menu/, edited
 | 
				
			||||
const template = [ | 
				
			||||
    { | 
				
			||||
        label: '&Edit', | 
				
			||||
        submenu: [ | 
				
			||||
            { role: 'undo' }, | 
				
			||||
            { role: 'redo' }, | 
				
			||||
            { type: 'separator' }, | 
				
			||||
            { role: 'cut' }, | 
				
			||||
            { role: 'copy' }, | 
				
			||||
            { role: 'paste' }, | 
				
			||||
            { role: 'pasteandmatchstyle' }, | 
				
			||||
            { role: 'delete' }, | 
				
			||||
            { role: 'selectall' }, | 
				
			||||
        ], | 
				
			||||
    }, | 
				
			||||
    { | 
				
			||||
        label: '&View', | 
				
			||||
        submenu: [ | 
				
			||||
            { type: 'separator' }, | 
				
			||||
            { role: 'resetzoom' }, | 
				
			||||
            { role: 'zoomin', accelerator: 'CommandOrControl+=' }, | 
				
			||||
            { role: 'zoomout' }, | 
				
			||||
            { type: 'separator' }, | 
				
			||||
            { | 
				
			||||
                label: 'Preferences', | 
				
			||||
                accelerator: 'Command+,', // Mac-only accelerator
 | 
				
			||||
                click() { global.mainWindow.webContents.send('preferences'); }, | 
				
			||||
            }, | 
				
			||||
            { role: 'togglefullscreen' }, | 
				
			||||
            { role: 'toggledevtools' }, | 
				
			||||
        ], | 
				
			||||
    }, | 
				
			||||
    { | 
				
			||||
        label: '&Window', | 
				
			||||
        role: 'window', | 
				
			||||
        submenu: [ | 
				
			||||
            { role: 'minimize' }, | 
				
			||||
            { role: 'close' }, | 
				
			||||
        ], | 
				
			||||
    }, | 
				
			||||
    { | 
				
			||||
        label: '&Help', | 
				
			||||
        role: 'help', | 
				
			||||
        submenu: [ | 
				
			||||
            { | 
				
			||||
                label: 'Riot Help', | 
				
			||||
                click() { shell.openExternal('https://about.riot.im/help'); }, | 
				
			||||
            }, | 
				
			||||
        ], | 
				
			||||
    }, | 
				
			||||
]; | 
				
			||||
 | 
				
			||||
// macOS has specific menu conventions...
 | 
				
			||||
if (process.platform === 'darwin') { | 
				
			||||
    // first macOS menu is the name of the app
 | 
				
			||||
    const name = app.getName(); | 
				
			||||
    template.unshift({ | 
				
			||||
        label: name, | 
				
			||||
        submenu: [ | 
				
			||||
            { role: 'about' }, | 
				
			||||
            { type: 'separator' }, | 
				
			||||
            { | 
				
			||||
                role: 'services', | 
				
			||||
                submenu: [], | 
				
			||||
            }, | 
				
			||||
            { type: 'separator' }, | 
				
			||||
            { role: 'hide' }, | 
				
			||||
            { role: 'hideothers' }, | 
				
			||||
            { role: 'unhide' }, | 
				
			||||
            { type: 'separator' }, | 
				
			||||
            { role: 'quit' }, | 
				
			||||
        ], | 
				
			||||
    }); | 
				
			||||
    // Edit menu.
 | 
				
			||||
    // This has a 'speech' section on macOS
 | 
				
			||||
    template[1].submenu.push( | 
				
			||||
        { type: 'separator' }, | 
				
			||||
        { | 
				
			||||
            label: 'Speech', | 
				
			||||
            submenu: [ | 
				
			||||
                { role: 'startspeaking' }, | 
				
			||||
                { role: 'stopspeaking' }, | 
				
			||||
            ], | 
				
			||||
        }); | 
				
			||||
 | 
				
			||||
    // Window menu.
 | 
				
			||||
    // This also has specific functionality on macOS
 | 
				
			||||
    template[3].submenu = [ | 
				
			||||
        { | 
				
			||||
            label: 'Close', | 
				
			||||
            accelerator: 'CmdOrCtrl+W', | 
				
			||||
            role: 'close', | 
				
			||||
        }, | 
				
			||||
        { | 
				
			||||
            label: 'Minimize', | 
				
			||||
            accelerator: 'CmdOrCtrl+M', | 
				
			||||
            role: 'minimize', | 
				
			||||
        }, | 
				
			||||
        { | 
				
			||||
            label: 'Zoom', | 
				
			||||
            role: 'zoom', | 
				
			||||
        }, | 
				
			||||
        { | 
				
			||||
            type: 'separator', | 
				
			||||
        }, | 
				
			||||
        { | 
				
			||||
            label: 'Bring All to Front', | 
				
			||||
            role: 'front', | 
				
			||||
        }, | 
				
			||||
    ]; | 
				
			||||
} else { | 
				
			||||
    template.unshift({ | 
				
			||||
        label: '&File', | 
				
			||||
        submenu: [ | 
				
			||||
            // For some reason, 'about' does not seem to work on windows.
 | 
				
			||||
            /*{ | 
				
			||||
                role: 'about' | 
				
			||||
            },*/ | 
				
			||||
            { role: 'quit' }, | 
				
			||||
        ], | 
				
			||||
    }); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
module.exports = Menu.buildFromTemplate(template); | 
				
			||||
 | 
				
			||||
@ -1,191 +0,0 @@ | 
				
			||||
const {clipboard, nativeImage, Menu, MenuItem, shell, dialog} = require('electron'); | 
				
			||||
const url = require('url'); | 
				
			||||
const fs = require('fs'); | 
				
			||||
const request = require('request'); | 
				
			||||
 | 
				
			||||
const MAILTO_PREFIX = "mailto:"; | 
				
			||||
 | 
				
			||||
const PERMITTED_URL_SCHEMES = [ | 
				
			||||
    'http:', | 
				
			||||
    'https:', | 
				
			||||
    MAILTO_PREFIX, | 
				
			||||
]; | 
				
			||||
 | 
				
			||||
function safeOpenURL(target) { | 
				
			||||
    // openExternal passes the target to open/start/xdg-open,
 | 
				
			||||
    // so put fairly stringent limits on what can be opened
 | 
				
			||||
    // (for instance, open /bin/sh does indeed open a terminal
 | 
				
			||||
    // with a shell, albeit with no arguments)
 | 
				
			||||
    const parsedUrl = url.parse(target); | 
				
			||||
    if (PERMITTED_URL_SCHEMES.indexOf(parsedUrl.protocol) > -1) { | 
				
			||||
        // explicitly use the URL re-assembled by the url library,
 | 
				
			||||
        // so we know the url parser has understood all the parts
 | 
				
			||||
        // of the input string
 | 
				
			||||
        const newTarget = url.format(parsedUrl); | 
				
			||||
        shell.openExternal(newTarget); | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
function onWindowOrNavigate(ev, target) { | 
				
			||||
    // always prevent the default: if something goes wrong,
 | 
				
			||||
    // we don't want to end up opening it in the electron
 | 
				
			||||
    // app, as we could end up opening any sort of random
 | 
				
			||||
    // url in a window that has node scripting access.
 | 
				
			||||
    ev.preventDefault(); | 
				
			||||
    safeOpenURL(target); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
function onLinkContextMenu(ev, params) { | 
				
			||||
    let url = params.linkURL || params.srcURL; | 
				
			||||
 | 
				
			||||
    if (url.startsWith('vector://vector/webapp')) { | 
				
			||||
        url = "https://riot.im/app/" + url.substring(23); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    const popupMenu = new Menu(); | 
				
			||||
    // No point trying to open blob: URLs in an external browser: it ain't gonna work.
 | 
				
			||||
    if (!url.startsWith('blob:')) { | 
				
			||||
        popupMenu.append(new MenuItem({ | 
				
			||||
            label: url, | 
				
			||||
            click() { | 
				
			||||
                safeOpenURL(url); | 
				
			||||
            }, | 
				
			||||
        })); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    let addSaveAs = false; | 
				
			||||
    if (params.mediaType && params.mediaType === 'image' && !url.startsWith('file://')) { | 
				
			||||
        popupMenu.append(new MenuItem({ | 
				
			||||
            label: '&Copy image', | 
				
			||||
            click() { | 
				
			||||
                if (url.startsWith('data:')) { | 
				
			||||
                    clipboard.writeImage(nativeImage.createFromDataURL(url)); | 
				
			||||
                } else { | 
				
			||||
                    ev.sender.copyImageAt(params.x, params.y); | 
				
			||||
                } | 
				
			||||
            }, | 
				
			||||
        })); | 
				
			||||
 | 
				
			||||
        // We want the link to be ordered below the copy stuff, but don't want to duplicate
 | 
				
			||||
        // the `if` statement, so use a flag.
 | 
				
			||||
        addSaveAs = true; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    // No point offering to copy a blob: URL either
 | 
				
			||||
    if (!url.startsWith('blob:')) { | 
				
			||||
        // Special-case e-mail URLs to strip the `mailto:` like modern browsers do
 | 
				
			||||
        if (url.startsWith(MAILTO_PREFIX)) { | 
				
			||||
            popupMenu.append(new MenuItem({ | 
				
			||||
                label: 'Copy email &address', | 
				
			||||
                click() { | 
				
			||||
                    clipboard.writeText(url.substr(MAILTO_PREFIX.length)); | 
				
			||||
                }, | 
				
			||||
            })); | 
				
			||||
        } else { | 
				
			||||
            popupMenu.append(new MenuItem({ | 
				
			||||
                label: 'Copy link &address', | 
				
			||||
                click() { | 
				
			||||
                    clipboard.writeText(url); | 
				
			||||
                }, | 
				
			||||
            })); | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    if (addSaveAs) { | 
				
			||||
        popupMenu.append(new MenuItem({ | 
				
			||||
            label: 'Sa&ve image as...', | 
				
			||||
            click() { | 
				
			||||
                const targetFileName = params.titleText || "image.png"; | 
				
			||||
                const filePath = dialog.showSaveDialog({ | 
				
			||||
                    defaultPath: targetFileName, | 
				
			||||
                }); | 
				
			||||
 | 
				
			||||
                if (!filePath) return; // user cancelled dialog
 | 
				
			||||
 | 
				
			||||
                try { | 
				
			||||
                    if (url.startsWith("data:")) { | 
				
			||||
                        fs.writeFileSync(filePath, nativeImage.createFromDataURL(url)); | 
				
			||||
                    } else { | 
				
			||||
                        request.get(url).pipe(fs.createWriteStream(filePath)); | 
				
			||||
                    } | 
				
			||||
                } catch (err) { | 
				
			||||
                    console.error(err); | 
				
			||||
                    dialog.showMessageBox({ | 
				
			||||
                        type: "error", | 
				
			||||
                        title: "Failed to save image", | 
				
			||||
                        message: "The image failed to save", | 
				
			||||
                    }); | 
				
			||||
                } | 
				
			||||
            }, | 
				
			||||
        })); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    // popup() requires an options object even for no options
 | 
				
			||||
    popupMenu.popup({}); | 
				
			||||
    ev.preventDefault(); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
function _CutCopyPasteSelectContextMenus(params) { | 
				
			||||
    return [{ | 
				
			||||
        role: 'cut', | 
				
			||||
        label: 'Cu&t', | 
				
			||||
        enabled: params.editFlags.canCut, | 
				
			||||
    }, { | 
				
			||||
        role: 'copy', | 
				
			||||
        label: '&Copy', | 
				
			||||
        enabled: params.editFlags.canCopy, | 
				
			||||
    }, { | 
				
			||||
        role: 'paste', | 
				
			||||
        label: '&Paste', | 
				
			||||
        enabled: params.editFlags.canPaste, | 
				
			||||
    }, { | 
				
			||||
        role: 'pasteandmatchstyle', | 
				
			||||
        enabled: params.editFlags.canPaste, | 
				
			||||
    }, { | 
				
			||||
        role: 'selectall', | 
				
			||||
        label: "Select &All", | 
				
			||||
        enabled: params.editFlags.canSelectAll, | 
				
			||||
    }]; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
function onSelectedContextMenu(ev, params) { | 
				
			||||
    const items = _CutCopyPasteSelectContextMenus(params); | 
				
			||||
    const popupMenu = Menu.buildFromTemplate(items); | 
				
			||||
 | 
				
			||||
    // popup() requires an options object even for no options
 | 
				
			||||
    popupMenu.popup({}); | 
				
			||||
    ev.preventDefault(); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
function onEditableContextMenu(ev, params) { | 
				
			||||
    const items = [ | 
				
			||||
        { role: 'undo' }, | 
				
			||||
        { role: 'redo', enabled: params.editFlags.canRedo }, | 
				
			||||
        { type: 'separator' }, | 
				
			||||
    ].concat(_CutCopyPasteSelectContextMenus(params)); | 
				
			||||
 | 
				
			||||
    const popupMenu = Menu.buildFromTemplate(items); | 
				
			||||
 | 
				
			||||
    // popup() requires an options object even for no options
 | 
				
			||||
    popupMenu.popup({}); | 
				
			||||
    ev.preventDefault(); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
 | 
				
			||||
module.exports = (webContents) => { | 
				
			||||
    webContents.on('new-window', onWindowOrNavigate); | 
				
			||||
    webContents.on('will-navigate', (ev, target) => { | 
				
			||||
        if (target.startsWith("vector://")) return; | 
				
			||||
        return onWindowOrNavigate(ev, target); | 
				
			||||
    }); | 
				
			||||
 | 
				
			||||
    webContents.on('context-menu', function(ev, params) { | 
				
			||||
        if (params.linkURL || params.srcURL) { | 
				
			||||
            onLinkContextMenu(ev, params); | 
				
			||||
        } else if (params.selectionText) { | 
				
			||||
            onSelectedContextMenu(ev, params); | 
				
			||||
        } else if (params.isEditable) { | 
				
			||||
            onEditableContextMenu(ev, params); | 
				
			||||
        } | 
				
			||||
    }); | 
				
			||||
}; | 
				
			||||
@ -1,837 +0,0 @@ | 
				
			||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. | 
				
			||||
# yarn lockfile v1 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
"@types/node@^9.4.0": | 
				
			||||
  version "9.6.45" | 
				
			||||
  resolved "https://registry.yarnpkg.com/@types/node/-/node-9.6.45.tgz#a9e5cfd026a3abaaf17e3c0318a470da9f2f178e" | 
				
			||||
  integrity sha512-9scD7xI1kpIoMs3gVFMOWsWDyRIQ1AOZwe56i1CQPE6N/P4POYkn9UtW5F66t8C2AIoPtVfOFycQ2r11t3pcyg== | 
				
			||||
 | 
				
			||||
ajv@^6.5.5: | 
				
			||||
  version "6.10.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.0.tgz#90d0d54439da587cd7e843bfb7045f50bd22bdf1" | 
				
			||||
  integrity sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg== | 
				
			||||
  dependencies: | 
				
			||||
    fast-deep-equal "^2.0.1" | 
				
			||||
    fast-json-stable-stringify "^2.0.0" | 
				
			||||
    json-schema-traverse "^0.4.1" | 
				
			||||
    uri-js "^4.2.2" | 
				
			||||
 | 
				
			||||
applescript@^1.0.0: | 
				
			||||
  version "1.0.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/applescript/-/applescript-1.0.0.tgz#bb87af568cad034a4e48c4bdaf6067a3a2701317" | 
				
			||||
  integrity sha1-u4evVoytA0pOSMS9r2Bno6JwExc= | 
				
			||||
 | 
				
			||||
asn1@~0.2.3: | 
				
			||||
  version "0.2.4" | 
				
			||||
  resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" | 
				
			||||
  integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== | 
				
			||||
  dependencies: | 
				
			||||
    safer-buffer "~2.1.0" | 
				
			||||
 | 
				
			||||
assert-plus@1.0.0, assert-plus@^1.0.0: | 
				
			||||
  version "1.0.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" | 
				
			||||
  integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= | 
				
			||||
 | 
				
			||||
asynckit@^0.4.0: | 
				
			||||
  version "0.4.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" | 
				
			||||
  integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= | 
				
			||||
 | 
				
			||||
auto-launch@^5.0.1: | 
				
			||||
  version "5.0.5" | 
				
			||||
  resolved "https://registry.yarnpkg.com/auto-launch/-/auto-launch-5.0.5.tgz#d14bd002b1ef642f85e991a6195ff5300c8ad3c0" | 
				
			||||
  integrity sha512-ppdF4mihhYzMYLuCcx9H/c5TUOCev8uM7en53zWVQhyYAJrurd2bFZx3qQVeJKF2jrc7rsPRNN5cD+i23l6PdA== | 
				
			||||
  dependencies: | 
				
			||||
    applescript "^1.0.0" | 
				
			||||
    mkdirp "^0.5.1" | 
				
			||||
    path-is-absolute "^1.0.0" | 
				
			||||
    untildify "^3.0.2" | 
				
			||||
    winreg "1.2.4" | 
				
			||||
 | 
				
			||||
aws-sign2@~0.7.0: | 
				
			||||
  version "0.7.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" | 
				
			||||
  integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= | 
				
			||||
 | 
				
			||||
aws4@^1.8.0: | 
				
			||||
  version "1.8.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" | 
				
			||||
  integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== | 
				
			||||
 | 
				
			||||
bcrypt-pbkdf@^1.0.0: | 
				
			||||
  version "1.0.2" | 
				
			||||
  resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" | 
				
			||||
  integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= | 
				
			||||
  dependencies: | 
				
			||||
    tweetnacl "^0.14.3" | 
				
			||||
 | 
				
			||||
bignumber.js@^2.1.0: | 
				
			||||
  version "2.4.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-2.4.0.tgz#838a992da9f9d737e0f4b2db0be62bb09dd0c5e8" | 
				
			||||
  integrity sha1-g4qZLan51zfg9LLbC+YrsJ3Qxeg= | 
				
			||||
 | 
				
			||||
bmp-js@0.0.3: | 
				
			||||
  version "0.0.3" | 
				
			||||
  resolved "https://registry.yarnpkg.com/bmp-js/-/bmp-js-0.0.3.tgz#64113e9c7cf1202b376ed607bf30626ebe57b18a" | 
				
			||||
  integrity sha1-ZBE+nHzxICs3btYHvzBibr5XsYo= | 
				
			||||
 | 
				
			||||
buffer-equal@0.0.1: | 
				
			||||
  version "0.0.1" | 
				
			||||
  resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-0.0.1.tgz#91bc74b11ea405bc916bc6aa908faafa5b4aac4b" | 
				
			||||
  integrity sha1-kbx0sR6kBbyRa8aqkI+q+ltKrEs= | 
				
			||||
 | 
				
			||||
caseless@~0.12.0: | 
				
			||||
  version "0.12.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" | 
				
			||||
  integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= | 
				
			||||
 | 
				
			||||
combined-stream@^1.0.6, combined-stream@~1.0.6: | 
				
			||||
  version "1.0.7" | 
				
			||||
  resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.7.tgz#2d1d24317afb8abe95d6d2c0b07b57813539d828" | 
				
			||||
  integrity sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w== | 
				
			||||
  dependencies: | 
				
			||||
    delayed-stream "~1.0.0" | 
				
			||||
 | 
				
			||||
conf@^2.0.0: | 
				
			||||
  version "2.2.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/conf/-/conf-2.2.0.tgz#ee282efafc1450b61e205372041ad7d866802d9a" | 
				
			||||
  integrity sha512-93Kz74FOMo6aWRVpAZsonOdl2I57jKtHrNmxhumehFQw4X8Sk37SohNY11PG7Q8Okta+UnrVaI006WLeyp8/XA== | 
				
			||||
  dependencies: | 
				
			||||
    dot-prop "^4.1.0" | 
				
			||||
    env-paths "^1.0.0" | 
				
			||||
    make-dir "^1.0.0" | 
				
			||||
    pkg-up "^2.0.0" | 
				
			||||
    write-file-atomic "^2.3.0" | 
				
			||||
 | 
				
			||||
core-util-is@1.0.2: | 
				
			||||
  version "1.0.2" | 
				
			||||
  resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" | 
				
			||||
  integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= | 
				
			||||
 | 
				
			||||
dashdash@^1.12.0: | 
				
			||||
  version "1.14.1" | 
				
			||||
  resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" | 
				
			||||
  integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= | 
				
			||||
  dependencies: | 
				
			||||
    assert-plus "^1.0.0" | 
				
			||||
 | 
				
			||||
deep-equal@^1.0.1: | 
				
			||||
  version "1.0.1" | 
				
			||||
  resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" | 
				
			||||
  integrity sha1-9dJgKStmDghO/0zbyfCK0yR0SLU= | 
				
			||||
 | 
				
			||||
define-properties@^1.1.2: | 
				
			||||
  version "1.1.3" | 
				
			||||
  resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" | 
				
			||||
  integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== | 
				
			||||
  dependencies: | 
				
			||||
    object-keys "^1.0.12" | 
				
			||||
 | 
				
			||||
delayed-stream@~1.0.0: | 
				
			||||
  version "1.0.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" | 
				
			||||
  integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= | 
				
			||||
 | 
				
			||||
dom-walk@^0.1.0: | 
				
			||||
  version "0.1.1" | 
				
			||||
  resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018" | 
				
			||||
  integrity sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg= | 
				
			||||
 | 
				
			||||
dot-prop@^4.1.0: | 
				
			||||
  version "4.2.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57" | 
				
			||||
  integrity sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ== | 
				
			||||
  dependencies: | 
				
			||||
    is-obj "^1.0.0" | 
				
			||||
 | 
				
			||||
ecc-jsbn@~0.1.1: | 
				
			||||
  version "0.1.2" | 
				
			||||
  resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" | 
				
			||||
  integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= | 
				
			||||
  dependencies: | 
				
			||||
    jsbn "~0.1.0" | 
				
			||||
    safer-buffer "^2.1.0" | 
				
			||||
 | 
				
			||||
electron-store@^2.0.0: | 
				
			||||
  version "2.0.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/electron-store/-/electron-store-2.0.0.tgz#1035cca2a95409d1f54c7466606345852450d64a" | 
				
			||||
  integrity sha512-1WCFYHsYvZBqDsoaS0Relnz0rd81ZkBAI0Fgx7Nq2UWU77rSNs1qxm4S6uH7TCZ0bV3LQpJFk7id/is/ZgoOPA== | 
				
			||||
  dependencies: | 
				
			||||
    conf "^2.0.0" | 
				
			||||
 | 
				
			||||
electron-window-state@^4.1.0: | 
				
			||||
  version "4.1.1" | 
				
			||||
  resolved "https://registry.yarnpkg.com/electron-window-state/-/electron-window-state-4.1.1.tgz#6b34fdc31b38514dfec8b7c8f7b5d4addb67632d" | 
				
			||||
  integrity sha1-azT9wxs4UU3+yLfI97XUrdtnYy0= | 
				
			||||
  dependencies: | 
				
			||||
    deep-equal "^1.0.1" | 
				
			||||
    jsonfile "^2.2.3" | 
				
			||||
    mkdirp "^0.5.1" | 
				
			||||
 | 
				
			||||
env-paths@^1.0.0: | 
				
			||||
  version "1.0.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-1.0.0.tgz#4168133b42bb05c38a35b1ae4397c8298ab369e0" | 
				
			||||
  integrity sha1-QWgTO0K7BcOKNbGuQ5fIKYqzaeA= | 
				
			||||
 | 
				
			||||
es-abstract@^1.5.0: | 
				
			||||
  version "1.13.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.13.0.tgz#ac86145fdd5099d8dd49558ccba2eaf9b88e24e9" | 
				
			||||
  integrity sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg== | 
				
			||||
  dependencies: | 
				
			||||
    es-to-primitive "^1.2.0" | 
				
			||||
    function-bind "^1.1.1" | 
				
			||||
    has "^1.0.3" | 
				
			||||
    is-callable "^1.1.4" | 
				
			||||
    is-regex "^1.0.4" | 
				
			||||
    object-keys "^1.0.12" | 
				
			||||
 | 
				
			||||
es-to-primitive@^1.2.0: | 
				
			||||
  version "1.2.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377" | 
				
			||||
  integrity sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg== | 
				
			||||
  dependencies: | 
				
			||||
    is-callable "^1.1.4" | 
				
			||||
    is-date-object "^1.0.1" | 
				
			||||
    is-symbol "^1.0.2" | 
				
			||||
 | 
				
			||||
es6-promise@^3.0.2: | 
				
			||||
  version "3.3.1" | 
				
			||||
  resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.3.1.tgz#a08cdde84ccdbf34d027a1451bc91d4bcd28a613" | 
				
			||||
  integrity sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM= | 
				
			||||
 | 
				
			||||
exif-parser@^0.1.9: | 
				
			||||
  version "0.1.12" | 
				
			||||
  resolved "https://registry.yarnpkg.com/exif-parser/-/exif-parser-0.1.12.tgz#58a9d2d72c02c1f6f02a0ef4a9166272b7760922" | 
				
			||||
  integrity sha1-WKnS1ywCwfbwKg70qRZicrd2CSI= | 
				
			||||
 | 
				
			||||
extend@~3.0.2: | 
				
			||||
  version "3.0.2" | 
				
			||||
  resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" | 
				
			||||
  integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== | 
				
			||||
 | 
				
			||||
extsprintf@1.3.0: | 
				
			||||
  version "1.3.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" | 
				
			||||
  integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= | 
				
			||||
 | 
				
			||||
extsprintf@^1.2.0: | 
				
			||||
  version "1.4.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" | 
				
			||||
  integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= | 
				
			||||
 | 
				
			||||
fast-deep-equal@^2.0.1: | 
				
			||||
  version "2.0.1" | 
				
			||||
  resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" | 
				
			||||
  integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= | 
				
			||||
 | 
				
			||||
fast-json-stable-stringify@^2.0.0: | 
				
			||||
  version "2.0.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" | 
				
			||||
  integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= | 
				
			||||
 | 
				
			||||
file-type@^3.1.0: | 
				
			||||
  version "3.9.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9" | 
				
			||||
  integrity sha1-JXoHg4TR24CHvESdEH1SpSZyuek= | 
				
			||||
 | 
				
			||||
find-up@^2.1.0: | 
				
			||||
  version "2.1.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" | 
				
			||||
  integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= | 
				
			||||
  dependencies: | 
				
			||||
    locate-path "^2.0.0" | 
				
			||||
 | 
				
			||||
for-each@^0.3.3: | 
				
			||||
  version "0.3.3" | 
				
			||||
  resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" | 
				
			||||
  integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== | 
				
			||||
  dependencies: | 
				
			||||
    is-callable "^1.1.3" | 
				
			||||
 | 
				
			||||
forever-agent@~0.6.1: | 
				
			||||
  version "0.6.1" | 
				
			||||
  resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" | 
				
			||||
  integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= | 
				
			||||
 | 
				
			||||
form-data@~2.3.2: | 
				
			||||
  version "2.3.3" | 
				
			||||
  resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" | 
				
			||||
  integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== | 
				
			||||
  dependencies: | 
				
			||||
    asynckit "^0.4.0" | 
				
			||||
    combined-stream "^1.0.6" | 
				
			||||
    mime-types "^2.1.12" | 
				
			||||
 | 
				
			||||
function-bind@^1.0.2, function-bind@^1.1.1: | 
				
			||||
  version "1.1.1" | 
				
			||||
  resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" | 
				
			||||
  integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== | 
				
			||||
 | 
				
			||||
getpass@^0.1.1: | 
				
			||||
  version "0.1.7" | 
				
			||||
  resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" | 
				
			||||
  integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= | 
				
			||||
  dependencies: | 
				
			||||
    assert-plus "^1.0.0" | 
				
			||||
 | 
				
			||||
global@~4.3.0: | 
				
			||||
  version "4.3.2" | 
				
			||||
  resolved "https://registry.yarnpkg.com/global/-/global-4.3.2.tgz#e76989268a6c74c38908b1305b10fc0e394e9d0f" | 
				
			||||
  integrity sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8= | 
				
			||||
  dependencies: | 
				
			||||
    min-document "^2.19.0" | 
				
			||||
    process "~0.5.1" | 
				
			||||
 | 
				
			||||
graceful-fs@^4.1.11, graceful-fs@^4.1.6: | 
				
			||||
  version "4.1.15" | 
				
			||||
  resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00" | 
				
			||||
  integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA== | 
				
			||||
 | 
				
			||||
har-schema@^2.0.0: | 
				
			||||
  version "2.0.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" | 
				
			||||
  integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= | 
				
			||||
 | 
				
			||||
har-validator@~5.1.0: | 
				
			||||
  version "5.1.3" | 
				
			||||
  resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" | 
				
			||||
  integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== | 
				
			||||
  dependencies: | 
				
			||||
    ajv "^6.5.5" | 
				
			||||
    har-schema "^2.0.0" | 
				
			||||
 | 
				
			||||
has-symbols@^1.0.0: | 
				
			||||
  version "1.0.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44" | 
				
			||||
  integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q= | 
				
			||||
 | 
				
			||||
has@^1.0.1, has@^1.0.3: | 
				
			||||
  version "1.0.3" | 
				
			||||
  resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" | 
				
			||||
  integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== | 
				
			||||
  dependencies: | 
				
			||||
    function-bind "^1.1.1" | 
				
			||||
 | 
				
			||||
http-signature@~1.2.0: | 
				
			||||
  version "1.2.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" | 
				
			||||
  integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= | 
				
			||||
  dependencies: | 
				
			||||
    assert-plus "^1.0.0" | 
				
			||||
    jsprim "^1.2.2" | 
				
			||||
    sshpk "^1.7.0" | 
				
			||||
 | 
				
			||||
imurmurhash@^0.1.4: | 
				
			||||
  version "0.1.4" | 
				
			||||
  resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" | 
				
			||||
  integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= | 
				
			||||
 | 
				
			||||
ip-regex@^1.0.1: | 
				
			||||
  version "1.0.3" | 
				
			||||
  resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-1.0.3.tgz#dc589076f659f419c222039a33316f1c7387effd" | 
				
			||||
  integrity sha1-3FiQdvZZ9BnCIgOaMzFvHHOH7/0= | 
				
			||||
 | 
				
			||||
is-callable@^1.1.3, is-callable@^1.1.4: | 
				
			||||
  version "1.1.4" | 
				
			||||
  resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" | 
				
			||||
  integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA== | 
				
			||||
 | 
				
			||||
is-date-object@^1.0.1: | 
				
			||||
  version "1.0.1" | 
				
			||||
  resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" | 
				
			||||
  integrity sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY= | 
				
			||||
 | 
				
			||||
is-function@^1.0.1: | 
				
			||||
  version "1.0.1" | 
				
			||||
  resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.1.tgz#12cfb98b65b57dd3d193a3121f5f6e2f437602b5" | 
				
			||||
  integrity sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU= | 
				
			||||
 | 
				
			||||
is-obj@^1.0.0: | 
				
			||||
  version "1.0.1" | 
				
			||||
  resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" | 
				
			||||
  integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= | 
				
			||||
 | 
				
			||||
is-regex@^1.0.4: | 
				
			||||
  version "1.0.4" | 
				
			||||
  resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" | 
				
			||||
  integrity sha1-VRdIm1RwkbCTDglWVM7SXul+lJE= | 
				
			||||
  dependencies: | 
				
			||||
    has "^1.0.1" | 
				
			||||
 | 
				
			||||
is-symbol@^1.0.2: | 
				
			||||
  version "1.0.2" | 
				
			||||
  resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38" | 
				
			||||
  integrity sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw== | 
				
			||||
  dependencies: | 
				
			||||
    has-symbols "^1.0.0" | 
				
			||||
 | 
				
			||||
is-typedarray@~1.0.0: | 
				
			||||
  version "1.0.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" | 
				
			||||
  integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= | 
				
			||||
 | 
				
			||||
isstream@~0.1.2: | 
				
			||||
  version "0.1.2" | 
				
			||||
  resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" | 
				
			||||
  integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= | 
				
			||||
 | 
				
			||||
jimp@^0.2.28: | 
				
			||||
  version "0.2.28" | 
				
			||||
  resolved "https://registry.yarnpkg.com/jimp/-/jimp-0.2.28.tgz#dd529a937190f42957a7937d1acc3a7762996ea2" | 
				
			||||
  integrity sha1-3VKak3GQ9ClXp5N9Gsw6d2KZbqI= | 
				
			||||
  dependencies: | 
				
			||||
    bignumber.js "^2.1.0" | 
				
			||||
    bmp-js "0.0.3" | 
				
			||||
    es6-promise "^3.0.2" | 
				
			||||
    exif-parser "^0.1.9" | 
				
			||||
    file-type "^3.1.0" | 
				
			||||
    jpeg-js "^0.2.0" | 
				
			||||
    load-bmfont "^1.2.3" | 
				
			||||
    mime "^1.3.4" | 
				
			||||
    mkdirp "0.5.1" | 
				
			||||
    pixelmatch "^4.0.0" | 
				
			||||
    pngjs "^3.0.0" | 
				
			||||
    read-chunk "^1.0.1" | 
				
			||||
    request "^2.65.0" | 
				
			||||
    stream-to-buffer "^0.1.0" | 
				
			||||
    tinycolor2 "^1.1.2" | 
				
			||||
    url-regex "^3.0.0" | 
				
			||||
 | 
				
			||||
jpeg-js@^0.2.0: | 
				
			||||
  version "0.2.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.2.0.tgz#53e448ec9d263e683266467e9442d2c5a2ef5482" | 
				
			||||
  integrity sha1-U+RI7J0mPmgyZkZ+lELSxaLvVII= | 
				
			||||
 | 
				
			||||
jsbn@~0.1.0: | 
				
			||||
  version "0.1.1" | 
				
			||||
  resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" | 
				
			||||
  integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= | 
				
			||||
 | 
				
			||||
json-schema-traverse@^0.4.1: | 
				
			||||
  version "0.4.1" | 
				
			||||
  resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" | 
				
			||||
  integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== | 
				
			||||
 | 
				
			||||
json-schema@0.2.3: | 
				
			||||
  version "0.2.3" | 
				
			||||
  resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" | 
				
			||||
  integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= | 
				
			||||
 | 
				
			||||
json-stringify-safe@~5.0.1: | 
				
			||||
  version "5.0.1" | 
				
			||||
  resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" | 
				
			||||
  integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= | 
				
			||||
 | 
				
			||||
jsonfile@^2.2.3: | 
				
			||||
  version "2.4.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" | 
				
			||||
  integrity sha1-NzaitCi4e72gzIO1P6PWM6NcKug= | 
				
			||||
  optionalDependencies: | 
				
			||||
    graceful-fs "^4.1.6" | 
				
			||||
 | 
				
			||||
jsprim@^1.2.2: | 
				
			||||
  version "1.4.1" | 
				
			||||
  resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" | 
				
			||||
  integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= | 
				
			||||
  dependencies: | 
				
			||||
    assert-plus "1.0.0" | 
				
			||||
    extsprintf "1.3.0" | 
				
			||||
    json-schema "0.2.3" | 
				
			||||
    verror "1.10.0" | 
				
			||||
 | 
				
			||||
load-bmfont@^1.2.3: | 
				
			||||
  version "1.4.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/load-bmfont/-/load-bmfont-1.4.0.tgz#75f17070b14a8c785fe7f5bee2e6fd4f98093b6b" | 
				
			||||
  integrity sha512-kT63aTAlNhZARowaNYcY29Fn/QYkc52M3l6V1ifRcPewg2lvUZDAj7R6dXjOL9D0sict76op3T5+odumDSF81g== | 
				
			||||
  dependencies: | 
				
			||||
    buffer-equal "0.0.1" | 
				
			||||
    mime "^1.3.4" | 
				
			||||
    parse-bmfont-ascii "^1.0.3" | 
				
			||||
    parse-bmfont-binary "^1.0.5" | 
				
			||||
    parse-bmfont-xml "^1.1.4" | 
				
			||||
    phin "^2.9.1" | 
				
			||||
    xhr "^2.0.1" | 
				
			||||
    xtend "^4.0.0" | 
				
			||||
 | 
				
			||||
locate-path@^2.0.0: | 
				
			||||
  version "2.0.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" | 
				
			||||
  integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= | 
				
			||||
  dependencies: | 
				
			||||
    p-locate "^2.0.0" | 
				
			||||
    path-exists "^3.0.0" | 
				
			||||
 | 
				
			||||
make-dir@^1.0.0: | 
				
			||||
  version "1.3.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" | 
				
			||||
  integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ== | 
				
			||||
  dependencies: | 
				
			||||
    pify "^3.0.0" | 
				
			||||
 | 
				
			||||
mime-db@~1.38.0: | 
				
			||||
  version "1.38.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.38.0.tgz#1a2aab16da9eb167b49c6e4df2d9c68d63d8e2ad" | 
				
			||||
  integrity sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg== | 
				
			||||
 | 
				
			||||
mime-types@^2.1.12, mime-types@~2.1.19: | 
				
			||||
  version "2.1.22" | 
				
			||||
  resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.22.tgz#fe6b355a190926ab7698c9a0556a11199b2199bd" | 
				
			||||
  integrity sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog== | 
				
			||||
  dependencies: | 
				
			||||
    mime-db "~1.38.0" | 
				
			||||
 | 
				
			||||
mime@^1.3.4: | 
				
			||||
  version "1.6.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" | 
				
			||||
  integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== | 
				
			||||
 | 
				
			||||
min-document@^2.19.0: | 
				
			||||
  version "2.19.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" | 
				
			||||
  integrity sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU= | 
				
			||||
  dependencies: | 
				
			||||
    dom-walk "^0.1.0" | 
				
			||||
 | 
				
			||||
minimist@0.0.8: | 
				
			||||
  version "0.0.8" | 
				
			||||
  resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" | 
				
			||||
  integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= | 
				
			||||
 | 
				
			||||
minimist@^1.2.0: | 
				
			||||
  version "1.2.3" | 
				
			||||
  resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.3.tgz#3db5c0765545ab8637be71f333a104a965a9ca3f" | 
				
			||||
  integrity sha512-+bMdgqjMN/Z77a6NlY/I3U5LlRDbnmaAk6lDveAPKwSpcPM4tKAuYsvYF8xjhOPXhOYGe/73vVLVez5PW+jqhw== | 
				
			||||
 | 
				
			||||
mkdirp@0.5.1, mkdirp@^0.5.1: | 
				
			||||
  version "0.5.1" | 
				
			||||
  resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" | 
				
			||||
  integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= | 
				
			||||
  dependencies: | 
				
			||||
    minimist "0.0.8" | 
				
			||||
 | 
				
			||||
oauth-sign@~0.9.0: | 
				
			||||
  version "0.9.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" | 
				
			||||
  integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== | 
				
			||||
 | 
				
			||||
object-keys@^1.0.12: | 
				
			||||
  version "1.1.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.0.tgz#11bd22348dd2e096a045ab06f6c85bcc340fa032" | 
				
			||||
  integrity sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg== | 
				
			||||
 | 
				
			||||
p-limit@^1.1.0: | 
				
			||||
  version "1.3.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" | 
				
			||||
  integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== | 
				
			||||
  dependencies: | 
				
			||||
    p-try "^1.0.0" | 
				
			||||
 | 
				
			||||
p-locate@^2.0.0: | 
				
			||||
  version "2.0.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" | 
				
			||||
  integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= | 
				
			||||
  dependencies: | 
				
			||||
    p-limit "^1.1.0" | 
				
			||||
 | 
				
			||||
p-try@^1.0.0: | 
				
			||||
  version "1.0.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" | 
				
			||||
  integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= | 
				
			||||
 | 
				
			||||
parse-bmfont-ascii@^1.0.3: | 
				
			||||
  version "1.0.6" | 
				
			||||
  resolved "https://registry.yarnpkg.com/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz#11ac3c3ff58f7c2020ab22769079108d4dfa0285" | 
				
			||||
  integrity sha1-Eaw8P/WPfCAgqyJ2kHkQjU36AoU= | 
				
			||||
 | 
				
			||||
parse-bmfont-binary@^1.0.5: | 
				
			||||
  version "1.0.6" | 
				
			||||
  resolved "https://registry.yarnpkg.com/parse-bmfont-binary/-/parse-bmfont-binary-1.0.6.tgz#d038b476d3e9dd9db1e11a0b0e53a22792b69006" | 
				
			||||
  integrity sha1-0Di0dtPp3Z2x4RoLDlOiJ5K2kAY= | 
				
			||||
 | 
				
			||||
parse-bmfont-xml@^1.1.4: | 
				
			||||
  version "1.1.4" | 
				
			||||
  resolved "https://registry.yarnpkg.com/parse-bmfont-xml/-/parse-bmfont-xml-1.1.4.tgz#015319797e3e12f9e739c4d513872cd2fa35f389" | 
				
			||||
  integrity sha512-bjnliEOmGv3y1aMEfREMBJ9tfL3WR0i0CKPj61DnSLaoxWR3nLrsQrEbCId/8rF4NyRF0cCqisSVXyQYWM+mCQ== | 
				
			||||
  dependencies: | 
				
			||||
    xml-parse-from-string "^1.0.0" | 
				
			||||
    xml2js "^0.4.5" | 
				
			||||
 | 
				
			||||
parse-headers@^2.0.0: | 
				
			||||
  version "2.0.2" | 
				
			||||
  resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.2.tgz#9545e8a4c1ae5eaea7d24992bca890281ed26e34" | 
				
			||||
  integrity sha512-/LypJhzFmyBIDYP9aDVgeyEb5sQfbfY5mnDq4hVhlQ69js87wXfmEI5V3xI6vvXasqebp0oCytYFLxsBVfCzSg== | 
				
			||||
  dependencies: | 
				
			||||
    for-each "^0.3.3" | 
				
			||||
    string.prototype.trim "^1.1.2" | 
				
			||||
 | 
				
			||||
path-exists@^3.0.0: | 
				
			||||
  version "3.0.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" | 
				
			||||
  integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= | 
				
			||||
 | 
				
			||||
path-is-absolute@^1.0.0: | 
				
			||||
  version "1.0.1" | 
				
			||||
  resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" | 
				
			||||
  integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= | 
				
			||||
 | 
				
			||||
performance-now@^2.1.0: | 
				
			||||
  version "2.1.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" | 
				
			||||
  integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= | 
				
			||||
 | 
				
			||||
phin@^2.9.1: | 
				
			||||
  version "2.9.3" | 
				
			||||
  resolved "https://registry.yarnpkg.com/phin/-/phin-2.9.3.tgz#f9b6ac10a035636fb65dfc576aaaa17b8743125c" | 
				
			||||
  integrity sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA== | 
				
			||||
 | 
				
			||||
pify@^3.0.0: | 
				
			||||
  version "3.0.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" | 
				
			||||
  integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= | 
				
			||||
 | 
				
			||||
pixelmatch@^4.0.0: | 
				
			||||
  version "4.0.2" | 
				
			||||
  resolved "https://registry.yarnpkg.com/pixelmatch/-/pixelmatch-4.0.2.tgz#8f47dcec5011b477b67db03c243bc1f3085e8854" | 
				
			||||
  integrity sha1-j0fc7FARtHe2fbA8JDvB8wheiFQ= | 
				
			||||
  dependencies: | 
				
			||||
    pngjs "^3.0.0" | 
				
			||||
 | 
				
			||||
pkg-up@^2.0.0: | 
				
			||||
  version "2.0.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f" | 
				
			||||
  integrity sha1-yBmscoBZpGHKscOImivjxJoATX8= | 
				
			||||
  dependencies: | 
				
			||||
    find-up "^2.1.0" | 
				
			||||
 | 
				
			||||
png-to-ico@^1.0.2: | 
				
			||||
  version "1.0.7" | 
				
			||||
  resolved "https://registry.yarnpkg.com/png-to-ico/-/png-to-ico-1.0.7.tgz#9346b5f4d6fd7e94cb08fd49eeb585f501c3e5f2" | 
				
			||||
  integrity sha512-heHiZjPFhVgLiuSG4C4wwKN9YPGLpPJvOfXRyI+cEJf0vPutjJ4XDaeI2f/hzTFs+2juihDw3pP8R5JtTuQTGg== | 
				
			||||
  dependencies: | 
				
			||||
    "@types/node" "^9.4.0" | 
				
			||||
    jimp "^0.2.28" | 
				
			||||
    minimist "^1.2.0" | 
				
			||||
 | 
				
			||||
pngjs@^3.0.0: | 
				
			||||
  version "3.4.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f" | 
				
			||||
  integrity sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w== | 
				
			||||
 | 
				
			||||
process@~0.5.1: | 
				
			||||
  version "0.5.2" | 
				
			||||
  resolved "https://registry.yarnpkg.com/process/-/process-0.5.2.tgz#1638d8a8e34c2f440a91db95ab9aeb677fc185cf" | 
				
			||||
  integrity sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8= | 
				
			||||
 | 
				
			||||
psl@^1.1.24: | 
				
			||||
  version "1.1.31" | 
				
			||||
  resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.31.tgz#e9aa86d0101b5b105cbe93ac6b784cd547276184" | 
				
			||||
  integrity sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw== | 
				
			||||
 | 
				
			||||
punycode@^1.4.1: | 
				
			||||
  version "1.4.1" | 
				
			||||
  resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" | 
				
			||||
  integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= | 
				
			||||
 | 
				
			||||
punycode@^2.1.0: | 
				
			||||
  version "2.1.1" | 
				
			||||
  resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" | 
				
			||||
  integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== | 
				
			||||
 | 
				
			||||
qs@~6.5.2: | 
				
			||||
  version "6.5.2" | 
				
			||||
  resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" | 
				
			||||
  integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== | 
				
			||||
 | 
				
			||||
read-chunk@^1.0.1: | 
				
			||||
  version "1.0.1" | 
				
			||||
  resolved "https://registry.yarnpkg.com/read-chunk/-/read-chunk-1.0.1.tgz#5f68cab307e663f19993527d9b589cace4661194" | 
				
			||||
  integrity sha1-X2jKswfmY/GZk1J9m1icrORmEZQ= | 
				
			||||
 | 
				
			||||
request@^2.65.0: | 
				
			||||
  version "2.88.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" | 
				
			||||
  integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== | 
				
			||||
  dependencies: | 
				
			||||
    aws-sign2 "~0.7.0" | 
				
			||||
    aws4 "^1.8.0" | 
				
			||||
    caseless "~0.12.0" | 
				
			||||
    combined-stream "~1.0.6" | 
				
			||||
    extend "~3.0.2" | 
				
			||||
    forever-agent "~0.6.1" | 
				
			||||
    form-data "~2.3.2" | 
				
			||||
    har-validator "~5.1.0" | 
				
			||||
    http-signature "~1.2.0" | 
				
			||||
    is-typedarray "~1.0.0" | 
				
			||||
    isstream "~0.1.2" | 
				
			||||
    json-stringify-safe "~5.0.1" | 
				
			||||
    mime-types "~2.1.19" | 
				
			||||
    oauth-sign "~0.9.0" | 
				
			||||
    performance-now "^2.1.0" | 
				
			||||
    qs "~6.5.2" | 
				
			||||
    safe-buffer "^5.1.2" | 
				
			||||
    tough-cookie "~2.4.3" | 
				
			||||
    tunnel-agent "^0.6.0" | 
				
			||||
    uuid "^3.3.2" | 
				
			||||
 | 
				
			||||
safe-buffer@^5.0.1, safe-buffer@^5.1.2: | 
				
			||||
  version "5.1.2" | 
				
			||||
  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" | 
				
			||||
  integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== | 
				
			||||
 | 
				
			||||
safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: | 
				
			||||
  version "2.1.2" | 
				
			||||
  resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" | 
				
			||||
  integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== | 
				
			||||
 | 
				
			||||
sax@>=0.6.0: | 
				
			||||
  version "1.2.4" | 
				
			||||
  resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" | 
				
			||||
  integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== | 
				
			||||
 | 
				
			||||
signal-exit@^3.0.2: | 
				
			||||
  version "3.0.2" | 
				
			||||
  resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" | 
				
			||||
  integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= | 
				
			||||
 | 
				
			||||
sshpk@^1.7.0: | 
				
			||||
  version "1.16.1" | 
				
			||||
  resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" | 
				
			||||
  integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== | 
				
			||||
  dependencies: | 
				
			||||
    asn1 "~0.2.3" | 
				
			||||
    assert-plus "^1.0.0" | 
				
			||||
    bcrypt-pbkdf "^1.0.0" | 
				
			||||
    dashdash "^1.12.0" | 
				
			||||
    ecc-jsbn "~0.1.1" | 
				
			||||
    getpass "^0.1.1" | 
				
			||||
    jsbn "~0.1.0" | 
				
			||||
    safer-buffer "^2.0.2" | 
				
			||||
    tweetnacl "~0.14.0" | 
				
			||||
 | 
				
			||||
stream-to-buffer@^0.1.0: | 
				
			||||
  version "0.1.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/stream-to-buffer/-/stream-to-buffer-0.1.0.tgz#26799d903ab2025c9bd550ac47171b00f8dd80a9" | 
				
			||||
  integrity sha1-JnmdkDqyAlyb1VCsRxcbAPjdgKk= | 
				
			||||
  dependencies: | 
				
			||||
    stream-to "~0.2.0" | 
				
			||||
 | 
				
			||||
stream-to@~0.2.0: | 
				
			||||
  version "0.2.2" | 
				
			||||
  resolved "https://registry.yarnpkg.com/stream-to/-/stream-to-0.2.2.tgz#84306098d85fdb990b9fa300b1b3ccf55e8ef01d" | 
				
			||||
  integrity sha1-hDBgmNhf25kLn6MAsbPM9V6O8B0= | 
				
			||||
 | 
				
			||||
string.prototype.trim@^1.1.2: | 
				
			||||
  version "1.1.2" | 
				
			||||
  resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz#d04de2c89e137f4d7d206f086b5ed2fae6be8cea" | 
				
			||||
  integrity sha1-0E3iyJ4Tf019IG8Ia17S+ua+jOo= | 
				
			||||
  dependencies: | 
				
			||||
    define-properties "^1.1.2" | 
				
			||||
    es-abstract "^1.5.0" | 
				
			||||
    function-bind "^1.0.2" | 
				
			||||
 | 
				
			||||
tinycolor2@^1.1.2: | 
				
			||||
  version "1.4.1" | 
				
			||||
  resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.1.tgz#f4fad333447bc0b07d4dc8e9209d8f39a8ac77e8" | 
				
			||||
  integrity sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g= | 
				
			||||
 | 
				
			||||
tough-cookie@~2.4.3: | 
				
			||||
  version "2.4.3" | 
				
			||||
  resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" | 
				
			||||
  integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ== | 
				
			||||
  dependencies: | 
				
			||||
    psl "^1.1.24" | 
				
			||||
    punycode "^1.4.1" | 
				
			||||
 | 
				
			||||
tunnel-agent@^0.6.0: | 
				
			||||
  version "0.6.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" | 
				
			||||
  integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= | 
				
			||||
  dependencies: | 
				
			||||
    safe-buffer "^5.0.1" | 
				
			||||
 | 
				
			||||
tweetnacl@^0.14.3, tweetnacl@~0.14.0: | 
				
			||||
  version "0.14.5" | 
				
			||||
  resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" | 
				
			||||
  integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= | 
				
			||||
 | 
				
			||||
untildify@^3.0.2: | 
				
			||||
  version "3.0.3" | 
				
			||||
  resolved "https://registry.yarnpkg.com/untildify/-/untildify-3.0.3.tgz#1e7b42b140bcfd922b22e70ca1265bfe3634c7c9" | 
				
			||||
  integrity sha512-iSk/J8efr8uPT/Z4eSUywnqyrQU7DSdMfdqK4iWEaUVVmcP5JcnpRqmVMwcwcnmI1ATFNgC5V90u09tBynNFKA== | 
				
			||||
 | 
				
			||||
uri-js@^4.2.2: | 
				
			||||
  version "4.2.2" | 
				
			||||
  resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" | 
				
			||||
  integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== | 
				
			||||
  dependencies: | 
				
			||||
    punycode "^2.1.0" | 
				
			||||
 | 
				
			||||
url-regex@^3.0.0: | 
				
			||||
  version "3.2.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/url-regex/-/url-regex-3.2.0.tgz#dbad1e0c9e29e105dd0b1f09f6862f7fdb482724" | 
				
			||||
  integrity sha1-260eDJ4p4QXdCx8J9oYvf9tIJyQ= | 
				
			||||
  dependencies: | 
				
			||||
    ip-regex "^1.0.1" | 
				
			||||
 | 
				
			||||
uuid@^3.3.2: | 
				
			||||
  version "3.3.2" | 
				
			||||
  resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" | 
				
			||||
  integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== | 
				
			||||
 | 
				
			||||
verror@1.10.0: | 
				
			||||
  version "1.10.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" | 
				
			||||
  integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= | 
				
			||||
  dependencies: | 
				
			||||
    assert-plus "^1.0.0" | 
				
			||||
    core-util-is "1.0.2" | 
				
			||||
    extsprintf "^1.2.0" | 
				
			||||
 | 
				
			||||
winreg@1.2.4: | 
				
			||||
  version "1.2.4" | 
				
			||||
  resolved "https://registry.yarnpkg.com/winreg/-/winreg-1.2.4.tgz#ba065629b7a925130e15779108cf540990e98d1b" | 
				
			||||
  integrity sha1-ugZWKbepJRMOFXeRCM9UCZDpjRs= | 
				
			||||
 | 
				
			||||
write-file-atomic@^2.3.0: | 
				
			||||
  version "2.4.2" | 
				
			||||
  resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.2.tgz#a7181706dfba17855d221140a9c06e15fcdd87b9" | 
				
			||||
  integrity sha512-s0b6vB3xIVRLWywa6X9TOMA7k9zio0TMOsl9ZnDkliA/cfJlpHXAscj0gbHVJiTdIuAYpIyqS5GW91fqm6gG5g== | 
				
			||||
  dependencies: | 
				
			||||
    graceful-fs "^4.1.11" | 
				
			||||
    imurmurhash "^0.1.4" | 
				
			||||
    signal-exit "^3.0.2" | 
				
			||||
 | 
				
			||||
xhr@^2.0.1: | 
				
			||||
  version "2.5.0" | 
				
			||||
  resolved "https://registry.yarnpkg.com/xhr/-/xhr-2.5.0.tgz#bed8d1676d5ca36108667692b74b316c496e49dd" | 
				
			||||
  integrity sha512-4nlO/14t3BNUZRXIXfXe+3N6w3s1KoxcJUUURctd64BLRe67E4gRwp4PjywtDY72fXpZ1y6Ch0VZQRY/gMPzzQ== | 
				
			||||
  dependencies: | 
				
			||||
    global "~4.3.0" | 
				
			||||
    is-function "^1.0.1" | 
				
			||||
    parse-headers "^2.0.0" | 
				
			||||
    xtend "^4.0.0" | 
				
			||||
 | 
				
			||||
xml-parse-from-string@^1.0.0: | 
				
			||||
  version "1.0.1" | 
				
			||||
  resolved "https://registry.yarnpkg.com/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz#a9029e929d3dbcded169f3c6e28238d95a5d5a28" | 
				
			||||
  integrity sha1-qQKekp09vN7RafPG4oI42VpdWig= | 
				
			||||
 | 
				
			||||
xml2js@^0.4.5: | 
				
			||||
  version "0.4.19" | 
				
			||||
  resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7" | 
				
			||||
  integrity sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q== | 
				
			||||
  dependencies: | 
				
			||||
    sax ">=0.6.0" | 
				
			||||
    xmlbuilder "~9.0.1" | 
				
			||||
 | 
				
			||||
xmlbuilder@~9.0.1: | 
				
			||||
  version "9.0.7" | 
				
			||||
  resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" | 
				
			||||
  integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0= | 
				
			||||
 | 
				
			||||
xtend@^4.0.0: | 
				
			||||
  version "4.0.1" | 
				
			||||
  resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" | 
				
			||||
  integrity sha1-pcbVMr5lbiPbgg77lDofBJmNY68= | 
				
			||||