diff --git a/CMakeLists.txt b/CMakeLists.txt index f8fd5b7637..8011808e3a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -256,6 +256,7 @@ endif() if (BUILD_CLIENT) add_subdirectory(interface) + add_subdirectory(screenshare) set_target_properties(interface PROPERTIES FOLDER "Apps") option(USE_SIXENSE "Build Interface with sixense library/plugin" OFF) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index c7ab810c9a..807f54953e 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -1081,6 +1081,12 @@ void AvatarMixer::setupEntityQuery() { priorityZoneQuery["avatarPriority"] = true; priorityZoneQuery["type"] = "Zone"; + QJsonObject queryFlags; + queryFlags["includeAncestors"] = true; + queryFlags["includeDescendants"] = true; + priorityZoneQuery["flags"] = queryFlags; + priorityZoneQuery["name"] = true; // Handy for debugging. + _entityViewer.getOctreeQuery().setJSONParameters(priorityZoneQuery); _slaveSharedData.entityTree = entityTree; } diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index 1195f0d801..ea5246c59a 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -99,6 +99,7 @@ namespace { glm::vec3 position; bool isInPriorityZone { false }; float zoneVolume { std::numeric_limits::max() }; + EntityItemID id {}; static bool operation(const OctreeElementPointer& element, void* extraData) { auto findPriorityZone = static_cast(extraData); @@ -113,6 +114,7 @@ namespace { if (volume < findPriorityZone->zoneVolume) { // Smaller volume wins findPriorityZone->isInPriorityZone = zoneItem->getAvatarPriority() == COMPONENT_MODE_ENABLED; findPriorityZone->zoneVolume = volume; + findPriorityZone->id = zoneItem->getEntityItemID(); } } } @@ -152,7 +154,15 @@ int AvatarMixerClientData::parseData(ReceivedMessage& message, const SlaveShared EntityTree& entityTree = *slaveSharedData.entityTree; FindPriorityZone findPriorityZone { newPosition } ; entityTree.recurseTreeWithOperation(&FindPriorityZone::operation, &findPriorityZone); - _avatar->setHasPriority(findPriorityZone.isInPriorityZone); + bool currentlyHasPriority = findPriorityZone.isInPriorityZone; + if (currentlyHasPriority != _avatar->getHasPriority()) { + _avatar->setHasPriority(currentlyHasPriority); + auto nodeList = DependencyManager::get(); + auto packet = NLPacket::create(PacketType::AvatarZonePresence, 2 * NUM_BYTES_RFC4122_UUID, true); + packet->write(_avatar->getSessionUUID().toRfc4122()); + packet->write(findPriorityZone.id.toRfc4122()); + nodeList->sendPacket(std::move(packet), nodeList->getDomainSockAddr()); + } _avatar->setNeedsHeroCheck(false); } diff --git a/cmake/macros/SetPackagingParameters.cmake b/cmake/macros/SetPackagingParameters.cmake index bf98dba29d..0d704184f8 100644 --- a/cmake/macros/SetPackagingParameters.cmake +++ b/cmake/macros/SetPackagingParameters.cmake @@ -146,23 +146,27 @@ macro(SET_PACKAGING_PARAMETERS) set(DMG_SUBFOLDER_ICON "${HF_CMAKE_DIR}/installer/install-folder.rsrc") - set(CONSOLE_INSTALL_DIR ${DMG_SUBFOLDER_NAME}) - set(INTERFACE_INSTALL_DIR ${DMG_SUBFOLDER_NAME}) - set(NITPICK_INSTALL_DIR ${DMG_SUBFOLDER_NAME}) + set(CONSOLE_INSTALL_DIR ${DMG_SUBFOLDER_NAME}) + set(INTERFACE_INSTALL_DIR ${DMG_SUBFOLDER_NAME}) + set(SCREENSHARE_INSTALL_DIR ${DMG_SUBFOLDER_NAME}) + set(NITPICK_INSTALL_DIR ${DMG_SUBFOLDER_NAME}) if (CLIENT_ONLY) set(CONSOLE_EXEC_NAME "Console.app") else () set(CONSOLE_EXEC_NAME "Sandbox.app") endif() - set(CONSOLE_INSTALL_APP_PATH "${CONSOLE_INSTALL_DIR}/${CONSOLE_EXEC_NAME}") + set(SCREENSHARE_EXEC_NAME "hifi-screenshare.app") + set(SCREENSHARE_INSTALL_APP_PATH "${SCREENSHARE_INSTALL_DIR}/${SCREENSHARE_EXEC_NAME}") + set(CONSOLE_APP_CONTENTS "${CONSOLE_INSTALL_APP_PATH}/Contents") set(COMPONENT_APP_PATH "${CONSOLE_APP_CONTENTS}/MacOS/Components.app") set(COMPONENT_INSTALL_DIR "${COMPONENT_APP_PATH}/Contents/MacOS") set(CONSOLE_PLUGIN_INSTALL_DIR "${COMPONENT_APP_PATH}/Contents/PlugIns") - + + set(SCREENSHARE_APP_CONTENTS "${SCREENSHARE_INSTALL_APP_PATH}/Contents") set(INTERFACE_INSTALL_APP_PATH "${CONSOLE_INSTALL_DIR}/${INTERFACE_BUNDLE_NAME}.app") set(INTERFACE_ICON_FILENAME "${INTERFACE_ICON_PREFIX}.icns") @@ -170,9 +174,11 @@ macro(SET_PACKAGING_PARAMETERS) else () if (WIN32) set(CONSOLE_INSTALL_DIR "server-console") + set(SCREENSHARE_INSTALL_DIR "hifi-screenshare") set(NITPICK_INSTALL_DIR "nitpick") else () set(CONSOLE_INSTALL_DIR ".") + set(SCREENSHARE_INSTALL_DIR ".") set(NITPICK_INSTALL_DIR ".") endif () @@ -186,6 +192,7 @@ macro(SET_PACKAGING_PARAMETERS) set(NITPICK_ICON_FILENAME "${NITPICK_ICON_PREFIX}.ico") set(CONSOLE_EXEC_NAME "server-console.exe") + set(SCREENSHARE_EXEC_NAME "hifi-screenshare.exe") set(DS_EXEC_NAME "domain-server.exe") set(AC_EXEC_NAME "assignment-client.exe") diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index ceb4679137..9eec7df86e 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -766,6 +766,7 @@ void DomainServer::setupNodeListAndAssignments() { packetReceiver.registerListener(PacketType::DomainServerPathQuery, this, "processPathQueryPacket"); packetReceiver.registerListener(PacketType::NodeJsonStats, this, "processNodeJSONStatsPacket"); packetReceiver.registerListener(PacketType::DomainDisconnectRequest, this, "processNodeDisconnectRequestPacket"); + packetReceiver.registerListener(PacketType::AvatarZonePresence, this, "processAvatarZonePresencePacket"); // NodeList won't be available to the settings manager when it is created, so call registerListener here packetReceiver.registerListener(PacketType::DomainSettingsRequest, &_settingsManager, "processSettingsRequestPacket"); @@ -3613,3 +3614,62 @@ void DomainServer::handleOctreeFileReplacementRequest(QSharedPointerreadAll(), QString(), QString(), username); } } + +void DomainServer::processAvatarZonePresencePacket(QSharedPointer message) { + QUuid avatar = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + QUuid zone = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + + if (avatar.isNull()) { + qCWarning(domain_server) << "Ignoring null avatar presence"; + return; + } + auto limitedNodeList = DependencyManager::get(); + auto matchingNode = limitedNodeList->nodeWithUUID(avatar); + if (!matchingNode) { + qCWarning(domain_server) << "Ignoring avatar presence for unknown avatar" << avatar; + return; + } + QString verifiedUsername = matchingNode->getPermissions().getVerifiedUserName(); + static const int SCREENSHARE_EXPIRATION_SECONDS = 24 * 60 * 60; + screensharePresence(zone.isNull() ? "" : zone.toString(), verifiedUsername, SCREENSHARE_EXPIRATION_SECONDS); +} + +void DomainServer::screensharePresence(QString roomname, QString username, int expirationSeconds) { + if (!DependencyManager::get()->hasValidAccessToken()) { + static std::once_flag presenceAuthorityWarning; + std::call_once(presenceAuthorityWarning, [] { + qCDebug(domain_server) << "No authority to send screensharePresence."; + }); + return; + } + JSONCallbackParameters callbackParams; + callbackParams.callbackReceiver = this; + callbackParams.jsonCallbackMethod = "handleSuccessfulScreensharePresence"; + callbackParams.errorCallbackMethod = "handleFailedScreensharePresence"; + const QString PATH = "api/v1/domains/%1/screenshare"; + QString domain_id = uuidStringWithoutCurlyBraces(getID()); + QJsonObject json, screenshare; + screenshare["username"] = username; + screenshare["roomname"] = roomname; + if (expirationSeconds > 0) { + screenshare["expiration"] = expirationSeconds; + } + json["screenshare"] = screenshare; + DependencyManager::get()->sendRequest( + PATH.arg(domain_id), + AccountManagerAuth::Required, + QNetworkAccessManager::PostOperation, + callbackParams, QJsonDocument(json).toJson() + ); +} + +void DomainServer::handleSuccessfulScreensharePresence(QNetworkReply* requestReply) { + QJsonObject jsonObject = QJsonDocument::fromJson(requestReply->readAll()).object(); + if (jsonObject["status"].toString() != "success") { + qCWarning(domain_server) << "screensharePresence api call failed:" << QJsonDocument(jsonObject).toJson(QJsonDocument::Compact); + } +} + +void DomainServer::handleFailedScreensharePresence(QNetworkReply* requestReply) { + qCWarning(domain_server) << "screensharePresence api call failed:" << requestReply->error(); +} diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index c725688b67..034ccb5a18 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -78,6 +78,8 @@ public: bool isAssetServerEnabled(); + void screensharePresence(QString roomname, QString username, int expiration_seconds = 0); + public slots: /// Called by NodeList to inform us a node has been added void nodeAdded(SharedNodePointer node); @@ -96,6 +98,7 @@ private slots: void processNodeDisconnectRequestPacket(QSharedPointer message); void processICEServerHeartbeatDenialPacket(QSharedPointer message); void processICEServerHeartbeatACK(QSharedPointer message); + void processAvatarZonePresencePacket(QSharedPointer packet); void handleDomainContentReplacementFromURLRequest(QSharedPointer message); void handleOctreeFileReplacementRequest(QSharedPointer message); @@ -129,6 +132,9 @@ private slots: void handleSuccessfulICEServerAddressUpdate(QNetworkReply* requestReply); void handleFailedICEServerAddressUpdate(QNetworkReply* requestReply); + void handleSuccessfulScreensharePresence(QNetworkReply* requestReply); + void handleFailedScreensharePresence(QNetworkReply* requestReply); + void updateReplicatedNodes(); void updateDownstreamNodes(); void updateUpstreamNodes(); diff --git a/interface/resources/qml/Web3DSurface.qml b/interface/resources/qml/Web3DSurface.qml index 3340226761..d58bcd2eba 100644 --- a/interface/resources/qml/Web3DSurface.qml +++ b/interface/resources/qml/Web3DSurface.qml @@ -49,4 +49,6 @@ Item { Component.onCompleted: { load(root.url, root.scriptUrl); } + + signal sendToScript(var message); } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a1c6efa32d..28e3368b30 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -184,6 +184,7 @@ #include "scripting/AssetMappingsScriptingInterface.h" #include "scripting/ClipboardScriptingInterface.h" #include "scripting/DesktopScriptingInterface.h" +#include "scripting/ScreenshareScriptingInterface.h" #include "scripting/AccountServicesScriptingInterface.h" #include "scripting/HMDScriptingInterface.h" #include "scripting/MenuScriptingInterface.h" @@ -967,6 +968,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(); PlatformHelper::setup(); QObject::connect(PlatformHelper::instance(), &PlatformHelper::systemWillWake, [] { @@ -2919,6 +2921,7 @@ Application::~Application() { DependencyManager::destroy(); DependencyManager::destroy(); DependencyManager::destroy(); + DependencyManager::destroy(); DependencyManager::get()->cleanup(); @@ -3430,7 +3433,7 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) { surfaceContext->setContextProperty("Users", DependencyManager::get().data()); surfaceContext->setContextProperty("UserActivityLogger", DependencyManager::get().data()); - + surfaceContext->setContextProperty("Screenshare", DependencyManager::get().data()); surfaceContext->setContextProperty("Camera", &_myCamera); #if defined(Q_OS_MAC) || defined(Q_OS_WIN) @@ -3536,6 +3539,7 @@ void Application::userKickConfirmation(const QUuid& nodeID) { } void Application::setupQmlSurface(QQmlContext* surfaceContext, bool setAdditionalContextProperties) { + surfaceContext->setContextProperty("Screenshare", DependencyManager::get().data()); surfaceContext->setContextProperty("Users", DependencyManager::get().data()); surfaceContext->setContextProperty("HMD", DependencyManager::get().data()); surfaceContext->setContextProperty("UserActivityLogger", DependencyManager::get().data()); @@ -7314,6 +7318,7 @@ void Application::registerScriptEngineWithApplicationServices(const ScriptEngine scriptEngine->registerGlobalObject("AvatarList", DependencyManager::get().data()); scriptEngine->registerGlobalObject("Camera", &_myCamera); + scriptEngine->registerGlobalObject("Screenshare", DependencyManager::get().data()); #if defined(Q_OS_MAC) || defined(Q_OS_WIN) scriptEngine->registerGlobalObject("SpeechRecognizer", DependencyManager::get().data()); diff --git a/interface/src/scripting/ScreenshareScriptingInterface.cpp b/interface/src/scripting/ScreenshareScriptingInterface.cpp new file mode 100644 index 0000000000..1dbd2eac2b --- /dev/null +++ b/interface/src/scripting/ScreenshareScriptingInterface.cpp @@ -0,0 +1,276 @@ +// +// ScreenshareScriptingInterface.cpp +// interface/src/scripting/ +// +// Created by Milad Nazeri and Zach Fox on 2019-10-23. +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "EntityScriptingInterface.h" +#include "ScreenshareScriptingInterface.h" + +ScreenshareScriptingInterface::ScreenshareScriptingInterface() { + auto esi = DependencyManager::get(); + if (!esi) { + return; + } + + // This signal/slot connection is used when the screen share local web entity sends an event bridge message. + QObject::connect(esi.data(), &EntityScriptingInterface::webEventReceived, this, &ScreenshareScriptingInterface::onWebEventReceived); +}; + +ScreenshareScriptingInterface::~ScreenshareScriptingInterface() { + stopScreenshare(); +} + +static const EntityTypes::EntityType LOCAL_SCREENSHARE_WEB_ENTITY_TYPE = EntityTypes::Web; +static const uint8_t LOCAL_SCREENSHARE_WEB_ENTITY_FPS = 30; +// This is going to be a good amount of work to make this work dynamically for any screensize. +// V1 will have only hardcoded values. +static const glm::vec3 LOCAL_SCREENSHARE_WEB_ENTITY_LOCAL_POSITION(0.0f, -0.0862f, 0.0711f); +static const glm::vec3 LOCAL_SCREENSHARE_WEB_ENTITY_DIMENSIONS(4.0419f, 2.2735f, 0.0100f); +static const QString LOCAL_SCREENSHARE_WEB_ENTITY_URL = + "https://content.highfidelity.com/Experiences/Releases/usefulUtilities/smartBoard/screenshareViewer/screenshareClient.html"; +static const QString LOCAL_SCREENSHARE_WEB_ENTITY_HOST_TYPE ="local"; +void ScreenshareScriptingInterface::startScreenshare(const QUuid& screenshareZoneID, + const QUuid& smartboardEntityID, + const bool& isPresenter) { + // We must start a new QProcess from the main thread. + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "startScreenshare", Q_ARG(const QUuid&, screenshareZoneID), + Q_ARG(const QUuid&, smartboardEntityID), Q_ARG(const bool&, isPresenter)); + return; + } + + // These three private member variables are set now so that they may be used later during asynchronous + // callbacks. + _screenshareZoneID = screenshareZoneID; + _smartboardEntityID = smartboardEntityID; + _isPresenter = isPresenter; + + // If we are presenting, and the screenshare process is already running, don't do anything else here. + if (_isPresenter && _screenshareProcess && _screenshareProcess->state() != QProcess::NotRunning) { + return; + } + + // If we're presenting... + if (_isPresenter) { + // ...make sure we first reset this `std::unique_ptr`. + _screenshareProcess.reset(new QProcess(this)); + + // Ensure that the screenshare executable exists where we expect it to. + // Error out and reset the screen share state machine if the executable doesn't exist. + QFileInfo screenshareExecutable(SCREENSHARE_EXE_PATH); + if (!screenshareExecutable.exists() || !screenshareExecutable.isFile()) { + qDebug() << "Screenshare executable doesn't exist at" << SCREENSHARE_EXE_PATH; + stopScreenshare(); + emit screenshareError(); + return; + } + } + + // Don't continue with any more of this logic if we can't get the `AccountManager` or `AddressManager`. + auto accountManager = DependencyManager::get(); + if (!accountManager) { + return; + } + auto addressManager = DependencyManager::get(); + if (!addressManager) { + return; + } + + // Construct and send a request to the Metaverse to obtain the information + // necessary to start the screen sharing process. + // This request requires: + // 1. The domain ID of the domain in which the user's avatar is present + // 2. User authentication information that is automatically included when `sendRequest()` is passed + // with the `AccountManagerAuth::Required` argument. + // Note that this request will only return successfully if the Domain Server has already registered + // the user paired with the current domain with the Metaverse. + // See `DomainServer::screensharePresence()` for more info about that. + QString currentDomainID = uuidStringWithoutCurlyBraces(addressManager->getDomainID()); + QString requestURLPath = "api/v1/domains/%1/screenshare"; + JSONCallbackParameters callbackParams; + callbackParams.callbackReceiver = this; + callbackParams.jsonCallbackMethod = "handleSuccessfulScreenshareInfoGet"; + callbackParams.errorCallbackMethod = "handleFailedScreenshareInfoGet"; + accountManager->sendRequest( + requestURLPath.arg(currentDomainID), + AccountManagerAuth::Required, + QNetworkAccessManager::GetOperation, + callbackParams + ); +} + +void ScreenshareScriptingInterface::stopScreenshare() { + // We can only deal with our Screen Share `QProcess` on the main thread. + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "stopScreenshare"); + return; + } + + // If the Screen Share process is running... + if (_screenshareProcess && _screenshareProcess->state() != QProcess::NotRunning) { + //...terminate it and make sure that scripts know we terminated it by emitting + // `screenshareProcessTerminated()`. + _screenshareProcess->terminate(); + emit screenshareProcessTerminated(); + } + + // Delete the local web entity if we know about it here. + if (!_screenshareViewerLocalWebEntityUUID.isNull()) { + auto esi = DependencyManager::get(); + if (esi) { + esi->deleteEntity(_screenshareViewerLocalWebEntityUUID); + } + } + + // Reset all private member variables related to screen share here. + _screenshareViewerLocalWebEntityUUID = "{00000000-0000-0000-0000-000000000000}"; + _token = ""; + _projectAPIKey = ""; + _sessionID = ""; + _isPresenter = false; +} + +// Called when the Metaverse returns the information necessary to start/view a screen share. +void ScreenshareScriptingInterface::handleSuccessfulScreenshareInfoGet(QNetworkReply* reply) { + // Read the reply and get it into a format we understand. + QString answer = reply->readAll(); + QByteArray answerByteArray = answer.toUtf8(); + QJsonDocument answerJSONObject = QJsonDocument::fromJson(answerByteArray); + + // This Metaverse endpoint will always return a status key/value pair of "success" if things went well. + QString status = answerJSONObject["status"].toString(); + if (status != "success") { + qDebug() << "Error when retrieving screenshare info via HTTP. Error:" << reply->errorString(); + stopScreenshare(); + emit screenshareError(); + return; + } + + // Store the information necessary to start/view a screen share in these private member variables. + _token = answerJSONObject["token"].toString(); + _projectAPIKey = answerJSONObject["projectApiKey"].toString(); + _sessionID = answerJSONObject["sessionID"].toString(); + + // Make sure we have all of the info that we need. + if (_token.isEmpty() || _projectAPIKey.isEmpty() || _sessionID.isEmpty()) { + qDebug() << "Not all Screen Share information was retrieved from the backend. Stopping..."; + stopScreenshare(); + emit screenshareError(); + return; + } + + // If we're presenting: + // 1. Build a list of arguments that we're going to pass to the screen share Electron app. + // 2. Make sure we connect a signal/slot to know when the user quits the Electron app. + // 3. Start the screen share Electron app with the list of args from (1). + if (_isPresenter) { + QStringList arguments; + arguments << " "; + arguments << "--token=" + _token << " "; + arguments << "--projectAPIKey=" + _projectAPIKey << " "; + arguments << "--sessionID=" + _sessionID << " "; + + connect(_screenshareProcess.get(), QOverload::of(&QProcess::finished), + [=](int exitCode, QProcess::ExitStatus exitStatus) { + stopScreenshare(); + emit screenshareProcessTerminated(); + }); + + _screenshareProcess->start(SCREENSHARE_EXE_PATH, arguments); + } + + // Make sure we can grab the entity scripting interface. Error out if we can't. + auto esi = DependencyManager::get(); + if (!esi) { + stopScreenshare(); + emit screenshareError(); + return; + } + + // If, for some reason, we already have a record of a screen share local Web entity, delete it. + if (!_screenshareViewerLocalWebEntityUUID.isNull()) { + esi->deleteEntity(_screenshareViewerLocalWebEntityUUID); + } + + // Set up the entity properties associated with the screen share local Web entity. + EntityItemProperties localScreenshareWebEntityProps; + localScreenshareWebEntityProps.setType(LOCAL_SCREENSHARE_WEB_ENTITY_TYPE); + localScreenshareWebEntityProps.setMaxFPS(LOCAL_SCREENSHARE_WEB_ENTITY_FPS); + localScreenshareWebEntityProps.setLocalPosition(LOCAL_SCREENSHARE_WEB_ENTITY_LOCAL_POSITION); + localScreenshareWebEntityProps.setSourceUrl(LOCAL_SCREENSHARE_WEB_ENTITY_URL); + localScreenshareWebEntityProps.setParentID(_smartboardEntityID); + localScreenshareWebEntityProps.setDimensions(LOCAL_SCREENSHARE_WEB_ENTITY_DIMENSIONS); + + // The lines below will be used when writing the feature to support scaling the Smartboard entity to any arbitrary size. + //EntityPropertyFlags desiredSmartboardProperties; + //desiredSmartboardProperties += PROP_POSITION; + //desiredSmartboardProperties += PROP_DIMENSIONS; + //EntityItemProperties smartboardProps = esi->getEntityProperties(_smartboardEntityID, desiredSmartboardProperties); + + // Add the screen share local Web entity to Interface's entity tree. + // When the Web entity loads the page specified by `LOCAL_SCREENSHARE_WEB_ENTITY_URL`, it will broadcast an Event Bridge + // message, which we will consume inside `ScreenshareScriptingInterface::onWebEventReceived()`. + _screenshareViewerLocalWebEntityUUID = esi->addEntity(localScreenshareWebEntityProps, LOCAL_SCREENSHARE_WEB_ENTITY_HOST_TYPE); +} + +void ScreenshareScriptingInterface::handleFailedScreenshareInfoGet(QNetworkReply* reply) { + qDebug() << "Failed to get screenshare info via HTTP. Error:" << reply->errorString(); + stopScreenshare(); + emit screenshareError(); +} + +// This function will handle _all_ web events received via `EntityScriptingInterface::webEventReceived()`, including +// those not related to screen sharing. +void ScreenshareScriptingInterface::onWebEventReceived(const QUuid& entityID, const QVariant& message) { + // Bail early if the entity that sent the Web event isn't the one we care about. + if (entityID == _screenshareViewerLocalWebEntityUUID) { + // Bail early if we can't grab the Entity Scripting Interface. + auto esi = DependencyManager::get(); + if (!esi) { + return; + } + + // Web events received from the screen share Web JS will always be in stringified JSON format. + QByteArray jsonByteArray = QVariant(message).toString().toUtf8(); + QJsonDocument jsonObject = QJsonDocument::fromJson(jsonByteArray); + + // It should never happen where the screen share Web JS sends a message without the `app` key's value + // set to "screenshare". + if (jsonObject["app"] != "screenshare") { + return; + } + + // The screen share Web JS only sends a message with one method: "eventBridgeReady". Handle it here. + if (jsonObject["method"] == "eventBridgeReady") { + // Stuff a JSON object full of information necessary for the screen share local Web entity + // to connect to the screen share session associated with the room in which the user's avatar is standing. + QJsonObject responseObject; + responseObject.insert("app", "screenshare"); + responseObject.insert("method", "receiveConnectionInfo"); + QJsonObject responseObjectData; + responseObjectData.insert("token", _token); + responseObjectData.insert("projectAPIKey", _projectAPIKey); + responseObjectData.insert("sessionID", _sessionID); + responseObject.insert("data", responseObjectData); + + esi->emitScriptEvent(_screenshareViewerLocalWebEntityUUID, responseObject.toVariantMap()); + } + } +} diff --git a/interface/src/scripting/ScreenshareScriptingInterface.h b/interface/src/scripting/ScreenshareScriptingInterface.h new file mode 100644 index 0000000000..7186ba4c13 --- /dev/null +++ b/interface/src/scripting/ScreenshareScriptingInterface.h @@ -0,0 +1,72 @@ +// +// ScreenshareScriptingInterface.h +// interface/src/scripting/ +// +// Created by Milad Nazeri and Zach Fox on 2019-10-23. +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ScreenshareScriptingInterface_h +#define hifi_ScreenshareScriptingInterface_h + +#include +#include +#include +#include + +#include + +class ScreenshareScriptingInterface : public QObject, public Dependency { + Q_OBJECT +public: + ScreenshareScriptingInterface(); + ~ScreenshareScriptingInterface(); + + Q_INVOKABLE void startScreenshare(const QUuid& screenshareZoneID, const QUuid& smartboardEntityID, const bool& isPresenter = false); + Q_INVOKABLE void stopScreenshare(); + +signals: + void screenshareError(); + void screenshareProcessTerminated(); + void startScreenshareViewer(); + +private slots: + void onWebEventReceived(const QUuid& entityID, const QVariant& message); + void handleSuccessfulScreenshareInfoGet(QNetworkReply* reply); + void handleFailedScreenshareInfoGet(QNetworkReply* reply); + +private: +#if DEV_BUILD +#ifdef Q_OS_WIN + const QString SCREENSHARE_EXE_PATH{ PathUtils::projectRootPath() + "/screenshare/hifi-screenshare-win32-x64/hifi-screenshare.exe" }; +#elif defined(Q_OS_MAC) + const QString SCREENSHARE_EXE_PATH{ PathUtils::projectRootPath() + "/screenshare/screenshare-darwin-x64/hifi-screenshare.app" }; +#else + // This path won't exist on other platforms, so the Screenshare Scripting Interface will exit early when invoked. + const QString SCREENSHARE_EXE_PATH{ PathUtils::projectRootPath() + "/screenshare/screenshare-other-os/hifi-screenshare" }; +#endif +#else +#ifdef Q_OS_WIN + const QString SCREENSHARE_EXE_PATH{ QCoreApplication::applicationDirPath() + "/hifi-screenshare/hifi-screenshare.exe" }; +#elif defined(Q_OS_MAC) + const QString SCREENSHARE_EXE_PATH{ QCoreApplication::applicationDirPath() + "/hifi-screenshare/hifi-screenshare.app" }; +#else + // This path won't exist on other platforms, so the Screenshare Scripting Interface will exit early when invoked. + const QString SCREENSHARE_EXE_PATH{ QCoreApplication::applicationDirPath() + "/hifi-screenshare/hifi-screenshare" }; +#endif +#endif + + std::unique_ptr _screenshareProcess{ nullptr }; + QUuid _screenshareViewerLocalWebEntityUUID; + QString _token{ "" }; + QString _projectAPIKey{ "" }; + QString _sessionID{ "" }; + QUuid _screenshareZoneID; + QUuid _smartboardEntityID; + bool _isPresenter{ false }; +}; + +#endif // hifi_ScreenshareScriptingInterface_h diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index fbf575065e..75904d8122 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -135,6 +135,7 @@ public: AudioSoloRequest, BulkAvatarTraitsAck, StopInjector, + AvatarZonePresence, NUM_PACKET_TYPE }; @@ -185,7 +186,8 @@ public: << PacketTypeEnum::Value::OctreeFileReplacement << PacketTypeEnum::Value::ReplicatedMicrophoneAudioNoEcho << PacketTypeEnum::Value::ReplicatedMicrophoneAudioWithEcho << PacketTypeEnum::Value::ReplicatedInjectAudio << PacketTypeEnum::Value::ReplicatedSilentAudioFrame << PacketTypeEnum::Value::ReplicatedAvatarIdentity - << PacketTypeEnum::Value::ReplicatedKillAvatar << PacketTypeEnum::Value::ReplicatedBulkAvatarData; + << PacketTypeEnum::Value::ReplicatedKillAvatar << PacketTypeEnum::Value::ReplicatedBulkAvatarData + << PacketTypeEnum::Value::AvatarZonePresence; return NON_SOURCED_PACKETS; } diff --git a/screenshare/.gitignore b/screenshare/.gitignore new file mode 100644 index 0000000000..e978d75d04 --- /dev/null +++ b/screenshare/.gitignore @@ -0,0 +1,4 @@ +hifi-screenshare-*/ +hifi-screenshare*.zip +screenshare*.zip +screenshare-*/ diff --git a/screenshare/CMakeLists.txt b/screenshare/CMakeLists.txt new file mode 100644 index 0000000000..1e675c09a4 --- /dev/null +++ b/screenshare/CMakeLists.txt @@ -0,0 +1,38 @@ +set(TARGET_NAME screenshare) + +add_custom_target(${TARGET_NAME}-npm-install + COMMAND npm install + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} +) +add_custom_target(${TARGET_NAME} + COMMAND npm run packager -- --out ${CMAKE_CURRENT_BINARY_DIR} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + DEPENDS ${TARGET_NAME}-npm-install +) + +set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "screenshare") +set_target_properties(${TARGET_NAME}-npm-install PROPERTIES FOLDER "hidden/screenshare") + +if (WIN32) + set(PACKAGED_SCREENSHARE_FOLDER "hifi-screenshare-win32-x64") + set(SCREENSHARE_DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/${PACKAGED_SCREENSHARE_FOLDER}") + install( + DIRECTORY "${SCREENSHARE_DESTINATION}/" + DESTINATION ${SCREENSHARE_INSTALL_DIR} + ) + + set(EXECUTABLE_PATH "${SCREENSHARE_DESTINATION}/${SCREENSHARE_EXEC_NAME}") + optional_win_executable_signing() +elseif (APPLE) + set(PACKAGED_SCREENSHARE_FOLDER "hifi-screenshare-darwin-x64/${SCREENSHARE_EXEC_NAME}") + install( + DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${PACKAGED_SCREENSHARE_FOLDER}" + USE_SOURCE_PERMISSIONS + DESTINATION ${SCREENSHARE_INSTALL_DIR} + ) +endif() + +# DO build the Screenshare Electron app when building the `ALL_BUILD` target. +# DO build the Screenshare Electron app when a user selects "Build Solution" from within Visual Studio. +set_target_properties(${TARGET_NAME} PROPERTIES EXCLUDE_FROM_ALL FALSE EXCLUDE_FROM_DEFAULT_BUILD FALSE) +set_target_properties(${TARGET_NAME}-npm-install PROPERTIES EXCLUDE_FROM_ALL FALSE EXCLUDE_FROM_DEFAULT_BUILD FALSE) diff --git a/screenshare/README.md b/screenshare/README.md new file mode 100644 index 0000000000..e57f37adc1 --- /dev/null +++ b/screenshare/README.md @@ -0,0 +1,16 @@ +# Screen Sharing within High Fidelity +This Screen Share app, built using Electron, allows for easy desktop screen sharing when used in conjuction with various scripts in the `hifi-content` repository. + +# Screen Sharing Source Files +## `packager.js` +Calling npm run packager will use this file to create the actual Electron `hifi-screenshare` executable. +It will kick out a folder `hifi-screenshare-` which contains an executable. + +## `src/screenshareApp.js` +The main process file to configure the electron app. + +## `src/screenshareMainProcess.js` +The render file to display the app's UI. + +## `screenshareApp.html` +The HTML that displays the screen selection UI and the confirmation screen UI. \ No newline at end of file diff --git a/screenshare/package-lock.json b/screenshare/package-lock.json new file mode 100644 index 0000000000..c7d92d3e17 --- /dev/null +++ b/screenshare/package-lock.json @@ -0,0 +1,2289 @@ +{ + "name": "highfidelity_screenshare", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@electron/get": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.5.0.tgz", + "integrity": "sha512-tafxBz6n08G6SX961F/h8XFtpB/DdwRvJJoDeOH9x78jDSCMQ2G/rRWqSwLFp9oeMFBJf0Pf5Kkw6TKt5w9TWg==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "got": "^9.6.0", + "sanitize-filename": "^1.6.2", + "sumchecker": "^3.0.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "env-paths": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz", + "integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==", + "dev": true + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "sumchecker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.0.tgz", + "integrity": "sha512-yreseuC/z4iaodVoq07XULEOO9p4jnQazO7mbrnDSvWAU/y2cbyIKs+gWJptfcGu9R+1l27K8Rkj0bfvqnBpgQ==", + "dev": true, + "requires": { + "debug": "^4.1.0" + } + } + } + }, + "@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "dev": true + }, + "@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "dev": true, + "requires": { + "defer-to-connect": "^1.0.1" + } + }, + "@types/node": { + "version": "10.14.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.21.tgz", + "integrity": "sha512-nuFlRdBiqbF+PJIEVxm2jLFcQWN7q7iWEJGsBV4n7v1dbI9qXB8im2pMMKMCUZe092sQb5SQft2DHfuQGK5hqQ==", + "dev": true + }, + "ajv": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true + }, + "asar": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/asar/-/asar-2.0.1.tgz", + "integrity": "sha512-Vo9yTuUtyFahkVMFaI6uMuX6N7k5DWa6a/8+7ov0/f8Lq9TVR0tUjzSzxQSxT1Y+RJIZgnP7BVb6Uhi+9cjxqA==", + "dev": true, + "requires": { + "chromium-pickle-js": "^0.2.0", + "commander": "^2.20.0", + "cuint": "^0.2.2", + "glob": "^7.1.3", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "tmp-promise": "^1.0.5" + } + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "author-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/author-regex/-/author-regex-1.0.0.tgz", + "integrity": "sha1-0IiFvmubv5Q5/gh8dihyRfCoFFA=", + "dev": true + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bluebird": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.1.tgz", + "integrity": "sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "dev": true, + "requires": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", + "dev": true + }, + "buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=", + "dev": true + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "dev": true, + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "dependencies": { + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true + } + } + }, + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "chromium-pickle-js": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz", + "integrity": "sha1-BKEGZywYsIWrd02YPfo+oTjyIgU=", + "dev": true + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "compare-version": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", + "integrity": "sha1-AWLsLZNR9d3VmpICy6k1NmpyUIA=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "cross-zip": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/cross-zip/-/cross-zip-2.1.6.tgz", + "integrity": "sha512-xLIETNkzRcU6jGRzenJyRFxahbtP4628xEKMTI/Ql0Vu8m4h8M7uRLVi7E5OYHuJ6VQPsG4icJumKAFUvfm0+A==", + "dev": true, + "requires": { + "rimraf": "^3.0.0" + }, + "dependencies": { + "rimraf": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.0.tgz", + "integrity": "sha512-NDGVxTsjqfunkds7CqsOiEnxln4Bo7Nddl3XhS4pXg5OzwkLqJ971ZVAAnB+DDLnF76N+VnDEiBHaVV8I06SUg==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "cuint": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/cuint/-/cuint-0.2.2.tgz", + "integrity": "sha1-QICG1AlVDCYxFVYZ6fp7ytw7mRs=", + "dev": true + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "requires": { + "array-find-index": "^1.0.1" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true + }, + "defer-to-connect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.0.2.tgz", + "integrity": "sha512-k09hcQcTDY+cwgiwa6PYKLm3jlagNzQ+RSvhjzESOGOx+MNOuXkxTfEvPrO1IOQ81tArCFYQgi631clB70RpQw==", + "dev": true + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "dev": true + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "electron": { + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/electron/-/electron-6.0.12.tgz", + "integrity": "sha512-70ODZa1RP6K0gE9IV9YLCXPSyhLjXksCuYSSPb3MljbfwfHo5uE6X0CGxzm+54YuPdE2e7EPnWZxOOsJYrS5iQ==", + "dev": true, + "requires": { + "@types/node": "^10.12.18", + "electron-download": "^4.1.0", + "extract-zip": "^1.0.3" + } + }, + "electron-download": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/electron-download/-/electron-download-4.1.1.tgz", + "integrity": "sha512-FjEWG9Jb/ppK/2zToP+U5dds114fM1ZOJqMAR4aXXL5CvyPE9fiqBK/9YcwC9poIFQTEJk/EM/zyRwziziRZrg==", + "dev": true, + "requires": { + "debug": "^3.0.0", + "env-paths": "^1.0.0", + "fs-extra": "^4.0.1", + "minimist": "^1.2.0", + "nugget": "^2.0.1", + "path-exists": "^3.0.0", + "rc": "^1.2.1", + "semver": "^5.4.1", + "sumchecker": "^2.0.2" + } + }, + "electron-notarize": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/electron-notarize/-/electron-notarize-0.1.1.tgz", + "integrity": "sha512-TpKfJcz4LXl5jiGvZTs5fbEx+wUFXV5u8voeG5WCHWfY/cdgdD8lDZIZRqLVOtR3VO+drgJ9aiSHIO9TYn/fKg==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "fs-extra": "^8.0.1" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + } + } + }, + "electron-osx-sign": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/electron-osx-sign/-/electron-osx-sign-0.4.14.tgz", + "integrity": "sha512-72vtrz9I3dOeFDaNvO5thwIjrimDiXMmYEbN0hEBqnvcSSMOWugjim2wiY9ox3dhuBFUhxp3owmuZCoH3Ij08A==", + "dev": true, + "requires": { + "bluebird": "^3.5.0", + "compare-version": "^0.1.2", + "debug": "^2.6.8", + "isbinaryfile": "^3.0.2", + "minimist": "^1.2.0", + "plist": "^3.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "electron-packager": { + "version": "14.0.6", + "resolved": "https://registry.npmjs.org/electron-packager/-/electron-packager-14.0.6.tgz", + "integrity": "sha512-X+ikV+TnnNkIrK93vOjsjPeykCQBFxBS7LXKMTE1s62rXWirGMdjWL+edVkBOMRkH0ROJyFmWM28Dpj6sfEg+A==", + "dev": true, + "requires": { + "@electron/get": "^1.3.0", + "asar": "^2.0.1", + "cross-zip": "^2.1.5", + "debug": "^4.0.1", + "electron-notarize": "^0.1.1", + "electron-osx-sign": "^0.4.11", + "fs-extra": "^8.1.0", + "galactus": "^0.2.1", + "get-package-info": "^1.0.0", + "junk": "^3.1.0", + "parse-author": "^2.0.0", + "plist": "^3.0.0", + "rcedit": "^2.0.0", + "resolve": "^1.1.6", + "sanitize-filename": "^1.6.0", + "semver": "^6.0.0", + "yargs-parser": "^13.0.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "env-paths": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-1.0.0.tgz", + "integrity": "sha1-QWgTO0K7BcOKNbGuQ5fIKYqzaeA=", + "dev": true + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "extract-zip": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz", + "integrity": "sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=", + "dev": true, + "requires": { + "concat-stream": "1.6.2", + "debug": "2.6.9", + "mkdirp": "0.5.1", + "yauzl": "2.4.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, + "fd-slicer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", + "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", + "dev": true, + "requires": { + "pend": "~1.2.0" + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + } + } + }, + "flora-colossus": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/flora-colossus/-/flora-colossus-1.0.1.tgz", + "integrity": "sha512-d+9na7t9FyH8gBJoNDSi28mE4NgQVGGvxQ4aHtFRetjyh5SXjuus+V5EZaxFmFdXVemSOrx0lsgEl/ZMjnOWJA==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "fs-extra": "^7.0.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + } + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "fs-extra": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", + "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "galactus": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/galactus/-/galactus-0.2.1.tgz", + "integrity": "sha1-y+0tIKQMH1Z5o1kI4rlBVzPnjbk=", + "dev": true, + "requires": { + "debug": "^3.1.0", + "flora-colossus": "^1.0.0", + "fs-extra": "^4.0.0" + } + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + }, + "get-package-info": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-package-info/-/get-package-info-1.0.0.tgz", + "integrity": "sha1-ZDJ5ZWPigRPNlHTbvQAFKYWkmZw=", + "dev": true, + "requires": { + "bluebird": "^3.1.1", + "debug": "^2.2.0", + "lodash.get": "^4.0.0", + "read-pkg-up": "^2.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "^2.0.0" + } + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } + } + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "dev": true, + "requires": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + } + }, + "graceful-fs": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", + "dev": true + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "dev": true, + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "hosted-git-info": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.5.tgz", + "integrity": "sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==", + "dev": true + }, + "http-cache-semantics": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.0.3.tgz", + "integrity": "sha512-TcIMG3qeVLgDr1TEd2XvHaTnMPwYQUQMIBLy+5pLSDKYFc7UIqj39w8EGzZkaxoLv/l2K8HaI0t5AVA+YYgUew==", + "dev": true + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "isbinaryfile": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", + "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", + "dev": true, + "requires": { + "buffer-alloc": "^1.2.0" + } + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "junk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/junk/-/junk-3.1.0.tgz", + "integrity": "sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==", + "dev": true + }, + "keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "dev": true, + "requires": { + "json-buffer": "3.0.0" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", + "dev": true + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + } + }, + "mime-db": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", + "dev": true + }, + "mime-types": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "dev": true, + "requires": { + "mime-db": "1.40.0" + } + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + } + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-url": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", + "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", + "dev": true + }, + "nugget": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nugget/-/nugget-2.0.1.tgz", + "integrity": "sha1-IBCVpIfhrTYIGzQy+jytpPjQcbA=", + "dev": true, + "requires": { + "debug": "^2.1.3", + "minimist": "^1.1.0", + "pretty-bytes": "^1.0.2", + "progress-stream": "^1.1.0", + "request": "^2.45.0", + "single-line-log": "^1.1.2", + "throttleit": "0.0.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-keys": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", + "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "parse-author": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-author/-/parse-author-2.0.0.tgz", + "integrity": "sha1-00YL8d3Q367tQtp1QkLmX7aEqB8=", + "dev": true, + "requires": { + "author-regex": "^1.0.0" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "dev": true + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "plist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/plist/-/plist-3.0.1.tgz", + "integrity": "sha512-GpgvHHocGRyQm74b6FWEZZVRroHKE1I0/BTjAmySaohK+cUn+hZpbqXkc3KWgW3gQYkqcQej35FohcT0FRlkRQ==", + "dev": true, + "requires": { + "base64-js": "^1.2.3", + "xmlbuilder": "^9.0.7", + "xmldom": "0.1.x" + } + }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", + "dev": true + }, + "pretty-bytes": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-1.0.4.tgz", + "integrity": "sha1-CiLoIQYJrTVUL4yNXSFZr/B1HIQ=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1", + "meow": "^3.1.0" + } + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "progress-stream": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/progress-stream/-/progress-stream-1.2.0.tgz", + "integrity": "sha1-LNPP6jO6OonJwSHsM0er6asSX3c=", + "dev": true, + "requires": { + "speedometer": "~0.1.2", + "through2": "~0.2.3" + } + }, + "psl": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz", + "integrity": "sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw==", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + } + }, + "rcedit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/rcedit/-/rcedit-2.0.0.tgz", + "integrity": "sha512-XcFGyEBjhWSsud+R8elwQtGBbVkCf7tAiad+nXo5jc6l2rMf46NfGNwjnmBNneBIZDfq+Npf8lwP371JTONfrw==", + "dev": true + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "requires": { + "is-finite": "^1.0.0" + } + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "dev": true, + "requires": { + "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" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + }, + "resolve": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", + "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "dev": true, + "requires": { + "lowercase-keys": "^1.0.0" + } + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "sanitize-filename": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", + "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", + "dev": true, + "requires": { + "truncate-utf8-bytes": "^1.0.0" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "single-line-log": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/single-line-log/-/single-line-log-1.1.2.tgz", + "integrity": "sha1-wvg/Jzo+GhbtsJlWYdoO1e8DM2Q=", + "dev": true, + "requires": { + "string-width": "^1.0.1" + } + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "dev": true + }, + "speedometer": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/speedometer/-/speedometer-0.1.4.tgz", + "integrity": "sha1-mHbb0qFp0xFUAtSObqYynIgWpQ0=", + "dev": true + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "requires": { + "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" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "sumchecker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-2.0.2.tgz", + "integrity": "sha1-D0LBDl0F2l1C7qPlbDOZo31sWz4=", + "dev": true, + "requires": { + "debug": "^2.2.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "throttleit": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-0.0.2.tgz", + "integrity": "sha1-z+34jmDADdlpe2H90qg0OptoDq8=", + "dev": true + }, + "through2": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.2.3.tgz", + "integrity": "sha1-6zKE2k6jEbbMis42U3SKUqvyWj8=", + "dev": true, + "requires": { + "readable-stream": "~1.1.9", + "xtend": "~2.1.1" + } + }, + "tmp": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz", + "integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==", + "dev": true, + "requires": { + "rimraf": "^2.6.3" + } + }, + "tmp-promise": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-1.1.0.tgz", + "integrity": "sha512-8+Ah9aB1IRXCnIOxXZ0uFozV1nMU5xiu7hhFVUSxZ3bYu+psD4TzagCzVbexUCgNNGJnsmNDQlS4nG3mTyoNkw==", + "dev": true, + "requires": { + "bluebird": "^3.5.0", + "tmp": "0.1.0" + } + }, + "to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", + "dev": true + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "dev": true, + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } + } + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true + }, + "truncate-utf8-bytes": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", + "integrity": "sha1-QFkjkJWS1W94pYGENLC3hInKXys=", + "dev": true, + "requires": { + "utf8-byte-length": "^1.0.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "dev": true, + "requires": { + "prepend-http": "^2.0.0" + } + }, + "utf8-byte-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz", + "integrity": "sha1-9F8VDExm7uloGGUFq5P8u4rWv2E=", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "uuid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", + "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=", + "dev": true + }, + "xmldom": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz", + "integrity": "sha1-1QH5ezvbQDr4757MIFcxh6rawOk=", + "dev": true + }, + "xtend": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", + "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", + "dev": true, + "requires": { + "object-keys": "~0.4.0" + } + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" + }, + "yargs": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.0.tgz", + "integrity": "sha512-/is78VKbKs70bVZH7w4YaZea6xcJWOAwkhbR0CFuZBmYtfTYF0xjGJF43AYd8g2Uii1yJwmS5GR2vBmrc32sbg==", + "requires": { + "cliui": "^5.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^15.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "yargs-parser": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.0.tgz", + "integrity": "sha512-xLTUnCMc4JhxrPEPUYD5IBR1mWCK/aT6+RJ/K29JY2y1vD+FhtgKK0AXRWvI262q3QSffAQuTouFIKUuHX89wQ==", + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "yargs-parser": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", + "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + } + } + }, + "yauzl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", + "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", + "dev": true, + "requires": { + "fd-slicer": "~1.0.1" + } + } + } +} diff --git a/screenshare/package.json b/screenshare/package.json new file mode 100644 index 0000000000..372679082f --- /dev/null +++ b/screenshare/package.json @@ -0,0 +1,27 @@ +{ + "name": "highfidelity_screenshare", + "version": "1.0.0", + "description": "High Fidelity Screenshare", + "main": "src/screenshareMainProcess.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "packager": "node packager.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/highfidelity/hifi.git" + }, + "author": "High Fidelity", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/highfidelity/hifi/issues" + }, + "homepage": "https://github.com/highfidelity/hifi#readme", + "devDependencies": { + "electron": "^6.0.12", + "electron-packager": "^14.0.6" + }, + "dependencies": { + "yargs": "^14.2.0" + } +} diff --git a/screenshare/packager.js b/screenshare/packager.js new file mode 100644 index 0000000000..7a0baebaf0 --- /dev/null +++ b/screenshare/packager.js @@ -0,0 +1,50 @@ +var packager = require('electron-packager'); +var osType = require('os').type(); +var argv = require('yargs').argv; + +var platform = null; +if (osType == "Darwin" || osType == "Linux") { + platform = osType.toLowerCase(); +} else if (osType == "Windows_NT") { + platform = "win32" +} + +var NAME = "hifi-screenshare"; +var options = { + dir: __dirname, + name: NAME, + version: "0.1.0", + overwrite: true, + prune: true, + arch: "x64", + platform: platform, + ignore: "electron-packager|README.md|CMakeLists.txt|packager.js|.gitignore" +}; + +// setup per OS options +if (osType == "Darwin") { + options["app-bundle-id"] = "com.highfidelity.hifi-screenshare"; +} else if (osType == "Windows_NT") { + options["version-string"] = { + CompanyName: "High Fidelity, Inc.", + FileDescription: "High Fidelity Screenshare", + ProductName: NAME, + OriginalFilename: NAME + ".exe" + } +} + +// check if we were passed a custom out directory, pass it along if so +if (argv.out) { + options.out = argv.out +} + +// call the packager to produce the executable +packager(options) + .then(appPath => { + console.log("Wrote new app to " + appPath); + }) + .catch(error => { + console.error("There was an error writing the packaged console: " + error.message); + process.exit(1); + }); + \ No newline at end of file diff --git a/screenshare/src/resources/Graphik-Regular.ttf b/screenshare/src/resources/Graphik-Regular.ttf new file mode 100644 index 0000000000..001faa7f47 Binary files /dev/null and b/screenshare/src/resources/Graphik-Regular.ttf differ diff --git a/screenshare/src/resources/interface.png b/screenshare/src/resources/interface.png new file mode 100644 index 0000000000..f90cbe591c Binary files /dev/null and b/screenshare/src/resources/interface.png differ diff --git a/screenshare/src/screenshareApp.html b/screenshare/src/screenshareApp.html new file mode 100644 index 0000000000..c647bd58a3 --- /dev/null +++ b/screenshare/src/screenshareApp.html @@ -0,0 +1,52 @@ + + + + + + + +
+

Share your screen

+

Please select the content you'd like to share.

+
+ +
+
+
+
+
+
+
+
+ + + + + diff --git a/screenshare/src/screenshareApp.js b/screenshare/src/screenshareApp.js new file mode 100644 index 0000000000..b687309ccb --- /dev/null +++ b/screenshare/src/screenshareApp.js @@ -0,0 +1,298 @@ +'use strict'; +// screenshareApp.js +// +// Created by Milad Nazeri, Rebecca Stankus, and Zach Fox 2019/11/13 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +// Helpers +function handleError(error) { + if (error) { + console.error(error); + } +} + + +// When an application is picked, make sure we clear out the previous pick, toggle the page, +// and add the correct source +let currentScreensharePickID = ""; +function screensharePicked(id) { + currentScreensharePickID = id; + document.getElementById("share_pick").innerHTML = ""; + togglePage(); + addSource(sourceMap[id], "share_pick"); +} + + +// Once we have confirmed that we want to share, prepare the tokbox publishing initiating +// and toggle back to the selects page +function screenConfirmed(isConfirmed) { + document.getElementById("selects").innerHTML = ""; + if (isConfirmed === true){ + onAccessApproved(currentScreensharePickID); + } + togglePage(); +} + + +// Hide/show the select page or the confirmation page +let currentPage = "mainPage"; +function togglePage(){ + if (currentPage === "mainPage") { + currentPage = "confirmationPage"; + document.getElementById("select_screen").style.display = "none"; + document.getElementById("subtitle").innerHTML = "Confirm that you'd like to share this content."; + document.getElementById("confirmation_screen").style.display = "block"; + } else { + showSources(); + currentPage = "mainPage"; + document.getElementById("select_screen").style.display = "block"; + document.getElementById("subtitle").innerHTML = "Please select the content you'd like to share."; + document.getElementById("confirmation_screen").style.display = "none"; + } +} + + +// UI + +// Render the html properly and append that to the correct parent +function addSource(source, type) { + let renderedHTML = renderSourceHTML(source); + if (type === "selects") { + document.getElementById("selects").appendChild(renderedHTML); + } else { + document.getElementById("share_pick").appendChild(renderedHTML); + document.getElementById("content_name").innerHTML = source.name; + } +} + + +// Get the html created from the source. Alter slightly depending on whether this source +// is on the selects screen, or the confirmation screen. Mainly removing highlighting. +// If there is an app Icon, then add it. +function renderSourceHTML(source) { + let type = currentPage === "confirmationPage" ? "share_pick" : "selects"; + let sourceBody = document.createElement('div') + let thumbnail = source.thumbnail.toDataURL(); + sourceBody.classList.add("box") + if (type === "share_pick") { + sourceBody.style.marginLeft = "0px"; + } + + let image = ""; + if (source.appIcon) { + image = ``; + } + sourceBody.innerHTML = ` +
+ ${image} + ${source.name} +
+
+ +
+ ` + return sourceBody; +} + + +// Separate out the screenshares and applications +// Make sure the screens are labeled in order +// Concact the two arrays back together and return +function sortSources() { + let screenSources = []; + let applicationSources = []; + // Difference with Mac selects: + // 1 screen = "Enitre Screen", more than one like PC "Screen 1, Screen 2..." + screenshareSourceArray.forEach((source) => { + if (source.name.match(/(entire )?screen( )?([0-9]?)/i)) { + screenSources.push(source); + } else { + applicationSources.push(source) + } + }); + screenSources.sort((a, b) => { + let aNumber = a.name.replace(/[^\d]/, ""); + let bNumber = b.name.replace(/[^\d]/, ""); + return aNumber - bNumber; + }); + let finalSources = [...screenSources, ...applicationSources]; + return finalSources; +} + + +// Setup sorting the selection array, add individual sources, and update the sourceMap +function addSources() { + screenshareSourceArray = sortSources(); + for (let i = 0; i < screenshareSourceArray.length; i++) { + addSource(screenshareSourceArray[i], "selects"); + sourceMap[screenshareSourceArray[i].id] = screenshareSourceArray[i]; + } +} + + +// 1. Get the screens and window that are available from electron +// 2. Remove the screenshare app itself +// 3. Create a source map to help grab the correct source when picked +// 4. push all the sources for sorting to the source array +// 5. Add thse sources +const electron = require('electron'); +const SCREENSHARE_TITLE = "Screen share"; +const SCREENSHARE_TITLE_REGEX = new RegExp("^" + SCREENSHARE_TITLE + "$"); +const IMAGE_WIDTH = 265; +const IMAGE_HEIGHT = 165; +let screenshareSourceArray = []; +let sourceMap = {}; +function showSources() { + screenshareSourceArray = []; + electron.desktopCapturer.getSources({ + types:['window', 'screen'], + thumbnailSize: { + width: IMAGE_WIDTH, + height: IMAGE_HEIGHT + }, + fetchWindowIcons: true + }, (error, sources) => { + if (error) { + console.log("Error getting sources", error); + } + for (let source of sources) { + if (source.name.match(SCREENSHARE_TITLE_REGEX)){ + continue; + } + sourceMap[source.id] = source; + screenshareSourceArray.push(source); + } + addSources(); + }); +} + + +// Stop the localstream and end the tokrok publishing +let localStream; +function stopSharing() { + desktopSharing = false; + + if (localStream) { + localStream.getTracks()[0].stop(); + localStream = null; + } + + document.getElementById('screenshare').style.display = "none"; + stopTokBoxPublisher(); +} + + +// Callback to start publishing after we have setup the chromium stream +function gotStream(stream) { + localStream = stream; + startTokboxPublisher(localStream); + + stream.onended = () => { + if (desktopSharing) { + togglePage(); + } + }; +} + + +// After we grant access to electron, create a stream and using the callback +// start the tokbox publisher +function onAccessApproved(desktop_id) { + if (!desktop_id) { + console.log('Desktop Capture access rejected.'); + return; + } + document.getElementById('screenshare').style.visibility = "block"; + desktopSharing = true; + navigator.webkitGetUserMedia({ + audio: false, + video: { + mandatory: { + chromeMediaSource: 'desktop', + chromeMediaSourceId: desktop_id, + minWidth: 1280, + maxWidth: 1280, + minHeight: 720, + maxHeight: 720 + } + } + }, gotStream, handleError); +} + + +// Tokbox + +// Once we have the connection info, this will create the session which will allow +// us to publish a stream when we are ready +function initializeTokboxSession() { + session = OT.initSession(projectAPIKey, sessionID); + session.on('sessionDisconnected', (event) => { + console.log('You were disconnected from the session.', event.reason); + }); + + // Connect to the session + session.connect(token, (error) => { + if (error) { + handleError(error); + } + }); +} + + +// Init the tokbox publisher with our newly created stream +var publisher; +function startTokboxPublisher(stream) { + publisher = document.createElement("div"); + var publisherOptions = { + videoSource: stream.getVideoTracks()[0], + audioSource: null, + insertMode: 'append', + width: 1280, + height: 720 + }; + + publisher = OT.initPublisher(publisher, publisherOptions, function(error){ + if (error) { + console.log("ERROR: " + error); + } else { + session.publish(publisher, function(error) { + if (error) { + console.log("ERROR FROM Session.publish: " + error); + return; + } + }) + } + }); +} + + +// Kills the streaming being sent to tokbox +function stopTokBoxPublisher() { + publisher.destroy(); +} + + +// When the app is ready, we get this info from the command line arguments. +const ipcRenderer = electron.ipcRenderer; +let projectAPIKey; +let sessionID; +let token; +let session; +ipcRenderer.on('connectionInfo', function(event, message) { + const connectionInfo = JSON.parse(message); + projectAPIKey = connectionInfo.projectAPIKey; + sessionID = connectionInfo.sessionID; + token = connectionInfo.token; + + initializeTokboxSession(); +}); + + +// Show the initial sources after the dom has loaded +// Sources come from electron.desktopCapturer +document.addEventListener("DOMContentLoaded", () => { + showSources(); +}); diff --git a/screenshare/src/screenshareMainProcess.js b/screenshare/src/screenshareMainProcess.js new file mode 100644 index 0000000000..5dce65a783 --- /dev/null +++ b/screenshare/src/screenshareMainProcess.js @@ -0,0 +1,74 @@ +'use strict'; +// screenshareMainProcess.js +// +// Milad Nazeri and Zach Fox 2019/11/13 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +const {app, BrowserWindow, ipcMain} = require('electron'); +const gotTheLock = app.requestSingleInstanceLock() +const argv = require('yargs').argv; + + +const connectionInfo = { + token: argv.token || "token", + projectAPIKey: argv.projectAPIKey || "projectAPIKey", + sessionID: argv.sessionID || "sessionID" +} + + +// Mac and PC need slightly different width and height sizes. +const osType = require('os').type(); +let width; +let height; +if (osType == "Darwin" || osType == "Linux") { + width = 960; + height = 660; +} else if (osType == "Windows_NT") { + width = 973; + height = 740; +} + + +if (!gotTheLock) { + console.log("Another instance of the screenshare is already running - this instance will quit."); + app.exit(0); + return; +} + +let window; +const zoomFactor = 1.0; +function createWindow(){ + window = new BrowserWindow({ + backgroundColor: "#000000", + width: width, + height: height, + center: true, + frame: true, + useContentSize: true, + zoomFactor: zoomFactor, + resizable: false, + webPreferences: { + nodeIntegration: true + }, + icon: __dirname + `/resources/interface.png`, + skipTaskbar: false, + title: "Screen share" + }); + window.loadURL('file://' + __dirname + '/screenshareApp.html'); + window.setMenu(null); + + window.webContents.on("did-finish-load", () => { + window.webContents.send('connectionInfo', JSON.stringify(connectionInfo)); + }); +} + + +// This method will be called when Electron has finished +// initialization and is ready to create browser windows. +app.on('ready', function() { + createWindow(); + window.webContents.send('connectionInfo', JSON.stringify(connectionInfo)) +}); diff --git a/screenshare/src/styles.css b/screenshare/src/styles.css new file mode 100644 index 0000000000..78dca032fa --- /dev/null +++ b/screenshare/src/styles.css @@ -0,0 +1,217 @@ +body { + background-color: black; + box-sizing: border-box; + font-family: "Graphik"; + margin: 0px 22px 10px 22px; + } + +#confirmation_screen { + width: 100%; + display: flex; + text-align: center; + justify-content: center; + align-items: center; +} + +#confirmation_text { + margin-top: 65px; + font-size: 25px; + line-height: 25px; +} + +#confirmation_text p { + margin: 0px; +} + +#button_selection { + margin-top: 25px; + width: 100%; + display: flex; + text-align: center; + justify-content: center; + align-items: center; + flex-direction: column; +} + +.button_confirmation { + margin: 4px; + cursor: pointer; + width: 300px; + height: 32px; + line-height: 32px; + text-align: center; + vertical-align: middle; + color: white +} + +.grey_background { + background-color: #191919; +} + +#no { + color: rgba(1, 152, 203,0.7); +} + +#no:hover { + color: rgba(1, 152, 203,1); +} + +#yes { + outline: solid white 2px; +} + +#yes:hover { + background: #0198CB; +} + +yes:hover + #yes_background { + display: block; +} + +#share_pick { + margin-top: 60px; +} + +.text_title { + color: white; +} + +@font-face { + font-family: "Graphik"; + src: url("./resources/Graphik-Regular.ttf"); +} + +#title { + margin-top: 21px; +} + +h1 { + line-height: 48px; + font-size: 48px; + margin: 0px; +} + +h3 { + line-height: 24px; + font-size: 24px; + margin: 9px 0px 0px 0px; +} + +#publisher { + visibility: hidden; + width: 0px; + height: 0px; + bottom: 10px; + left: 10px; + z-index: 100; + border: 3px solid white; + border-radius: 3px; +} + +#selects { + margin-right: 19px; + padding-left: 3px; +} + +.screen_label { + max-width: 220px; + font-size: 25px; + line-height: 25px; + color: white; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.icon { + display: inline-block; + background: #000000; + width: 20px; + height: 20px; + margin-right: 15px; +} + +.box { + height: 165px; + width: 265px; + display: inline-block; + margin-left: 35px; + margin-top: 60px; + cursor: pointer; +} + +.box:nth-child(1) { + margin-top: 0 !important; +} + +.box:nth-child(2) { + margin-top: 0 !important; +} + +.box:nth-child(3) { + margin-top: 0 !important; +} + +.box:nth-child(3n) { + margin-right: 0 !important; +} + +.box:nth-child(3n+1) { + margin-left: 0 !important; +} + +.heading { + height: 35px; + display: flex; + align-items: center; +} + +.image { + width: 265px; + height: 165px; + max-height: 165px; + max-width: 265px; + margin: 0px; +} + +.image:hover { + outline: solid white 3px; +} + +.image_no_hover { + width: 265px; + height: 165px; + max-height: 165px; + max-width: 265px; +} + +img { + width: 265px; + height: 165px; + margin: 0px; +} + +.scrollbar { + float: right; + height: 500px; + width: 100%; + overflow-y: scroll; + margin-top: 40px; +} + +#style-1::-webkit-scrollbar { + width: 9px; + overflow: scroll; + overflow-x: hidden; +} + +#style-1::-webkit-scrollbar-thumb { + background-color: #0198CB; + width: 9px; +} + +#style-1::-webkit-scrollbar-track { + -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3); + background-color: #848484; + width: 9px; +} diff --git a/scripts/simplifiedUI/clickToZoom/clickToZoom.js b/scripts/simplifiedUI/clickToZoom/clickToZoom.js new file mode 100644 index 0000000000..709df406b6 --- /dev/null +++ b/scripts/simplifiedUI/clickToZoom/clickToZoom.js @@ -0,0 +1,428 @@ +// +// Created by Luis Cuenca on 11/14/19 +// Copyright 2019 High Fidelity, Inc. +// +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + var ZoomStatus = { + "zoomingIn" : 0, + "zoomingOut" : 1, + "zoomedIn" : 2, + "zoomedOut" : 4, + "consumed" : 5 + } + + var FocusType = { + "avatar" : 0, + "entity" : 1 + } + + var EasingFunctions = { + easeInOutQuad: function (t) { return t<.5 ? 2*t*t : -1+(4-2*t)*t }, + // accelerating from zero velocity + easeInCubic: function (t) { return t*t*t }, + // decelerating to zero velocity + easeOutCubic: function (t) { return (--t)*t*t+1 }, + // acceleration until halfway, then deceleration + easeInOutCubic: function (t) { return t<.5 ? 4*t*t*t : (t-1)*(2*t-2)*(2*t-2)+1 }, + // accelerating from zero velocity + easeInQuart: function (t) { return t*t*t*t }, + // decelerating to zero velocity + easeOutQuart: function (t) { return 1-(--t)*t*t*t }, + // acceleration until halfway, then deceleration + easeInOutQuart: function (t) { return t<.5 ? 8*t*t*t*t : 1-8*(--t)*t*t*t }, + // accelerating from zero velocity + easeInQuint: function (t) { return t*t*t*t*t }, + // decelerating to zero velocity + easeOutQuint: function (t) { return 1+(--t)*t*t*t*t }, + // acceleration until halfway, then deceleration + easeInOutQuint: function (t) { return t<.5 ? 16*t*t*t*t*t : 1+16*(--t)*t*t*t*t } + }; + + var ZoomData = function(type, lookAt, focusNormal, focusDimensions, velocity, maxDuration) { + var self = this; + + this.focusType = type; + this.lookAt = lookAt; + this.focusDimensions = focusDimensions; + this.focusNormal = focusNormal; + this.velocity = velocity; + this.maxDuration = maxDuration; + + this.initialPos = Camera.getPosition(); + this.initialRot = Camera.getOrientation(); + this.interpolatedPos = this.initialPos; + this.interpolatedRot = this.initialRot; + this.initialMode = Camera.mode; + this.initialOffset = Vec3.distance(self.initialPos, MyAvatar.getDefaultEyePosition()); + + this.finalPos = Vec3.ZERO; + this.finalRot = Quat.IDENTITY; + this.direction = Vec3.ZERO; + this.distance = Vec3.ZERO; + this.totalTime = 0.0; + this.elapsedTime = 0.0; + this.maxZoomInAmount = 0.6; + this.maxZoomOutAmount = 0.2; + this.currentZoomAmount = 0.0; + + this.zoomPanOffset = {x: 0.5 * Window.innerWidth, y: 0.5 * Window.innerHeight}; + this.zoomPanDelta = {x: 0.0, y: 0.0}; + + this.status = ZoomStatus.zoomedOut; + + var OWN_CAMERA_CHANGE_MAX_FRAMES = 30; + this.ownCameraChangeElapseTime = 0.0; + this.ownCameraChange = false; + + this.applyZoomPan = function() { + self.zoomPanOffset.x += self.zoomPanDelta.x; + self.zoomPanOffset.y += self.zoomPanDelta.y; + } + + this.setZoomPanDelta = function(x, y) { + self.zoomPanDelta.x = x; + self.zoomPanDelta.y = y; + var totalX = self.zoomPanOffset.x + self.zoomPanDelta.x; + var totalY = self.zoomPanOffset.y + self.zoomPanDelta.y; + totalX = Math.min(Math.max(totalX, 0.0), Window.innerWidth); + totalY = Math.min(Math.max(totalY, 0.0), Window.innerHeight); + self.zoomPanDelta.x = totalX - self.zoomPanOffset.x; + self.zoomPanDelta.y = totalY - self.zoomPanOffset.y; + self.updateSuperPan(totalX, totalY); + } + + this.getFocusDistance = function(zoomDims) { + var objAspect = zoomDims.x / zoomDims.y; + var camAspect = Camera.frustum.aspectRatio; + var m = 0.0; + if (objAspect < camAspect) { + m = zoomDims.y; + } else { + m = zoomDims.x / camAspect; + } + var DEGREE_TO_RADIAN = 0.0174533; + var fov = DEGREE_TO_RADIAN * Camera.frustum.fieldOfView; + return (0.5 * m) / Math.tan(0.5 * fov); + } + + this.finalPos = Vec3.sum(this.lookAt, Vec3.multiply(this.getFocusDistance(this.focusDimensions), this.focusNormal)); + this.finalRot = Quat.lookAtSimple(this.finalPos, this.lookAt); + + this.computeRouteIn = function() { + var railVector = Vec3.subtract(self.finalPos, self.initialPos); + self.direction = Vec3.normalize(railVector); + self.distance = Vec3.length(railVector); + self.totalTime = self.distance / self.velocity; + self.totalTime = self.totalTime > self.maxDuration ? self.maxDuration : self.totalTime; + } + + this.computeRouteOut = function() { + self.finalPos = Camera.getPosition(); + var camOffset = Vec3.ZERO; + var myAvatarRotation = MyAvatar.orientation; + if (self.initialMode.indexOf("first") != -1) { + self.initialRot = myAvatarRotation; + } else { + var lookAtPoint = MyAvatar.getDefaultEyePosition(); + var lookFromSign = self.initialMode.indexOf("selfie") != -1 ? 1 : -1; + var lookFromPoint = Vec3.sum(lookAtPoint, Vec3.multiply(self.initialOffset * lookFromSign, Quat.getFront(myAvatarRotation))); + self.initialRot = Quat.lookAtSimple(lookFromPoint, lookAtPoint); + self.initialPos = lookFromPoint; + } + self.computeRouteIn(); + } + + this.initZoomIn = function() { + if (self.status === ZoomStatus.zoomedOut) { + self.computeRouteIn(); + self.status = ZoomStatus.zoomingIn; + self.changeCameraMode("independent"); + self.elapsedTime = 0.0; + } + } + + this.initZoomOut = function() { + if (self.status === ZoomStatus.zoomedIn) { + self.computeRouteOut(); + self.status = ZoomStatus.zoomingOut; + self.changeCameraMode("independent"); + self.elapsedTime = 0.0; + } + } + + this.needsUpdate = function() { + return self.status === ZoomStatus.zoomingIn || self.status === ZoomStatus.zoomingOut; + } + + this.updateZoom = function(deltaTime) { + if (self.ownCameraChange) { + self.ownCameraChange = self.ownCameraChangeElapseTime < OWN_CAMERA_CHANGE_MAX_FRAMES * deltaTime; + self.ownCameraChangeElapseTime += deltaTime; + } + if (!self.needsUpdate) { + return; + } + if (self.elapsedTime < self.totalTime) { + var ratio = EasingFunctions.easeInOutQuart(self.elapsedTime / self.totalTime); + + if (self.status === ZoomStatus.zoomingIn) { + var curDist = self.distance * ratio; + var addition = Vec3.multiply(curDist, self.direction); + self.interpolatedPos = Vec3.sum(self.initialPos, addition); + self.interpolatedRot = Quat.mix(self.initialRot, self.finalRot, ratio); + } else if (self.status === ZoomStatus.zoomingOut) { + self.interpolatedPos = Vec3.sum(self.finalPos, Vec3.multiply(self.distance * ratio, Vec3.multiply(-1, self.direction))); + self.interpolatedRot = Quat.mix(self.finalRot, self.initialRot, ratio); + } + self.elapsedTime += deltaTime; + Camera.setPosition(self.interpolatedPos); + Camera.setOrientation(self.interpolatedRot); + } else { + Camera.setPosition(self.finalPos); + Camera.setOrientation(self.finalRot); + if (self.status === ZoomStatus.zoomingIn) { + self.status = ZoomStatus.zoomedIn; + } else if (self.status === ZoomStatus.zoomingOut) { + self.status = ZoomStatus.consumed; + self.changeCameraMode(self.initialMode); + } + } + } + + this.resetZoomAspect = function() { + self.computeRouteIn(); + Camera.setPosition(self.finalPos); + } + + this.updateSuperZoom = function(delta) { + var ZOOM_STEP = 0.1; + self.currentZoomAmount = self.currentZoomAmount + (delta < 0.0 ? -1 : 1) * ZOOM_STEP; + self.currentZoomAmount = Math.min(Math.max(self.currentZoomAmount, -self.maxZoomOutAmount), self.maxZoomInAmount); + self.updateSuperPan(self.zoomPanOffset.x, self.zoomPanOffset.y); + } + + this.updateSuperPan = function(x, y) { + var zoomOffset = Vec3.multiply(self.currentZoomAmount, Vec3.subtract(self.lookAt, self.finalPos)); + var xRatio = 0.5 - x / Window.innerWidth; + var yRatio = 0.5 - y / Window.innerHeight; + var cameraOrientation = Camera.getOrientation(); + var cameraY = Quat.getUp(cameraOrientation); + var cameraX = Vec3.multiply(-1, Quat.getRight(cameraOrientation)); + var xOffset = Vec3.multiply(xRatio * self.focusDimensions.x, cameraX); + var yOffset = Vec3.multiply(yRatio * self.focusDimensions.y, cameraY); + zoomOffset = Vec3.sum(zoomOffset, xOffset); + zoomOffset = Vec3.sum(zoomOffset, yOffset); + Camera.setPosition(Vec3.sum(self.finalPos, zoomOffset)); + } + + this.abort = function() { + self.changeCameraMode(self.initialMode); + } + + this.changeCameraMode = function(mode) { + self.ownCameraChange = true; + self.ownCameraChangeElapseTime = 0.0; + Camera.mode = mode; + } + } + + var ZoomOnAnything = function() { + var self = this; + this.zoomEntityID; + this.zoomEntityData; + this.zoomCameraPos; + var ZOOM_MAX_VELOCITY = 15.0; // meters per second + var ZOOM_MAX_DURATION = 1.0; + this.zoomDelta = {x: 0.0, y: 0.0}; + this.isPanning = false; + this.screenPointRef = {x: 0.0, y: 0.0}; + + this.getEntityDimsFromNormal = function(dims, rot, normal) { + var zoomXNormal = Vec3.multiplyQbyV(rot, Vec3.UNIT_X); + var zoomYNormal = Vec3.multiplyQbyV(rot, Vec3.UNIT_Y); + var zoomZNormal = Vec3.multiplyQbyV(rot, Vec3.UNIT_Z); + var affinities = [ + {axis: "x", normal: zoomXNormal, affin: Math.abs(Vec3.dot(zoomXNormal, normal)), dims: {x: dims.z, y: dims.y}}, + {axis: "y", normal: zoomYNormal, affin: Math.abs(Vec3.dot(zoomYNormal, normal)), dims: {x: dims.x, y: dims.z}}, + {axis: "z", normal: zoomZNormal, affin: Math.abs(Vec3.dot(zoomZNormal, normal)), dims: {x: dims.x, y: dims.y}} + ]; + affinities.sort(function(a, b) { + return b.affin - a.affin; + }); + return affinities[0]; + } + + this.getAvatarFocusPoint = function(avatar) { + var rEyeIndex = avatar.getJointIndex("RightEye"); + var lEyeIndex = avatar.getJointIndex("LeftEye"); + var headIndex = avatar.getJointIndex("Head"); + var focusPoint = Vec3.ZERO; + var validPoint = false; + var count = 0; + if (rEyeIndex != -1) { + focusPoint = Vec3.sum(focusPoint, avatar.getJointPosition(rEyeIndex)); + validPoint = true; + count++; + } + if (lEyeIndex != -1) { + var leftEyePos = avatar.getJointPosition(lEyeIndex); + var NORMAL_EYE_DISTANCE = 0.1; + focusPoint = Vec3.sum(focusPoint, leftEyePos); + validPoint = true; + count++; + } + if (headIndex != -1) { + focusPoint = Vec3.sum(focusPoint, avatar.getJointPosition(headIndex)); + validPoint = true; + count++; + } + if (!validPoint) { + focusPoint = avatar.getJointPosition("Hips"); + count++; + } + return Vec3.multiply(1.0/count, focusPoint); + } + + this.getZoomDataFromAvatar = function(avatarID, skinToBoneDist, zoomVelocity, maxDuration) { + var headDiam = 2.0 * skinToBoneDist; + headDiam = headDiam < 0.5 ? 0.5 : headDiam; + var avatar = AvatarList.getAvatar(avatarID); + var focusPoint = self.getAvatarFocusPoint(avatar); + var focusDims = {x: headDiam, y: headDiam}; + var focusNormal = Quat.getFront(avatar.orientation); + var zoomData = new ZoomData(FocusType.avatar, focusPoint, focusNormal, focusDims, zoomVelocity, maxDuration); + return zoomData; + } + + this.getZoomDataFromEntity = function(intersection, objectProps, zoomVelocity, maxDuration) { + var position = objectProps.position; + var dimensions = objectProps.dimensions; + var rotation = objectProps.rotation; + var focusNormal = intersection.surfaceNormal; + var dimsResult = self.getEntityDimsFromNormal(dimensions, rotation, focusNormal); + var focusDims = dimsResult.dims; + var focusDepth = Vec3.dot(Vec3.subtract(intersection.intersection, position), dimsResult.normal); + var newPosition = Vec3.sum(position, Vec3.multiply(focusDepth, dimsResult.normal)); + var zoomData = new ZoomData(FocusType.entity, newPosition, focusNormal, focusDims, zoomVelocity, maxDuration); + return zoomData; + } + + this.zoomOnEntity = function(intersection, objectProps) { + self.zoomEntityData = self.getZoomDataFromEntity(intersection, objectProps, ZOOM_MAX_VELOCITY, ZOOM_MAX_DURATION); + self.zoomEntityData.initZoomIn(); + } + + this.zoomOnAvatar = function(avatarID, skinToBoneDist) { + self.zoomEntityData = self.getZoomDataFromAvatar(avatarID, skinToBoneDist, ZOOM_MAX_VELOCITY, ZOOM_MAX_DURATION); + self.zoomEntityData.initZoomIn(); + } + + this.updateZoom = function(deltaTime) { + if (self.zoomEntityData && self.zoomEntityData.needsUpdate()) { + self.zoomEntityData.updateZoom(deltaTime); + if (self.zoomEntityData.status === ZoomStatus.consumed) { + self.zoomEntityData = undefined; + } + } + } + + this.mouseDoublePressEvent = function(event) { + if (event.isLeftButton) { + if (!self.zoomEntityData) { + var pickRay = Camera.computePickRay(event.x, event.y); + var intersection = AvatarManager.findRayIntersection({origin: pickRay.origin, direction: pickRay.direction}, [], [MyAvatar.sessionUUID], false); + zoomingAtAvatarID = intersection.intersects ? intersection.avatarID : undefined; + if (!zoomingAtAvatarID) { + intersection = Entities.findRayIntersection({origin: pickRay.origin, direction: pickRay.direction}, true); + self.zoomEntityID = intersection.entityID; + var entityProps = Entities.getEntityProperties(intersection.entityID); + if (entityProps.type === "Shape") { + var FIND_SHAPES_DISTANCE = 10.0; + var shapes = Entities.findEntitiesByType("Shape", intersection.intersection, FIND_SHAPES_DISTANCE); + intersection = Entities.findRayIntersection({origin: pickRay.origin, direction: pickRay.direction}, true, [], shapes); + self.zoomEntityID = intersection.entityID; + entityProps = Entities.getEntityProperties(intersection.entityID); + } + if (!entityProps.dimensions) { + return; + } + self.zoomOnEntity(intersection, entityProps); + } else { + var avatar = AvatarList.getAvatar(zoomingAtAvatarID); + var skinToBoneDist = Vec3.distance(intersection.intersection, avatar.getJointPosition(intersection.jointIndex)); + self.zoomOnAvatar(zoomingAtAvatarID, skinToBoneDist); + } + } else if (!self.zoomEntityData.needsUpdate()){ + self.zoomEntityData.initZoomOut(); + } + } + } + + this.mousePressEvent = function(event) { + if (event.isRightButton) { + self.isPanning = true; + self.screenPointRef = {x: event.x, y: event.y}; + } + } + + this.mouseReleaseEvent = function(event) { + if (event.isRightButton) { + if (self.zoomEntityData) { + self.zoomEntityData.applyZoomPan(); + self.isPanning = false; + self.screenPointRef = {x: 0, y: 0}; + } + } + } + + this.mouseMoveEvent = function(event) { + if (event.isRightButton) { + if (self.isPanning && self.zoomEntityData) { + self.zoomEntityData.setZoomPanDelta(event.x - self.screenPointRef.x, event.y - self.screenPointRef.y); + } + } + } + + this.mouseWheel = function(event) { + if (self.zoomEntityData) { + self.zoomEntityData.updateSuperZoom(event.delta); + } + } + + this.abort = function() { + self.zoomEntityData.abort(); + self.zoomEntityData = undefined; + } + } + + var zoomOE = new ZoomOnAnything(); + + Window.geometryChanged.connect(function() { + if (zoomOE.zoomEntityData){ + zoomOE.zoomEntityData.resetZoomAspect(); + } + }); + + Camera.modeUpdated.connect(function(mode) { + if (zoomOE.zoomEntityData && !zoomOE.zoomEntityData.ownCameraChange) { + zoomOE.abort(); + } + }); + + Controller.mousePressEvent.connect(zoomOE.mousePressEvent); + Controller.mouseDoublePressEvent.connect(zoomOE.mouseDoublePressEvent); + Controller.mouseMoveEvent.connect(zoomOE.mouseMoveEvent); + Controller.mouseReleaseEvent.connect(zoomOE.mouseReleaseEvent); + Controller.wheelEvent.connect(zoomOE.mouseWheel); + Script.update.connect(zoomOE.updateZoom); + Script.scriptEnding.connect(function() { + if (zoomOE.zoomEntityData) { + zoomOE.abort(); + } + }); +})(); \ No newline at end of file diff --git a/scripts/simplifiedUIBootstrapper.js b/scripts/simplifiedUIBootstrapper.js index 88bba109ed..985485c4f9 100644 --- a/scripts/simplifiedUIBootstrapper.js +++ b/scripts/simplifiedUIBootstrapper.js @@ -17,7 +17,8 @@ var currentlyRunningScripts = ScriptDiscoveryService.getRunning(); var DEFAULT_SCRIPTS_SEPARATE = [ "system/controllers/controllerScripts.js", - "simplifiedUI/ui/simplifiedUI.js" + "simplifiedUI/ui/simplifiedUI.js", + "simplifiedUI/clickToZoom/clickToZoom.js" ]; function loadSeparateDefaults() { for (var i = 0; i < DEFAULT_SCRIPTS_SEPARATE.length; i++) {