diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 000000000..c14909783 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,202 @@ +version: 2 + +workflows: + version: 2 + full_test: + jobs: + - prep-deps-npm + - prep-deps-firefox + - prep-scss: + requires: + - prep-deps-npm + - test-lint: + requires: + - prep-deps-npm + - test-unit: + requires: + - prep-deps-npm + - test-integration-mascara-chrome: + requires: + - prep-deps-npm + - prep-scss + - test-integration-mascara-firefox: + requires: + - prep-deps-npm + - prep-deps-firefox + - prep-scss + - test-integration-flat-chrome: + requires: + - prep-deps-npm + - prep-scss + - test-integration-flat-firefox: + requires: + - prep-deps-npm + - prep-deps-firefox + - prep-scss + +jobs: + prep-deps-npm: + docker: + - image: circleci/node:8-browsers + steps: + - checkout + - restore_cache: + key: dependency-cache-{{ checksum "package-lock.json" }} + - run: + name: Install deps via npm + command: npm install + - save_cache: + key: dependency-cache-{{ checksum "package-lock.json" }} + paths: + - node_modules + + prep-deps-firefox: + docker: + - image: circleci/node:8-browsers + steps: + - checkout + - run: + name: Download Firefox + command: > + wget https://ftp.mozilla.org/pub/firefox/releases/58.0/linux-x86_64/en-US/firefox-58.0.tar.bz2 + && tar xjf firefox-58.0.tar.bz2 + - save_cache: + key: dependency-cache-firefox-{{ .Revision }} + paths: + - firefox + + + prep-scss: + docker: + - image: circleci/node:8-browsers + steps: + - checkout + - restore_cache: + key: dependency-cache-{{ checksum "package-lock.json" }} + - run: + name: Get Scss Cache key + # this allows us to checksum against a whole directory + command: find ui/app/css -type f -exec md5sum {} \; | sort -k 2 > scss_checksum + - run: + name: Build for integration tests + command: npm run test:integration:build + - save_cache: + key: scss-cache-{{ checksum "scss_checksum" }} + paths: + - ui/app/css/output + + test-lint: + docker: + - image: circleci/node:8-browsers + steps: + - checkout + - restore_cache: + key: dependency-cache-{{ checksum "package-lock.json" }} + - run: + name: Test + command: npm run lint + + test-unit: + docker: + - image: circleci/node:8-browsers + steps: + - checkout + - restore_cache: + key: dependency-cache-{{ checksum "package-lock.json" }} + - run: + name: test:coverage + command: npm run test:coverage + + test-integration-flat-firefox: + environment: + browsers: '["Firefox"]' + docker: + - image: circleci/node:8-browsers + steps: + - checkout + - restore_cache: + key: dependency-cache-firefox-{{ .Revision }} + - run: + name: Install firefox + command: > + sudo rm -r /opt/firefox + && sudo mv firefox /opt/firefox58 + && sudo mv /usr/bin/firefox /usr/bin/firefox-old + && sudo ln -s /opt/firefox58/firefox /usr/bin/firefox + - restore_cache: + key: dependency-cache-{{ checksum "package-lock.json" }} + - run: + name: Get Scss Cache key + # this allows us to checksum against a whole directory + command: find ui/app/css -type f -exec md5sum {} \; | sort -k 2 > scss_checksum + - restore_cache: + key: scss-cache-{{ checksum "scss_checksum" }} + - run: + name: test:integration:flat + command: npm run test:flat + + test-integration-flat-chrome: + environment: + browsers: '["Chrome"]' + docker: + - image: circleci/node:8-browsers + steps: + - checkout + - restore_cache: + key: dependency-cache-{{ checksum "package-lock.json" }} + - run: + name: Get Scss Cache key + # this allows us to checksum against a whole directory + command: find ui/app/css -type f -exec md5sum {} \; | sort -k 2 > scss_checksum + - restore_cache: + key: scss-cache-{{ checksum "scss_checksum" }} + - run: + name: test:integration:flat + command: npm run test:flat + + test-integration-mascara-firefox: + environment: + browsers: '["Firefox"]' + docker: + - image: circleci/node:8-browsers + steps: + - checkout + - restore_cache: + key: dependency-cache-firefox-{{ .Revision }} + - run: + name: Install firefox + command: > + sudo rm -r /opt/firefox + && sudo mv firefox /opt/firefox58 + && sudo mv /usr/bin/firefox /usr/bin/firefox-old + && sudo ln -s /opt/firefox58/firefox /usr/bin/firefox + - restore_cache: + key: dependency-cache-{{ checksum "package-lock.json" }} + - run: + name: Get Scss Cache key + # this allows us to checksum against a whole directory + command: find ui/app/css -type f -exec md5sum {} \; | sort -k 2 > scss_checksum + - restore_cache: + key: scss-cache-{{ checksum "scss_checksum" }} + - run: + name: test:integration:mascara + command: npm run test:mascara + + test-integration-mascara-chrome: + environment: + browsers: '["Chrome"]' + docker: + - image: circleci/node:8-browsers + steps: + - checkout + - restore_cache: + key: dependency-cache-{{ checksum "package-lock.json" }} + - run: + name: Get Scss Cache key + # this allows us to checksum against a whole directory + command: find ui/app/css -type f -exec md5sum {} \; | sort -k 2 > scss_checksum + - restore_cache: + key: scss-cache-{{ checksum "scss_checksum" }} + - run: + name: test:integration:mascara + command: npm run test:mascara diff --git a/.gitignore b/.gitignore index 5f2d2f551..2bf38cad4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ npm-debug.log node_modules +yarn.lock app/bower_components test/bower_components diff --git a/.nsprc b/.nsprc new file mode 100644 index 000000000..a9abc1e1e --- /dev/null +++ b/.nsprc @@ -0,0 +1,3 @@ +{ + "exceptions": ["https://nodesecurity.io/advisories/566"] +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 177e08bdd..e7d4a09fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,17 @@ # Changelog ## Current Master + +- Add ability for internationalization. +- Will now throw an error if the `to` field in txParams is not valid. +- Will strip null values from the `to` field. - Fix flashing to Log in screen after logging in or restoring from seed phrase. +- Increase tap areas for menu buttons on mobile +- Change all fonts in new-ui onboarding to Roboto, size 400 +- Add a welcome screen to new-ui onboarding flow +- Make new-ui create password screen responsive +- Hide network dropdown before account is initialized +- Fix bug that could prevent MetaMask from saving the latest vault. ## 4.2.0 Tue Mar 06 2018 diff --git a/README.md b/README.md index d45b73778..278357c23 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,7 @@ To write tests that will be run in the browser using QUnit, add your test files - [How to add custom build to Chrome](./docs/add-to-chrome.md) - [How to add custom build to Firefox](./docs/add-to-firefox.md) - [How to develop a live-reloading UI](./docs/ui-dev-mode.md) +- [How to add a new translation to MetaMask](./docs/translating-guide.md) - [Publishing Guide](./docs/publishing.md) - [How to develop an in-browser mocked UI](./docs/ui-mock-mode.md) - [How to live reload on local dependency changes](./docs/developing-on-deps.md) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 8c28f1c43..1ca31427d 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -1,10 +1,609 @@ { + "accept": { + "message": "Accept" + }, + "account": { + "message": "Account" + }, + "accountDetails": { + "message": "Account Details" + }, + "accountName": { + "message": "Account Name" + }, + "address": { + "message": "Address" + }, + "addToken": { + "message": "Add Token" + }, + "amount": { + "message": "Amount" + }, + "amountPlusGas": { + "message": "Amount + Gas" + }, + "appDescription": { + "message": "Ethereum Browser Extension", + "description": "The description of the application" + }, "appName": { "message": "MetaMask", "description": "The name of the application" }, - "appDescription": { - "message": "Ethereum Identity Management", - "description": "The description of the application" + "attemptingConnect": { + "message": "Attempting to connect to blockchain." + }, + "available": { + "message": "Available" + }, + "back": { + "message": "Back" + }, + "balance": { + "message": "Balance:" + }, + "balanceIsInsufficientGas": { + "message": "Insufficient balance for current gas total" + }, + "beta": { + "message": "BETA" + }, + "betweenMinAndMax": { + "message": "must be greater than or equal to $1 and less than or equal to $2.", + "description": "helper for inputting hex as decimal input" + }, + "borrowDharma": { + "message": "Borrow With Dharma (Beta)" + }, + "buy": { + "message": "Buy" + }, + "buyCoinbase": { + "message": "Buy on Coinbase" + }, + "buyCoinbaseExplainer": { + "message": "Coinbase is the world’s most popular way to buy and sell bitcoin, ethereum, and litecoin." + }, + "cancel": { + "message": "Cancel" + }, + "clickCopy": { + "message": "Click to Copy" + }, + "confirm": { + "message": "Confirm" + }, + "confirmContract": { + "message": "Confirm Contract" + }, + "confirmPassword": { + "message": "Confirm Password" + }, + "confirmTransaction": { + "message": "Confirm Transaction" + }, + "continueToCoinbase": { + "message": "Continue to Coinbase" + }, + "contractDeployment": { + "message": "Contract Deployment" + }, + "conversionProgress": { + "message": "Conversion in progress" + }, + "copiedButton": { + "message": "Copied" + }, + "copiedClipboard": { + "message": "Copied to Clipboard" + }, + "copiedExclamation": { + "message": "Copied!" + }, + "copy": { + "message": "Copy" + }, + "copyToClipboard": { + "message": "Copy to clipboard" + }, + "copyButton": { + "message": " Copy " + }, + "copyPrivateKey": { + "message": "This is your private key (click to copy)" + }, + "create": { + "message": "Create" + }, + "createAccount": { + "message": "Create Account" + }, + "createDen": { + "message": "Create" + }, + "crypto": { + "message": "Crypto", + "description": "Exchange type (cryptocurrencies)" + }, + "customGas": { + "message": "Customize Gas" + }, + "customize": { + "message": "Customize" + }, + "customRPC": { + "message": "Custom RPC" + }, + "defaultNetwork": { + "message": "The default network for Ether transactions is Main Net." + }, + "denExplainer": { + "message": "Your DEN is your password-encrypted storage within MetaMask." + }, + "deposit": { + "message": "Deposit" + }, + "depositBTC": { + "message": "Deposit your BTC to the address below:" + }, + "depositCoin": { + "message": "Deposit your $1 to the address below", + "description": "Tells the user what coin they have selected to deposit with shapeshift" + }, + "depositEth": { + "message": "Deposit Eth" + }, + "depositEther": { + "message": "Deposit Ether" + }, + "depositFiat": { + "message": "Deposit with Fiat" + }, + "depositFromAccount": { + "message": "Deposit from another account" + }, + "depositShapeShift": { + "message": "Deposit with ShapeShift" + }, + "depositShapeShiftExplainer": { + "message": "If you own other cryptocurrencies, you can trade and deposit Ether directly into your MetaMask wallet. No Account Needed." + }, + "details": { + "message": "Details" + }, + "directDeposit": { + "message": "Direct Deposit" + }, + "directDepositEther": { + "message": "Directly Deposit Ether" + }, + "directDepositEtherExplainer": { + "message": "If you already have some Ether, the quickest way to get Ether in your new wallet by direct deposit." + }, + "done": { + "message": "Done" + }, + "edit": { + "message": "Edit" + }, + "editAccountName": { + "message": "Edit Account Name" + }, + "encryptNewDen": { + "message": "Encrypt your new DEN" + }, + "enterPassword": { + "message": "Enter password" + }, + "etherscanView": { + "message": "View account on Etherscan" + }, + "exchangeRate": { + "message": "Exchange Rate" + }, + "exportPrivateKey": { + "message": "Export Private Key" + }, + "exportPrivateKeyWarning": { + "message": "Export private keys at your own risk." + }, + "failed": { + "message": "Failed" + }, + "fiat": { + "message": "FIAT", + "description": "Exchange type" + }, + "fileImportFail": { + "message": "File import not working? Click here!", + "description": "Helps user import their account from a JSON file" + }, + "from": { + "message": "From" + }, + "fromShapeShift": { + "message": "From ShapeShift" + }, + "gas": { + "message": "Gas", + "description": "Short indication of gas cost" + }, + "gasFee": { + "message": "Gas Fee" + }, + "gasLimit": { + "message": "Gas Limit" + }, + "gasLimitCalculation": { + "message": "We calculate the suggested gas limit based on network success rates." + }, + "gasLimitRequired": { + "message": "Gas Limit Required" + }, + "gasLimitTooLow": { + "message": "Gas limit must be at least 21000" + }, + "gasPrice": { + "message": "Gas Price (GWEI)" + }, + "gasPriceCalculation": { + "message": "We calculate the suggested gas prices based on network success rates." + }, + "gasPriceRequired": { + "message": "Gas Price Required" + }, + "getEther": { + "message": "Get Ether" + }, + "getEtherFromFaucet": { + "message": "Get Ether from a faucet for the $1", + "description": "Displays network name for Ether faucet" + }, + "greaterThanMin": { + "message": "must be greater than or equal to $1.", + "description": "helper for inputting hex as decimal input" + }, + "here": { + "message": "here", + "description": "as in -click here- for more information (goes with troubleTokenBalances)" + }, + "hide": { + "message": "Hide" + }, + "hideToken": { + "message": "Hide Token" + }, + "hideTokenPrompt": { + "message": "Hide Token?" + }, + "howToDeposit": { + "message": "How would you like to deposit Ether?" + }, + "import": { + "message": "Import", + "description": "Button to import an account from a selected file" + }, + "importAccount": { + "message": "Import Account" + }, + "importAnAccount": { + "message": "Import an account" + }, + "importDen": { + "message": "Import Existing DEN" + }, + "imported": { + "message": "Imported", + "description": "status showing that an account has been fully loaded into the keyring" + }, + "infoHelp": { + "message": "Info & Help" + }, + "invalidAddress": { + "message": "Invalid address" + }, + "invalidGasParams": { + "message": "Invalid Gas Parameters" + }, + "invalidInput": { + "message": "Invalid input." + }, + "invalidRequest": { + "message": "Invalid Request" + }, + "jsonFile": { + "message": "JSON File", + "description": "format for importing an account" + }, + "kovan": { + "message": "Kovan Test Network" + }, + "lessThanMax": { + "message": "must be less than or equal to $1.", + "description": "helper for inputting hex as decimal input" + }, + "limit": { + "message": "Limit" + }, + "loading": { + "message": "Loading..." + }, + "loadingTokens": { + "message": "Loading Tokens..." + }, + "localhost": { + "message": "Localhost 8545" + }, + "logout": { + "message": "Log out" + }, + "loose": { + "message": "Loose" + }, + "mainnet": { + "message": "Main Ethereum Network" + }, + "message": { + "message": "Message" + }, + "min": { + "message": "Minimum" + }, + "myAccounts": { + "message": "My Accounts" + }, + "needEtherInWallet": { + "message": "To interact with decentralized applications using MetaMask, you’ll need Ether in your wallet." + }, + "needImportFile": { + "message": "You must select a file to import.", + "description": "User is important an account and needs to add a file to continue" + }, + "needImportPassword": { + "message": "You must enter a password for the selected file.", + "description": "Password and file needed to import an account" + }, + "networks": { + "message": "Networks" + }, + "newAccount": { + "message": "New Account" + }, + "newAccountNumberName": { + "message": "Account $1", + "description": "Default name of next account to be created on create account screen" + }, + "newContract": { + "message": "New Contract" + }, + "newPassword": { + "message": "New Password (min 8 chars)" + }, + "newRecipient": { + "message": "New Recipient" + }, + "next": { + "message": "Next" + }, + "noAddressForName": { + "message": "No address has been set for this name." + }, + "noDeposits": { + "message": "No deposits received" + }, + "noTransactionHistory": { + "message": "No transaction history." + }, + "noTransactions": { + "message": "No Transactions" + }, + "notStarted": { + "message": "Not Started" + }, + "oldUI": { + "message": "Old UI" + }, + "oldUIMessage": { + "message": "You have returned to the old UI. You can switch back to the New UI through the option in the top right dropdown menu." + }, + "or": { + "message": "or", + "description": "choice between creating or importing a new account" + }, + "passwordMismatch": { + "message": "passwords don't match", + "description": "in password creation process, the two new password fields did not match" + }, + "passwordShort": { + "message": "password not long enough", + "description": "in password creation process, the password is not long enough to be secure" + }, + "pastePrivateKey": { + "message": "Paste your private key string here:", + "description": "For importing an account from a private key" + }, + "pasteSeed": { + "message": "Paste your seed phrase here!" + }, + "pleaseReviewTransaction": { + "message": "Please review your transaction." + }, + "privateKey": { + "message": "Private Key", + "description": "select this type of file to use to import an account" + }, + "privateKeyWarning": { + "message": "Warning: Never disclose this key. Anyone with your private keys can take steal any assets held in your account." + }, + "privateNetwork": { + "message": "Private Network" + }, + "qrCode": { + "message": "Show QR Code" + }, + "readdToken": { + "message": "You can add this token back in the future by going go to “Add token” in your accounts options menu." + }, + "readMore": { + "message": "Read more here." + }, + "receive": { + "message": "Receive" + }, + "recipientAddress": { + "message": "Recipient Address" + }, + "refundAddress": { + "message": "Your Refund Address" + }, + "rejected": { + "message": "Rejected" + }, + "required": { + "message": "Required" + }, + "retryWithMoreGas": { + "message": "Retry with a higher gas price here" + }, + "revert": { + "message": "Revert" + }, + "rinkeby": { + "message": "Rinkeby Test Network" + }, + "ropsten": { + "message": "Ropsten Test Network" + }, + "sampleAccountName": { + "message": "E.g. My new account", + "description": "Help user understand concept of adding a human-readable name to their account" + }, + "save": { + "message": "Save" + }, + "saveAsFile": { + "message": "Save as File", + "description": "Account export process" + }, + "selectService": { + "message": "Select Service" + }, + "send": { + "message": "Send" + }, + "sendTokens": { + "message": "Send Tokens" + }, + "sendTokensAnywhere": { + "message": "Send Tokens to anyone with an Ethereum account" + }, + "settings": { + "message": "Settings" + }, + "shapeshiftBuy": { + "message": "Buy with Shapeshift" + }, + "showPrivateKeys": { + "message": "Show Private Keys" + }, + "showQRCode": { + "message": "Show QR Code" + }, + "sign": { + "message": "Sign" + }, + "signMessage": { + "message": "Sign Message" + }, + "signNotice": { + "message": "Signing this message can have \ndangerous side effects. Only sign messages from \nsites you fully trust with your entire account.\n This dangerous method will be removed in a future version. " + }, + "sigRequest": { + "message": "Signature Request" + }, + "sigRequested": { + "message": "Signature Requested" + }, + "status": { + "message": "Status" + }, + "submit": { + "message": "Submit" + }, + "takesTooLong": { + "message": "Taking too long?" + }, + "testFaucet": { + "message": "Test Faucet" + }, + "to": { + "message": "To" + }, + "toETHviaShapeShift": { + "message": "$1 to ETH via ShapeShift", + "description": "system will fill in deposit type in start of message" + }, + "tokenBalance": { + "message": "Your Token Balance is:" + }, + "total": { + "message": "Total" + }, + "transactionMemo": { + "message": "Transaction memo (optional)" + }, + "transactionNumber": { + "message": "Transaction Number" + }, + "transfers": { + "message": "Transfers" + }, + "troubleTokenBalances": { + "message": "We had trouble loading your token balances. You can view them ", + "description": "Followed by a link (here) to view token balances" + }, + "typePassword": { + "message": "Type Your Password" + }, + "uiWelcome": { + "message": "Welcome to the New UI (Beta)" + }, + "uiWelcomeMessage": { + "message": "You are now using the new Metamask UI. Take a look around, try out new features like sending tokens, and let us know if you have any issues." + }, + "unavailable": { + "message": "Unavailable" + }, + "unknown": { + "message": "Unknown" + }, + "unknownNetwork": { + "message": "Unknown Private Network" + }, + "unknownNetworkId": { + "message": "Unknown network ID" + }, + "usaOnly": { + "message": "USA only", + "description": "Using this exchange is limited to people inside the USA" + }, + "usedByClients": { + "message": "Used by a variety of different clients" + }, + "viewAccount": { + "message": "View Account" + }, + "warning": { + "message": "Warning" + }, + "whatsThis": { + "message": "What's this?" + }, + "yourSigRequested": { + "message": "Your signature is being requested" + }, + "youSign": { + "message": "You are signing" } } diff --git a/app/manifest.json b/app/manifest.json index 0c89c2b3e..6fcf6cd7c 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -1,10 +1,10 @@ { - "name": "MetaMask", - "short_name": "Metamask", + "name": "__MSG_appName__", + "short_name": "__MSG_appName__", "version": "4.2.0", "manifest_version": 2, "author": "https://metamask.io", - "description": "Ethereum Browser Extension", + "description": "__MSG_appDescription__", "commands": { "_execute_browser_action": { "suggested_key": { @@ -56,6 +56,7 @@ ], "permissions": [ "storage", + "unlimitedStorage", "clipboardWrite", "http://localhost:8545/", "https://*.infura.io/" diff --git a/app/scripts/background.js b/app/scripts/background.js index 601ae0372..ef5513ec7 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -1,9 +1,11 @@ const urlUtil = require('url') const endOfStream = require('end-of-stream') const pump = require('pump') +const debounce = require('debounce-stream') const log = require('loglevel') const extension = require('extensionizer') const LocalStorageStore = require('obs-store/lib/localStorage') +const LocalStore = require('./lib/local-store') const storeTransform = require('obs-store/lib/transform') const asStream = require('obs-store/lib/asStream') const ExtensionPlatform = require('./platforms/extension') @@ -44,6 +46,8 @@ let openMetamaskTabsIDs = {} // state persistence const diskStore = new LocalStorageStore({ storageKey: STORAGE_KEY }) +const localStore = new LocalStore() +let versionedData // initialization flow initialize().catch(log.error) @@ -64,12 +68,23 @@ async function initialize () { async function loadStateFromPersistence () { // migrations const migrator = new Migrator({ migrations }) + // read from disk - let versionedData = diskStore.getState() || migrator.generateInitialState(firstTimeState) + // first from preferred, async API: + versionedData = (await localStore.get()) || + diskStore.getState() || + migrator.generateInitialState(firstTimeState) + // migrate data versionedData = await migrator.migrateData(versionedData) + if (!versionedData) { + throw new Error('MetaMask - migrator returned undefined') + } + // write to disk + if (localStore.isSupported) localStore.set(versionedData) diskStore.putState(versionedData) + // return just the data return versionedData.data } @@ -102,16 +117,30 @@ function setupController (initState) { // setup state persistence pump( asStream(controller.store), + debounce(1000), storeTransform(versionifyData), - asStream(diskStore) + storeTransform(syncDataWithExtension), + asStream(diskStore), + (error) => { + log.error('pump hit error', error) + } ) function versionifyData (state) { - const versionedData = diskStore.getState() versionedData.data = state return versionedData } + function syncDataWithExtension(state) { + if (localStore.isSupported) { + localStore.set(state) + .catch((err) => { + log.error('error setting state in local store:', err) + }) + } + return state + } + // // connect to other contexts // diff --git a/app/scripts/lib/local-store.js b/app/scripts/lib/local-store.js new file mode 100644 index 000000000..781aea17e --- /dev/null +++ b/app/scripts/lib/local-store.js @@ -0,0 +1,38 @@ +// We should not rely on local storage in an extension! +// We should use this instead! +// https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/storage/local + +const extension = require('extensionizer') +const { promisify } = require('util').promisify + +module.exports = class ExtensionStore { + constructor() { + this.isSupported = !!(extension.storage.local) + if (!this.isSupported) { + log.error('Storage local API not available.') + } + const local = extension.storage.local + this._get = promisify(local.get).bind(local) + this._set = promisify(local.set).bind(local) + } + + async get() { + if (!this.isSupported) return undefined + const result = await this._get() + // extension.storage.local always returns an obj + // if the object is empty, treat it as undefined + if (isEmpty(result)) { + return undefined + } else { + return result + } + } + + async set(state) { + return this._set(state) + } +} + +function isEmpty(obj) { + return Object.keys(obj).length === 0 +} diff --git a/app/scripts/lib/tx-gas-utils.js b/app/scripts/lib/tx-gas-utils.js index 6f6ff7852..0fa9dd8d4 100644 --- a/app/scripts/lib/tx-gas-utils.js +++ b/app/scripts/lib/tx-gas-utils.js @@ -4,7 +4,7 @@ const { BnMultiplyByFraction, bnToHex, } = require('./util') -const addHexPrefix = require('ethereumjs-util').addHexPrefix +const { addHexPrefix, isValidAddress } = require('ethereumjs-util') const SIMPLE_GAS_COST = '0x5208' // Hex for 21000, cost of a simple send. /* @@ -113,12 +113,14 @@ module.exports = class TxGasUtil { } } validateRecipient (txParams) { - if (txParams.to === '0x') { + if (txParams.to === '0x' || txParams.to === null ) { if (txParams.data) { delete txParams.to } else { throw new Error('Invalid recipient address') } + } else if ( txParams.to !== undefined && !isValidAddress(txParams.to) ) { + throw new Error('Invalid recipient address') } return txParams } diff --git a/circle.yml b/circle.yml deleted file mode 100644 index 6aba5c1be..000000000 --- a/circle.yml +++ /dev/null @@ -1,17 +0,0 @@ -machine: - node: - version: 8.1.4 -test: - override: - - "npm test" -dependencies: - pre: - - sudo apt-get update - # get latest stable firefox - - sudo apt-get install firefox - - firefox_cmd=`which firefox`; sudo rm -f $firefox_cmd; sudo ln -s `which firefox.ubuntu` $firefox_cmd - # get latest stable chrome - - wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add - - - sudo sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' - - sudo apt-get update - - sudo apt-get install google-chrome-stable \ No newline at end of file diff --git a/development/genStates.js b/development/genStates.js index 39a672ee0..d92e2bc2e 100644 --- a/development/genStates.js +++ b/development/genStates.js @@ -1,18 +1,21 @@ const fs = require('fs') const path = require('path') +const { promisify } = require('util') -const statesPath = path.join(__dirname, 'states') -const stateNames = fs.readdirSync(statesPath) +start().catch(console.error) -const states = stateNames.reduce((result, stateFileName) => { - const statePath = path.join(__dirname, 'states', stateFileName) - const stateFile = fs.readFileSync(statePath).toString() - const state = JSON.parse(stateFile) - result[stateFileName.split('.')[0].replace(/-/g, ' ', 'g')] = state - return result -}, {}) - -const result = `module.exports = ${JSON.stringify(states)}` - -const statesJsonPath = path.join(__dirname, 'states.js') -fs.writeFileSync(statesJsonPath, result) +async function start () { + const statesPath = path.join(__dirname, 'states') + const stateFilesNames = await promisify(fs.readdir)(statesPath) + const states = {} + await Promise.all(stateFilesNames.map(async (stateFileName) => { + const stateFilePath = path.join(__dirname, 'states', stateFileName) + const stateFileContent = await promisify(fs.readFile)(stateFilePath, 'utf8') + const state = JSON.parse(stateFileContent) + const stateName = stateFileName.split('.')[0].replace(/-/g, ' ', 'g') + states[stateName] = state + })) + const generatedFileContent = `module.exports = ${JSON.stringify(states)}` + const generatedFilePath = path.join(__dirname, 'states.js') + await promisify(fs.writeFile)(generatedFilePath, generatedFileContent) +} diff --git a/mock-dev.js b/development/mock-dev.js similarity index 81% rename from mock-dev.js rename to development/mock-dev.js index 8b04352cf..a1fb3a86d 100644 --- a/mock-dev.js +++ b/development/mock-dev.js @@ -15,17 +15,16 @@ const extend = require('xtend') const render = require('react-dom').render const h = require('react-hyperscript') -const pipe = require('mississippi').pipe -const Root = require('./ui/app/root') -const configureStore = require('./ui/app/store') -const actions = require('./ui/app/actions') -const states = require('./development/states') -const backGroundConnectionModifiers = require('./development/backGroundConnectionModifiers') -const Selector = require('./development/selector') -const MetamaskController = require('./app/scripts/metamask-controller') -const firstTimeState = require('./app/scripts/first-time-state') -const ExtensionPlatform = require('./app/scripts/platforms/extension') -const extension = require('./development/mockExtension') +const Root = require('../ui/app/root') +const configureStore = require('../ui/app/store') +const actions = require('../ui/app/actions') +const states = require('./states') +const backGroundConnectionModifiers = require('./backGroundConnectionModifiers') +const Selector = require('./selector') +const MetamaskController = require('../app/scripts/metamask-controller') +const firstTimeState = require('../app/scripts/first-time-state') +const ExtensionPlatform = require('../app/scripts/platforms/extension') +const extension = require('./mockExtension') const noop = function () {} const log = require('loglevel') @@ -52,7 +51,7 @@ function updateQueryParams(newView) { // CSS // -const MetaMaskUiCss = require('./ui/css') +const MetaMaskUiCss = require('../ui/css') const injectCss = require('inject-css') // diff --git a/ui-dev.js b/development/ui-dev.js similarity index 89% rename from ui-dev.js rename to development/ui-dev.js index 620d81667..cac433909 100644 --- a/ui-dev.js +++ b/development/ui-dev.js @@ -17,10 +17,10 @@ const render = require('react-dom').render const h = require('react-hyperscript') -const Root = require('./ui/app/root') -const configureStore = require('./development/uiStore') -const states = require('./development/states') -const Selector = require('./development/selector') +const Root = require('../ui/app/root') +const configureStore = require('./uiStore') +const states = require('./states') +const Selector = require('./selector') // logger const log = require('loglevel') @@ -35,7 +35,7 @@ const firstState = states[selectedView] updateQueryParams(selectedView) // CSS -const MetaMaskUiCss = require('./ui/css') +const MetaMaskUiCss = require('../ui/css') const injectCss = require('inject-css') diff --git a/docs/translating-guide.md b/docs/translating-guide.md new file mode 100644 index 000000000..62d444b5a --- /dev/null +++ b/docs/translating-guide.md @@ -0,0 +1,18 @@ +# MetaMask Translation Guide + +The MetaMask browser extension supports new translations added in the form of new locales files added in `app/_locales`. + +- [The MDN Guide to Internationalizing Extensions](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Internationalization) + +## Adding a new Language + +Each supported language is represented by a folder in `app/_locales` whose name is that language's subtag ([look up a language subtag using this tool](https://r12a.github.io/app-subtags/)). + +Inside that folder there should be a `messages.json` file that follows the specified format. An easy way to start your translation is to first duplicate `app/_locales/en/messages.json` (the english translation), and then update the `message` key for each in-app message. + +That's it! When MetaMask is loaded on a computer with that language set as the system language, they will see your translation instead of the default one. + +## Testing + +To verify that your translation works, you will need to [build a local copy](https://github.com/MetaMask/metamask-extension#building-locally) of MetaMask. + diff --git a/mascara/src/app/first-time/create-password-screen.js b/mascara/src/app/first-time/create-password-screen.js index 450d6a479..192da3399 100644 --- a/mascara/src/app/first-time/create-password-screen.js +++ b/mascara/src/app/first-time/create-password-screen.js @@ -2,6 +2,7 @@ import EventEmitter from 'events' import React, { Component } from 'react' import PropTypes from 'prop-types' import {connect} from 'react-redux' +import classnames from 'classnames' import {createNewVaultAndKeychain} from '../../../../ui/app/actions' import LoadingScreen from './loading-screen' import Breadcrumbs from './breadcrumbs' @@ -14,6 +15,7 @@ class CreatePasswordScreen extends Component { goToImportWithSeedPhrase: PropTypes.func.isRequired, goToImportAccount: PropTypes.func.isRequired, next: PropTypes.func.isRequired, + isMascara: PropTypes.bool.isRequired, } state = { @@ -53,14 +55,17 @@ class CreatePasswordScreen extends Component { } render () { - const { isLoading, goToImportWithSeedPhrase } = this.props + const { isLoading, goToImportWithSeedPhrase, isMascara } = this.props return isLoading ? : ( -
-
-
+
+
+ {isMascara &&
It allows you to hold ether & tokens, and interact with decentralized applications.
-
+
}
Create Password @@ -127,7 +132,7 @@ class CreatePasswordScreen extends Component { } export default connect( - ({ appState: { isLoading } }) => ({ isLoading }), + ({ appState: { isLoading }, metamask: { isMascara } }) => ({ isLoading, isMascara }), dispatch => ({ createAccount: password => dispatch(createNewVaultAndKeychain(password)), }) diff --git a/mascara/src/app/first-time/index.css b/mascara/src/app/first-time/index.css index f59eb4ce1..6c45816bd 100644 --- a/mascara/src/app/first-time/index.css +++ b/mascara/src/app/first-time/index.css @@ -1,3 +1,10 @@ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + src: local('Roboto'), local('Roboto-Regular'), url('/fonts/Roboto/Roboto-Regular.ttf') format('truetype'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} .first-time-flow { width: 100vw; @@ -6,21 +13,52 @@ display: flex; justify-content: center; flex: 1 0 auto; + font-weight: 400; + font-family: Roboto; } -.alpha-warning { +.alpha-warning, +.alpha-warning-welcome-screen { background: #f7861c; color: #fff; line-height: 2em; padding-left: 10vw; } -.first-view-main { +.alpha-warning-welcome-screen { + padding-left: 0; + text-align: center; +} + +.first-view-main-wrapper { + display: flex; + width: 100%; + padding-left: 10vw; +} + +.first-view-main, +.first-view-main__mascara { display: flex; flex-direction: row-reverse; + justify-content: center; +} + +.first-view-main__mascara { justify-content: space-between; } +@media screen and (max-width: 575px) { + .first-view-main-wrapper { + padding: 0; + } +} + +@media screen and (min-width: 1281px) { + .first-view-main { + width: 62vw; + } +} + .mascara-info { display: flex; flex-flow: column; @@ -81,6 +119,45 @@ width: initial !important; } + .alpha-warning, + .alpha-warning-welcome-screen { + line-height: 1em; + padding: 8px 12px; + } + + .first-view-main { + height: 100%; + flex-direction: column; + align-items: center; + justify-content: space-evenly; + margin-top: 12px; + } + + .first-view-main .create-password { + margin-top: 0px; + } + + .mascara-info { + margin-top: 0px; + width: 100%; + align-items: center; + } + + .mascara-info .info { + text-align: center; + font-size: 16px; + margin: 0 10px; + padding-left: 0px; + } + + .mascara-info :first-child { + align-self: center; + } + + .first-view-phone-invisible { + display: none; + } + .first-time-flow__input { width: initial !important; font-size: 14px !important; @@ -180,11 +257,11 @@ color: #1B344D; font-size: 16px; line-height: 23px; - font-family: Montserrat UltraLight; + font-family: Roboto; } .buy-ether__small-body-text { - font-family: Montserrat UltraLight; + font-family: Roboto; height: 14px; color: #757575; font-size: 12px; @@ -212,7 +289,7 @@ height: 334px; overflow-y: auto; color: #757575; - font-family: Montserrat UltraLight; + font-family: Roboto; font-size: 12px; line-height: 15px; text-align: justify; @@ -237,7 +314,7 @@ color: #5B5D67; font-size: 16px; line-height: 23px; - font-family: Montserrat UltraLight; + font-family: Roboto; } .backup-phrase__secret { @@ -255,7 +332,7 @@ .backup-phrase__secret-words { width: 310px; color: #5B5D67; - font-family: Montserrat Light; + font-family: Roboto; font-size: 20px; line-height: 26px; text-align: center; @@ -284,7 +361,7 @@ background: none; box-shadow: none; color: #FFFFFF; - font-family: Montserrat Regular; + font-family: Roboto; font-size: 12px; font-weight: bold; line-height: 15px; @@ -338,7 +415,7 @@ button.backup-phrase__reveal-button:hover { .backup-phrase__confirm-seed-option { color: #5B5D67; - font-family: Montserrat Light; + font-family: Roboto; font-size: 16px; line-height: 21px; background-color: #E7E7E7; @@ -360,7 +437,7 @@ button.backup-phrase__confirm-seed-option:hover { .import-account__faq-link { font-size: 18px; line-height: 23px; - font-family: Montserrat Light; + font-family: Roboto; } .import-account__selector-label { @@ -375,7 +452,7 @@ button.backup-phrase__confirm-seed-option:hover { background-color: #FFFFFF; margin-top: 14px; color: #5B5D67; - font-family: Montserrat Light; + font-family: Roboto; font-size: 18px; line-height: 23px; padding: 14px 21px; @@ -390,7 +467,7 @@ button.backup-phrase__confirm-seed-option:hover { font-size: 18px; line-height: 23px; margin-top: 21px; - font-family: Montserrat UltraLight; + font-family: Roboto; } .import-account__input-wrapper { @@ -436,7 +513,7 @@ button.backup-phrase__confirm-seed-option:hover { border: 1px solid #1B344D; border-radius: 4px; color: #1B344D; - font-family: Montserrat Light; + font-family: Roboto; font-size: 18px; display: flex; flex-flow: column nowrap; @@ -453,7 +530,7 @@ button.backup-phrase__confirm-seed-option:hover { .import-account__file-name { color: #000000; - font-family: Montserrat Light; + font-family: Roboto; font-size: 18px; line-height: 23px; margin-left: 22px; @@ -474,7 +551,7 @@ button.backup-phrase__confirm-seed-option:hover { .buy-ether__content-headline { color: #1B344D; - font-family: Montserrat Light; + font-family: Roboto; font-size: 18px; line-height: 23px; } @@ -503,7 +580,7 @@ button.backup-phrase__confirm-seed-option:hover { align-items: center; padding: 20px 0; color: #9B9B9B; - font-family: Montserrat Light; + font-family: Roboto; font-size: 14px; line-height: 18px; cursor: pointer; @@ -538,7 +615,7 @@ button.backup-phrase__confirm-seed-option:hover { .buy-ether__button-separator-text { font-size: 20px; line-height: 26px; - font-family: Montserrat Light; + font-family: Roboto; margin: 35px 0 14px 30px; display: flex; flex-flow: column nowrap; @@ -550,7 +627,7 @@ button.backup-phrase__confirm-seed-option:hover { color: #1B344D !important; font-size: 14px !important; line-height: 18px !important; - font-family: Montserrat UltraLight !important; + font-family: Roboto; } .buy-ether__action-content-wrapper { @@ -584,6 +661,7 @@ button.backup-phrase__confirm-seed-option:hover { color: #FFFFFF; font-size: 20px; font-weight: 500; + font-family: Roboto; line-height: 26px; text-align: center; text-transform: uppercase; @@ -608,7 +686,7 @@ button.first-time-flow__button:hover { color: #1B344D; font-size: 20px; line-height: 26px; - font-family: Montserrat Light; + font-family: Roboto; text-align: center; margin: 35px 0 14px; background-color: transparent; @@ -660,7 +738,7 @@ button.first-time-flow__button--tertiary:hover { font-size: 20px; line-height: 26px; text-align: center; - font-family: Montserrat UltraLight; + font-family: Roboto; } .icon { @@ -708,7 +786,7 @@ button.first-time-flow__button--tertiary:hover { .shapeshift-form__deposit-instruction { color: #757575; color: rgba(0, 0, 0, 0.45); - font-family: Montserrat Light; + font-family: Roboto; font-weight: 300; line-height: 19px; padding-bottom: 6px; @@ -725,7 +803,7 @@ button.first-time-flow__button--tertiary:hover { width: 100%; height: 45px; line-height: 44px; - font-family: Montserrat Light; + font-family: Roboto; } .shapeshift-form__address-input-label { @@ -734,7 +812,7 @@ button.first-time-flow__button--tertiary:hover { font-weight: 500; line-height: 18px; padding-bottom: 6px; - font-family: Montserrat Light; + font-family: Roboto; } .shapeshift-form__address-input { @@ -753,7 +831,7 @@ button.first-time-flow__button--tertiary:hover { .shapeshift-form__address-input-error-message { color: #FF001F; - font-family: Montserrat Light; + font-family: Roboto; font-size: 12px; height: 24px; line-height: 18px; @@ -763,7 +841,7 @@ button.first-time-flow__button--tertiary:hover { display: flex; flex-flow: row wrap; color: #9B9B9B; - font-family: Montserrat Light; + font-family: Roboto; font-size: 10px; line-height: 16px; } diff --git a/old-ui/app/accounts/import/json.js b/old-ui/app/accounts/import/json.js index 8d6bd7f7b..3ee3b95df 100644 --- a/old-ui/app/accounts/import/json.js +++ b/old-ui/app/accounts/import/json.js @@ -1,100 +1,122 @@ -const inherits = require('util').inherits const Component = require('react').Component const h = require('react-hyperscript') const connect = require('react-redux').connect const actions = require('../../../../ui/app/actions') const FileInput = require('react-simple-file-input').default +const PropTypes = require('prop-types') const HELP_LINK = 'https://github.com/MetaMask/faq/blob/master/README.md#q-i-cant-use-the-import-feature-for-uploading-a-json-file-the-window-keeps-closing-when-i-try-to-select-a-file' -module.exports = connect(mapStateToProps)(JsonImportSubview) +class JsonImportSubview extends Component { + constructor (props) { + super(props) -function mapStateToProps (state) { + this.state = { + file: null, + fileContents: '', + } + } + + render () { + const { error } = this.props + + return ( + h('div', { + style: { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + padding: '5px 15px 0px 15px', + }, + }, [ + + h('p', 'Used by a variety of different clients'), + h('a.warning', { + href: HELP_LINK, + target: '_blank', + }, 'File import not working? Click here!'), + + h(FileInput, { + readAs: 'text', + onLoad: this.onLoad.bind(this), + style: { + margin: '20px 0px 12px 20px', + fontSize: '15px', + }, + }), + + h('input.large-input.letter-spacey', { + type: 'password', + placeholder: 'Enter password', + id: 'json-password-box', + onKeyPress: this.createKeyringOnEnter.bind(this), + style: { + width: 260, + marginTop: 12, + }, + }), + + h('button.primary', { + onClick: this.createNewKeychain.bind(this), + style: { + margin: 12, + }, + }, 'Import'), + + error ? h('span.error', error) : null, + ]) + ) + } + + onLoad (event, file) { + this.setState({file: file, fileContents: event.target.result}) + } + + createKeyringOnEnter (event) { + if (event.key === 'Enter') { + event.preventDefault() + this.createNewKeychain() + } + } + + createNewKeychain () { + const { fileContents } = this.state + + if (!fileContents) { + const message = 'You must select a file to import.' + return this.props.displayWarning(message) + } + + const passwordInput = document.getElementById('json-password-box') + const password = passwordInput.value + + if (!password) { + const message = 'You must enter a password for the selected file.' + return this.props.displayWarning(message) + } + + this.props.importNewAccount([ fileContents, password ]) + } +} + +JsonImportSubview.propTypes = { + error: PropTypes.string, + displayWarning: PropTypes.func, + importNewAccount: PropTypes.func, +} + +const mapStateToProps = state => { return { error: state.appState.warning, } } -inherits(JsonImportSubview, Component) -function JsonImportSubview () { - Component.call(this) -} - -JsonImportSubview.prototype.render = function () { - const { error } = this.props - - return ( - h('div', { - style: { - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - padding: '5px 15px 0px 15px', - }, - }, [ - - h('p', 'Used by a variety of different clients'), - h('a.warning', { href: HELP_LINK, target: '_blank' }, 'File import not working? Click here!'), - - h(FileInput, { - readAs: 'text', - onLoad: this.onLoad.bind(this), - style: { - margin: '20px 0px 12px 20px', - fontSize: '15px', - }, - }), - - h('input.large-input.letter-spacey', { - type: 'password', - placeholder: 'Enter password', - id: 'json-password-box', - onKeyPress: this.createKeyringOnEnter.bind(this), - style: { - width: 260, - marginTop: 12, - }, - }), - - h('button.primary', { - onClick: this.createNewKeychain.bind(this), - style: { - margin: 12, - }, - }, 'Import'), - - error ? h('span.error', error) : null, - ]) - ) -} - -JsonImportSubview.prototype.onLoad = function (event, file) { - this.setState({file: file, fileContents: event.target.result}) -} - -JsonImportSubview.prototype.createKeyringOnEnter = function (event) { - if (event.key === 'Enter') { - event.preventDefault() - this.createNewKeychain() +const mapDispatchToProps = dispatch => { + return { + goHome: () => dispatch(actions.goHome()), + displayWarning: warning => dispatch(actions.displayWarning(warning)), + importNewAccount: options => dispatch(actions.importNewAccount('JSON File', options)), } } -JsonImportSubview.prototype.createNewKeychain = function () { - const state = this.state - const { fileContents } = state - - if (!fileContents) { - const message = 'You must select a file to import.' - return this.props.dispatch(actions.displayWarning(message)) - } - - const passwordInput = document.getElementById('json-password-box') - const password = passwordInput.value - - if (!password) { - const message = 'You must enter a password for the selected file.' - return this.props.dispatch(actions.displayWarning(message)) - } - - this.props.dispatch(actions.importNewAccount('JSON File', [ fileContents, password ])) -} +module.exports = connect(mapStateToProps, mapDispatchToProps)(JsonImportSubview) diff --git a/old-ui/app/keychains/hd/restore-vault.js b/old-ui/app/keychains/hd/restore-vault.js index 222172dfd..d334d8e5f 100644 --- a/old-ui/app/keychains/hd/restore-vault.js +++ b/old-ui/app/keychains/hd/restore-vault.js @@ -140,6 +140,19 @@ RestoreVaultScreen.prototype.createNewVaultAndRestore = function () { // check seed var seedBox = document.querySelector('textarea.twelve-word-phrase') var seed = seedBox.value.trim() + + // true if the string has more than a space between words. + if (seed.split(' ').length > 1) { + this.warning = 'there can only be a space between words' + this.props.dispatch(actions.displayWarning(this.warning)) + return + } + // true if seed contains a character that is not between a-z or a space + if (!seed.match(/^[a-z ]+$/)) { + this.warning = 'seed words only have lowercase characters' + this.props.dispatch(actions.displayWarning(this.warning)) + return + } if (seed.split(' ').length !== 12) { this.warning = 'seed phrases are 12 words long' this.props.dispatch(actions.displayWarning(this.warning)) diff --git a/package-lock.json b/package-lock.json index d7cff8f2e..bd3026816 100644 --- a/package-lock.json +++ b/package-lock.json @@ -332,6 +332,24 @@ } } }, + "acorn-node": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.3.0.tgz", + "integrity": "sha512-efP54n3d1aLfjL2UMdaXa6DsswwzJeI5rqhbFvXMrKiJ6eJFpf+7R0zN7t8IC+XKn2YOAFAv6xbBNgHUkoHWLw==", + "dev": true, + "requires": { + "acorn": "5.5.3", + "xtend": "4.0.1" + }, + "dependencies": { + "acorn": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz", + "integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ==", + "dev": true + } + } + }, "addressparser": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/addressparser/-/addressparser-1.0.1.tgz", @@ -454,6 +472,7 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz", "integrity": "sha1-U4rlKK+JgvKK4w2G8vF0VtJgmHM=", + "dev": true, "requires": { "ansi-wrap": "0.1.0" } @@ -475,6 +494,7 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz", "integrity": "sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw=", + "dev": true, "requires": { "ansi-wrap": "0.1.0" } @@ -640,12 +660,6 @@ } } }, - "array-iterate": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-iterate/-/array-iterate-1.1.1.tgz", - "integrity": "sha1-hlv3+K851rCYLGCQKRSsdrwBCPY=", - "dev": true - }, "array-last": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/array-last/-/array-last-1.3.0.tgz", @@ -853,31 +867,36 @@ "integrity": "sha1-GcenYEc3dEaPILLS0DNyrX1Mv10=" }, "autoprefixer": { - "version": "7.2.6", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-7.2.6.tgz", - "integrity": "sha512-Iq8TRIB+/9eQ8rbGhcP7ct5cYb/3qjNYAR2SnzLCEcwF6rvVOax8+9+fccgXk4bEhQGjOZd5TLhsksmAdsbGqQ==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-8.1.0.tgz", + "integrity": "sha512-b6mjq6VZ0guW6evRkKXL5sSSvIXICAE9dyWReZ3l/riidU7bVaJMe5cQ512SmaLA4Pvgnhi5MFsMs/Mvyh9//Q==", "requires": { - "browserslist": "2.11.3", - "caniuse-lite": "1.0.30000808", + "browserslist": "3.1.2", + "caniuse-lite": "1.0.30000813", "normalize-range": "0.1.2", "num2fraction": "1.2.2", - "postcss": "6.0.18", + "postcss": "6.0.19", "postcss-value-parser": "3.3.0" }, "dependencies": { "browserslist": { - "version": "2.11.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-2.11.3.tgz", - "integrity": "sha512-yWu5cXT7Av6mVwzWc8lMsJMHWn4xyjSuGYi4IozbVTLUOEYPSagUB8kiMDUHA1fS3zjr8nkxkn9jdvug4BBRmA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-3.1.2.tgz", + "integrity": "sha512-iO5MiK7MZXejqfnCK8onktxxb+mcW+KMiL/5gGF/UCWvVgPzbgbkA5cyYfqj/IIHHo7X1z0znrSHPw9AIfpvrw==", "requires": { - "caniuse-lite": "1.0.30000808", - "electron-to-chromium": "1.3.30" + "caniuse-lite": "1.0.30000813", + "electron-to-chromium": "1.3.37" } }, "caniuse-lite": { - "version": "1.0.30000808", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000808.tgz", - "integrity": "sha512-vT0JLmHdvq1UVbYXioxCXHYdNw55tyvi+IUWyX0Zeh1OFQi2IllYtm38IJnSgHWCv/zUnX1hdhy3vMJvuTNSqw==" + "version": "1.0.30000813", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000813.tgz", + "integrity": "sha512-A8ITSmH5SFdMFdC704ggjg+x2z5PzQmVlG8tavwnfvbC33Q1UYrj0+G+Xm0SNAnd4He36fwUE/KEWytOEchw+A==" + }, + "electron-to-chromium": { + "version": "1.3.37", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.37.tgz", + "integrity": "sha1-SpJzTgBEyM8LFVO+V+riGkxuX6s=" } } }, @@ -2374,35 +2393,36 @@ } }, "browserify": { - "version": "14.5.0", - "resolved": "https://registry.npmjs.org/browserify/-/browserify-14.5.0.tgz", - "integrity": "sha512-gKfOsNQv/toWz+60nSPfYzuwSEdzvV2WdxrVPUbPD/qui44rAkB3t3muNtmmGYHqrG56FGwX9SUEQmzNLAeS7g==", + "version": "16.1.1", + "resolved": "https://registry.npmjs.org/browserify/-/browserify-16.1.1.tgz", + "integrity": "sha512-iSH21jK0+IApV8YHOfmGt1qsGd74oflQ1Ko/28JOkWLFNBngAQfKb6WYIJ9CufH8vycqKX1sYU3y7ZrVhwevAg==", "dev": true, "requires": { "JSONStream": "1.3.2", "assert": "1.4.1", - "browser-pack": "6.0.2", + "browser-pack": "6.0.4", "browser-resolve": "1.11.2", "browserify-zlib": "0.2.0", - "buffer": "5.0.8", + "buffer": "5.1.0", "cached-path-relative": "1.0.1", - "concat-stream": "1.5.2", + "concat-stream": "1.6.0", "console-browserify": "1.1.0", "constants-browserify": "1.0.0", "crypto-browserify": "3.12.0", "defined": "1.0.0", "deps-sort": "2.0.0", - "domain-browser": "1.1.7", + "domain-browser": "1.2.0", "duplexer2": "0.1.4", - "events": "1.1.1", + "events": "2.0.0", "glob": "7.1.2", "has": "1.0.1", "htmlescape": "1.1.1", "https-browserify": "1.0.0", "inherits": "2.0.3", - "insert-module-globals": "7.0.1", + "insert-module-globals": "7.0.2", "labeled-stream-splicer": "2.0.0", - "module-deps": "4.1.1", + "mkdirp": "0.5.1", + "module-deps": "6.0.0", "os-browserify": "0.3.0", "parents": "1.0.1", "path-browserify": "0.0.0", @@ -2418,10 +2438,10 @@ "stream-http": "2.7.2", "string_decoder": "1.0.3", "subarg": "1.0.0", - "syntax-error": "1.3.0", + "syntax-error": "1.4.0", "through2": "2.0.3", "timers-browserify": "1.4.2", - "tty-browserify": "0.0.0", + "tty-browserify": "0.0.1", "url": "0.11.0", "util": "0.10.3", "vm-browserify": "0.0.4", @@ -2429,22 +2449,23 @@ }, "dependencies": { "browser-pack": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.0.2.tgz", - "integrity": "sha1-+GzWzvT1MAyOY+B6TVEvZfv/RTE=", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.0.4.tgz", + "integrity": "sha512-Q4Rvn7P6ObyWfc4stqLWHtG1MJ8vVtjgT24Zbu+8UTzxYuZouqZsmNRRTFVMY/Ux0eIKv1d+JWzsInTX+fdHPQ==", "dev": true, "requires": { "JSONStream": "1.3.2", - "combine-source-map": "0.7.2", + "combine-source-map": "0.8.0", "defined": "1.0.0", + "safe-buffer": "5.1.1", "through2": "2.0.3", "umd": "3.0.1" } }, "combine-source-map": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.7.2.tgz", - "integrity": "sha1-CHAxKFazB6h8xKxIbzqaYq7MwJ4=", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.8.0.tgz", + "integrity": "sha1-pY0N8ELBhvz4IqjoAV9UUNLXmos=", "dev": true, "requires": { "convert-source-map": "1.1.3", @@ -2453,38 +2474,11 @@ "source-map": "0.5.7" } }, - "concat-stream": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz", - "integrity": "sha1-cIl4Yk2FavQaWnQd790mHadSwmY=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.0.6", - "typedarray": "0.0.6" - }, - "dependencies": { - "readable-stream": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", - "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "string_decoder": "0.10.31", - "util-deprecate": "1.0.2" - } - }, - "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 - } - } + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true }, "duplexer2": { "version": "0.1.4", @@ -2495,6 +2489,12 @@ "readable-stream": "2.3.3" } }, + "events": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/events/-/events-2.0.0.tgz", + "integrity": "sha512-r/M5YkNg9zwI8QbSf7tsDWWJvO3PGwZXyG7GpFAxtMASnHL2eblFd7iHiGPtyGKKFPZ59S63NeX10Ws6WqGDcg==", + "dev": true + }, "inline-source-map": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz", @@ -2521,6 +2521,12 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true + }, + "tty-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", + "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==", + "dev": true } } }, @@ -2669,9 +2675,9 @@ } }, "buffer": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.0.8.tgz", - "integrity": "sha512-xXvjQhVNz50v2nPeoOsNqWCLGfiv4ji/gXZM28jnVwdLJxH4mFyqgqCKfaK9zf1KUbG6zTkjLOy7ou+jSMarGA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.1.0.tgz", + "integrity": "sha512-YkIRgwsZwJWTnyQrsBTWefizHh+8GYj3kbL1BTiAQ/9pwpino0G7B2gp5tx/FUBqUlvtxV85KNR3mwfAtv15Yw==", "dev": true, "requires": { "base64-js": "1.2.1", @@ -2821,12 +2827,6 @@ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, - "ccount": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.0.2.tgz", - "integrity": "sha1-U7ai+BW7d7nChx97mnLDol8djok=", - "dev": true - }, "center-align": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", @@ -2884,12 +2884,6 @@ "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.1.tgz", "integrity": "sha1-92hxvl72bdt/j440eOzDdMJ9bco=" }, - "character-entities-html4": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-1.1.1.tgz", - "integrity": "sha1-NZoqSg9+KdPcKsmb2+Ie45Q46lA=", - "dev": true - }, "character-entities-legacy": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.1.tgz", @@ -3617,6 +3611,16 @@ "object-assign": "4.1.1" } }, + "cross-env": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-5.1.4.tgz", + "integrity": "sha512-Mx8mw6JWhfpYoEk7PGvHxJMLQwQHORAs8+2bX+C1lGQ4h3GkDb1zbzC2Nw85YH9ZQMlO0BHZxMacgrfPmMFxbg==", + "dev": true, + "requires": { + "cross-spawn": "5.1.0", + "is-windows": "1.0.2" + } + }, "cross-spawn": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", @@ -3876,11 +3880,6 @@ "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=", "dev": true }, - "cyclist": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", - "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=" - }, "d": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", @@ -3965,16 +3964,6 @@ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" }, - "decamelize-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", - "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", - "dev": true, - "requires": { - "decamelize": "1.2.0", - "map-obj": "1.0.1" - } - }, "decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", @@ -4250,33 +4239,6 @@ "randombytes": "2.0.5" } }, - "dir-glob": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.0.0.tgz", - "integrity": "sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag==", - "dev": true, - "requires": { - "arrify": "1.0.1", - "path-type": "3.0.0" - }, - "dependencies": { - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } - }, "disc": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/disc/-/disc-1.3.3.tgz", @@ -4540,15 +4502,6 @@ "domelementtype": "1.3.0" } }, - "dot-prop": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", - "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", - "dev": true, - "requires": { - "is-obj": "1.0.1" - } - }, "double-ended-queue": { "version": "2.1.0-0", "resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz", @@ -7090,15 +7043,6 @@ "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", "dev": true }, - "from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.3" - } - }, "fs-access": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/fs-access/-/fs-access-1.0.1.tgz", @@ -8321,23 +8265,6 @@ "sparkles": "1.0.0" } }, - "gonzales-pe": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/gonzales-pe/-/gonzales-pe-4.2.3.tgz", - "integrity": "sha512-Kjhohco0esHQnOiqqdJeNz/5fyPkOMD/d6XVjwTAoPGUFh0mCollPUTUTa2OZy4dYNAqlPIQdTiNzJTWdd9Htw==", - "dev": true, - "requires": { - "minimist": "1.1.3" - }, - "dependencies": { - "minimist": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.1.3.tgz", - "integrity": "sha1-O+39kaktOQFvz6ocaB6Pqhoe/ag=", - "dev": true - } - } - }, "graceful-fs": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", @@ -8431,16 +8358,51 @@ } }, "gulp-autoprefixer": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/gulp-autoprefixer/-/gulp-autoprefixer-4.1.0.tgz", - "integrity": "sha1-Bkr3PMAsrayP800L+T/9+5TqEqo=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/gulp-autoprefixer/-/gulp-autoprefixer-5.0.0.tgz", + "integrity": "sha1-gjfCeKaXdScKHK/n1vEBz81YVUQ=", "requires": { - "autoprefixer": "7.2.6", + "autoprefixer": "8.1.0", "fancy-log": "1.3.2", - "plugin-error": "0.1.2", - "postcss": "6.0.18", + "plugin-error": "1.0.1", + "postcss": "6.0.19", "through2": "2.0.3", "vinyl-sourcemaps-apply": "0.2.1" + }, + "dependencies": { + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "requires": { + "assign-symbols": "1.0.0", + "is-extendable": "1.0.1" + } + }, + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "2.0.4" + } + }, + "plugin-error": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "requires": { + "ansi-colors": "1.0.1", + "arr-diff": "4.0.0", + "arr-union": "3.1.0", + "extend-shallow": "3.0.2" + } + } } }, "gulp-babel": { @@ -8779,18 +8741,19 @@ } }, "gulp-stylelint": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/gulp-stylelint/-/gulp-stylelint-4.0.0.tgz", - "integrity": "sha1-RA+n5sRH6SZEcA4eKganPm5Fd1A=", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/gulp-stylelint/-/gulp-stylelint-7.0.0.tgz", + "integrity": "sha512-0PI+tNTzaJz5+qO3i9Jyd04ZPSb+NCN7bZ2GaIArvbQpuyJha9p3lpWxPG+XJtrVT42bIiyLeYKPnLe7uW4dQQ==", "dev": true, "requires": { - "chalk": "2.3.1", + "chalk": "2.3.2", "deep-extend": "0.5.0", - "gulp-util": "3.0.8", + "fancy-log": "1.3.2", "mkdirp": "0.5.1", + "plugin-error": "1.0.1", "promise": "8.0.1", + "source-map": "0.5.7", "strip-ansi": "4.0.0", - "stylelint": "8.4.0", "through2": "2.0.3" }, "dependencies": { @@ -8801,90 +8764,39 @@ "dev": true }, "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "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==", "dev": true, "requires": { "color-convert": "1.9.1" } }, - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", "dev": true }, - "camelcase-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", - "integrity": "sha1-oqpfsa9oh1glnDLBQUJteJI7m3c=", - "dev": true, - "requires": { - "camelcase": "4.1.0", - "map-obj": "2.0.0", - "quick-lru": "1.1.0" - } - }, "chalk": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz", - "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.2.tgz", + "integrity": "sha512-ZM4j2/ld/YZDc3Ma8PgN7gyAk+kHMMMyzLNryCPGhWrsfAuDVeuid5bpRFTDgMH9JBK2lA4dyyAkkZYF/WcqDQ==", "dev": true, "requires": { - "ansi-styles": "3.2.0", + "ansi-styles": "3.2.1", "escape-string-regexp": "1.0.5", - "supports-color": "5.2.0" + "supports-color": "5.3.0" } }, - "cosmiconfig": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-3.1.0.tgz", - "integrity": "sha512-zedsBhLSbPBms+kE7AH4vHg6JsKDz6epSv2/+5XHs8ILHlgDciSJfSWf8sX9aQ52Jb7KI7VswUTsLpR/G0cr2Q==", + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", "dev": true, "requires": { - "is-directory": "0.3.1", - "js-yaml": "3.10.0", - "parse-json": "3.0.0", - "require-from-string": "2.0.1" - } - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "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" - } - }, - "get-stdin": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", - "integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g=", - "dev": true - }, - "globby": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz", - "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=", - "dev": true, - "requires": { - "array-union": "1.0.2", - "dir-glob": "2.0.0", - "glob": "7.1.2", - "ignore": "3.3.7", - "pify": "3.0.0", - "slash": "1.0.0" + "assign-symbols": "1.0.0", + "is-extendable": "1.0.1" } }, "has-flag": { @@ -8893,214 +8805,25 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, - "indent-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", - "dev": true - }, - "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=", - "dev": true - }, - "known-css-properties": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.5.0.tgz", - "integrity": "sha512-LOS0CoS8zcZnB1EjLw4LLqDXw8nvt3AGH5dXLQP3D9O1nLLA+9GC5GnPl5mmF+JiQAtSX4VyZC7KvEtcA4kUtA==", - "dev": true - }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "parse-json": "4.0.0", - "pify": "3.0.0", - "strip-bom": "3.0.0" - }, - "dependencies": { - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "1.3.1", - "json-parse-better-errors": "1.0.1" - } - } + "is-plain-object": "2.0.4" } }, - "log-symbols": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", - "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "plugin-error": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", "dev": true, "requires": { - "chalk": "2.3.1" - } - }, - "map-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", - "integrity": "sha1-plzSkIepJZi4eRJXpSPgISIqwfk=", - "dev": true - }, - "meow": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-4.0.0.tgz", - "integrity": "sha512-Me/kel335m6vMKmEmA6c87Z6DUFW3JqkINRnxkbC+A/PUm0D5Fl2dEBQrPKnqCL9Te/CIa1MUt/0InMJhuC/sw==", - "dev": true, - "requires": { - "camelcase-keys": "4.2.0", - "decamelize-keys": "1.1.0", - "loud-rejection": "1.6.0", - "minimist": "1.2.0", - "minimist-options": "3.0.2", - "normalize-package-data": "2.4.0", - "read-pkg-up": "3.0.0", - "redent": "2.0.0", - "trim-newlines": "2.0.0" - } - }, - "parse-json": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-3.0.0.tgz", - "integrity": "sha1-+m9HsY4jgm6tMvJj50TQ4ehH+xM=", - "dev": true, - "requires": { - "error-ex": "1.3.1" - } - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, - "postcss-less": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/postcss-less/-/postcss-less-1.1.3.tgz", - "integrity": "sha512-WS0wsQxRm+kmN8wEYAGZ3t4lnoNfoyx9EJZrhiPR1K0lMHR0UNWnz52Ya5QRXChHtY75Ef+kDc05FpnBujebgw==", - "dev": true, - "requires": { - "postcss": "5.2.18" - }, - "dependencies": { - "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": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "1.1.3", - "js-base64": "2.4.3", - "source-map": "0.5.7", - "supports-color": "3.2.3" - } - }, - "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.1.1" - } - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "postcss-reporter": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-reporter/-/postcss-reporter-5.0.0.tgz", - "integrity": "sha512-rBkDbaHAu5uywbCR2XE8a25tats3xSOsGNx6mppK6Q9kSFGKc/FyAzfci+fWM2l+K402p1D0pNcfDGxeje5IKg==", - "dev": true, - "requires": { - "chalk": "2.3.1", - "lodash": "4.17.4", - "log-symbols": "2.2.0", - "postcss": "6.0.18" - } - }, - "postcss-scss": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-1.0.3.tgz", - "integrity": "sha512-N2ZPDOV5PGEGVwdiB7b1QppxKkmkHodNWkemja7PV+/mHqbUlA6ZcYRreden5Ag5nwBBX8/aRE7lfg1xjdszyg==", - "dev": true, - "requires": { - "postcss": "6.0.18" - } - }, - "postcss-selector-parser": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz", - "integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=", - "dev": true, - "requires": { - "dot-prop": "4.2.0", - "indexes-of": "1.0.1", - "uniq": "1.0.1" + "ansi-colors": "1.0.1", + "arr-diff": "4.0.0", + "arr-union": "3.1.0", + "extend-shallow": "3.0.2" } }, "promise": { @@ -9112,65 +8835,12 @@ "asap": "2.0.6" } }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", - "dev": true, - "requires": { - "load-json-file": "4.0.0", - "normalize-package-data": "2.4.0", - "path-type": "3.0.0" - } - }, - "read-pkg-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", - "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", - "dev": true, - "requires": { - "find-up": "2.1.0", - "read-pkg": "3.0.0" - } - }, - "redent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", - "integrity": "sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=", - "dev": true, - "requires": { - "indent-string": "3.2.0", - "strip-indent": "2.0.0" - } - }, - "require-from-string": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.1.tgz", - "integrity": "sha1-xUUjPp19pmFunVmt+zn8n1iGdv8=", - "dev": true - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" - } - }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", @@ -9180,88 +8850,14 @@ "ansi-regex": "3.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 - }, - "strip-indent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", - "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", - "dev": true - }, - "stylelint": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-8.4.0.tgz", - "integrity": "sha512-56hPH5mTFnk8LzlEuTWq0epa34fHuS54UFYQidBOFt563RJBNi1nz1F2HK2MoT1X1waq47milvRsRahFCCJs/Q==", - "dev": true, - "requires": { - "autoprefixer": "7.2.6", - "balanced-match": "1.0.0", - "chalk": "2.3.1", - "cosmiconfig": "3.1.0", - "debug": "3.1.0", - "execall": "1.0.0", - "file-entry-cache": "2.0.0", - "get-stdin": "5.0.1", - "globby": "7.1.1", - "globjoin": "0.1.4", - "html-tags": "2.0.0", - "ignore": "3.3.7", - "imurmurhash": "0.1.4", - "known-css-properties": "0.5.0", - "lodash": "4.17.4", - "log-symbols": "2.2.0", - "mathml-tag-names": "2.0.1", - "meow": "4.0.0", - "micromatch": "2.3.11", - "normalize-selector": "0.2.0", - "pify": "3.0.0", - "postcss": "6.0.18", - "postcss-html": "0.12.0", - "postcss-less": "1.1.3", - "postcss-media-query-parser": "0.2.3", - "postcss-reporter": "5.0.0", - "postcss-resolve-nested-selector": "0.1.1", - "postcss-safe-parser": "3.0.1", - "postcss-sass": "0.2.0", - "postcss-scss": "1.0.3", - "postcss-selector-parser": "3.1.1", - "postcss-value-parser": "3.3.0", - "resolve-from": "4.0.0", - "specificity": "0.3.2", - "string-width": "2.1.1", - "style-search": "0.1.0", - "sugarss": "1.0.1", - "svg-tags": "1.0.0", - "table": "4.0.2" - } - }, - "sugarss": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sugarss/-/sugarss-1.0.1.tgz", - "integrity": "sha512-3qgLZytikQQEVn1/FrhY7B68gPUUGY3R1Q1vTiD5xT+Ti1DP/8iZuwFet9ONs5+bmL8pZoDQ6JrQHVgrNlK6mA==", - "dev": true, - "requires": { - "postcss": "6.0.18" - } - }, "supports-color": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz", - "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.3.0.tgz", + "integrity": "sha512-0aP01LLIskjKs3lq52EC0aGBAJhLq7B2Rd8HC/DR/PtNNpcLilNmHC12O+hu0usQpo7wtHNRqtrhBwtDb0+dNg==", "dev": true, "requires": { "has-flag": "3.0.0" } - }, - "trim-newlines": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", - "integrity": "sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA=", - "dev": true } } }, @@ -10492,9 +10088,9 @@ } }, "insert-module-globals": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.0.1.tgz", - "integrity": "sha1-wDv04BywhtW15azorQr+eInWOMM=", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.0.2.tgz", + "integrity": "sha512-p3s7g96Nm62MbHRuj9ZXab0DuJNWD7qcmdUXCOQ/ZZn42DtDXfsLill7bq19lDCx3K3StypqUnuE3H2VmIJFUw==", "dev": true, "requires": { "JSONStream": "1.3.2", @@ -10644,12 +10240,6 @@ "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.1.tgz", "integrity": "sha1-x3B5zJHU76x3W+EDS/LSQ/lebwg=" }, - "is-alphanumeric": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-alphanumeric/-/is-alphanumeric-1.0.0.tgz", - "integrity": "sha1-Spzvcdr0wAHB2B1j0UDPU/1oifQ=", - "dev": true - }, "is-alphanumerical": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.1.tgz", @@ -10847,12 +10437,6 @@ "integrity": "sha1-8mWrian0RQNO9q/xWo8AsA9VF5k=", "dev": true }, - "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", - "dev": true - }, "is-odd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-1.0.0.tgz", @@ -11237,12 +10821,6 @@ "resolved": "https://registry.npmjs.org/json-loader/-/json-loader-0.5.7.tgz", "integrity": "sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w==" }, - "json-parse-better-errors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.1.tgz", - "integrity": "sha512-xyQpxeWWMKyJps9CuGJYeng6ssI5bpqS9ltQpdVQ90t4ql6NdnxFKh95JcRt2cun/DjMVNrdjniLPuMA69xmCw==", - "dev": true - }, "json-rpc-engine": { "version": "3.6.1", "resolved": "https://registry.npmjs.org/json-rpc-engine/-/json-rpc-engine-3.6.1.tgz", @@ -11561,12 +11139,195 @@ "useragent": "2.3.0" }, "dependencies": { + "browser-pack": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.0.4.tgz", + "integrity": "sha512-Q4Rvn7P6ObyWfc4stqLWHtG1MJ8vVtjgT24Zbu+8UTzxYuZouqZsmNRRTFVMY/Ux0eIKv1d+JWzsInTX+fdHPQ==", + "dev": true, + "requires": { + "JSONStream": "1.3.2", + "combine-source-map": "0.8.0", + "defined": "1.0.0", + "safe-buffer": "5.1.1", + "through2": "2.0.3", + "umd": "3.0.1" + } + }, + "browserify": { + "version": "14.5.0", + "resolved": "https://registry.npmjs.org/browserify/-/browserify-14.5.0.tgz", + "integrity": "sha512-gKfOsNQv/toWz+60nSPfYzuwSEdzvV2WdxrVPUbPD/qui44rAkB3t3muNtmmGYHqrG56FGwX9SUEQmzNLAeS7g==", + "dev": true, + "requires": { + "JSONStream": "1.3.2", + "assert": "1.4.1", + "browser-pack": "6.0.4", + "browser-resolve": "1.11.2", + "browserify-zlib": "0.2.0", + "buffer": "5.1.0", + "cached-path-relative": "1.0.1", + "concat-stream": "1.5.2", + "console-browserify": "1.1.0", + "constants-browserify": "1.0.0", + "crypto-browserify": "3.12.0", + "defined": "1.0.0", + "deps-sort": "2.0.0", + "domain-browser": "1.1.7", + "duplexer2": "0.1.4", + "events": "1.1.1", + "glob": "7.1.2", + "has": "1.0.1", + "htmlescape": "1.1.1", + "https-browserify": "1.0.0", + "inherits": "2.0.3", + "insert-module-globals": "7.0.2", + "labeled-stream-splicer": "2.0.0", + "module-deps": "4.1.1", + "os-browserify": "0.3.0", + "parents": "1.0.1", + "path-browserify": "0.0.0", + "process": "0.11.10", + "punycode": "1.4.1", + "querystring-es3": "0.2.1", + "read-only-stream": "2.0.0", + "readable-stream": "2.3.3", + "resolve": "1.4.0", + "shasum": "1.0.2", + "shell-quote": "1.6.1", + "stream-browserify": "2.0.1", + "stream-http": "2.7.2", + "string_decoder": "1.0.3", + "subarg": "1.0.0", + "syntax-error": "1.4.0", + "through2": "2.0.3", + "timers-browserify": "1.4.2", + "tty-browserify": "0.0.0", + "url": "0.11.0", + "util": "0.10.3", + "vm-browserify": "0.0.4", + "xtend": "4.0.1" + } + }, "colors": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", "dev": true }, + "combine-source-map": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.8.0.tgz", + "integrity": "sha1-pY0N8ELBhvz4IqjoAV9UUNLXmos=", + "dev": true, + "requires": { + "convert-source-map": "1.1.3", + "inline-source-map": "0.6.2", + "lodash.memoize": "3.0.4", + "source-map": "0.5.7" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "concat-stream": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz", + "integrity": "sha1-cIl4Yk2FavQaWnQd790mHadSwmY=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.0.6", + "typedarray": "0.0.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "0.10.31", + "util-deprecate": "1.0.2" + } + }, + "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 + } + } + }, + "duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", + "dev": true, + "requires": { + "readable-stream": "2.3.3" + } + }, + "inline-source-map": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz", + "integrity": "sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU=", + "dev": true, + "requires": { + "source-map": "0.5.7" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "lodash.memoize": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", + "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=", + "dev": true + }, + "module-deps": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-4.1.1.tgz", + "integrity": "sha1-IyFYM/HaE/1gbMuAh7RIUty4If0=", + "dev": true, + "requires": { + "JSONStream": "1.3.2", + "browser-resolve": "1.11.2", + "cached-path-relative": "1.0.1", + "concat-stream": "1.5.2", + "defined": "1.0.0", + "detective": "4.7.1", + "duplexer2": "0.1.4", + "inherits": "2.0.3", + "parents": "1.0.1", + "readable-stream": "2.3.3", + "resolve": "1.4.0", + "stream-combiner2": "1.1.1", + "subarg": "1.0.0", + "through2": "2.0.3", + "xtend": "4.0.1" + } + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -11986,24 +11747,6 @@ "object-assign": "4.1.1" } }, - "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" - }, - "dependencies": { - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - } - } - }, "lodash": { "version": "4.17.4", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", @@ -12505,12 +12248,6 @@ "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" }, - "longest-streak": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-2.0.2.tgz", - "integrity": "sha512-TmYTeEYxiAmSVdpbnQDXGtvYOIRsCMg89CVZzwzc2o7GFL1CjoiRPjH5ec0NFAVlAx3fVof9dX/t6KKRAo2OWA==", - "dev": true - }, "loose-envify": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", @@ -12685,12 +12422,6 @@ "resolved": "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.1.tgz", "integrity": "sha1-GZTfLTr0gR3lmmcUk0wrIpJzRRg=" }, - "markdown-table": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-1.1.1.tgz", - "integrity": "sha1-Sz3ToTPRUYuO8NvHCb8qG0gkvIw=", - "dev": true - }, "matchdep": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz", @@ -13033,16 +12764,6 @@ } } }, - "mdast-util-compact": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-compact/-/mdast-util-compact-1.0.1.tgz", - "integrity": "sha1-zbX4TitqLTEU3zO9BdnLMuPECDo=", - "dev": true, - "requires": { - "unist-util-modify-children": "1.1.1", - "unist-util-visit": "1.3.0" - } - }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -13193,6 +12914,15 @@ "through2": "2.0.3", "xtend": "4.0.1" } + }, + "pump": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.3.tgz", + "integrity": "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==", + "requires": { + "end-of-stream": "1.4.0", + "once": "1.4.0" + } } } }, @@ -13397,33 +13127,6 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" }, - "minimist-options": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", - "integrity": "sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==", - "dev": true, - "requires": { - "arrify": "1.0.1", - "is-plain-obj": "1.1.0" - } - }, - "mississippi": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-1.3.0.tgz", - "integrity": "sha1-0gFYPrEjJ+PFwWQqQEqcrPlONPU=", - "requires": { - "concat-stream": "1.6.0", - "duplexify": "3.5.1", - "end-of-stream": "1.4.0", - "flush-write-stream": "1.0.2", - "from2": "2.3.0", - "parallel-transform": "1.1.0", - "pump": "1.0.3", - "pumpify": "1.3.5", - "stream-each": "1.2.2", - "through2": "2.0.3" - } - }, "mixin-deep": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.0.tgz", @@ -13521,17 +13224,17 @@ "dev": true }, "module-deps": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-4.1.1.tgz", - "integrity": "sha1-IyFYM/HaE/1gbMuAh7RIUty4If0=", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.0.0.tgz", + "integrity": "sha512-BKsMhJJENEM4dTgqq2MDTTHXRHcNUFegoAwlG4HO4VMdUyMcJDKgfgI+MOv6tR5Iv8G3MKZFgsSiyP3ZoosRMw==", "dev": true, "requires": { "JSONStream": "1.3.2", "browser-resolve": "1.11.2", "cached-path-relative": "1.0.1", - "concat-stream": "1.5.2", + "concat-stream": "1.6.0", "defined": "1.0.0", - "detective": "4.7.1", + "detective": "5.1.0", "duplexer2": "0.1.4", "inherits": "2.0.3", "parents": "1.0.1", @@ -13543,31 +13246,15 @@ "xtend": "4.0.1" }, "dependencies": { - "concat-stream": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz", - "integrity": "sha1-cIl4Yk2FavQaWnQd790mHadSwmY=", + "detective": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.1.0.tgz", + "integrity": "sha512-TFHMqfOvxlgrfVzTEkNBSh9SvSNX/HfF4OFI2QFGCyPm02EsyILqnUeb5P6q7JZ3SFNTBL5t2sePRgrN4epUWQ==", "dev": true, "requires": { - "inherits": "2.0.3", - "readable-stream": "2.0.6", - "typedarray": "0.0.6" - }, - "dependencies": { - "readable-stream": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", - "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "string_decoder": "0.10.31", - "util-deprecate": "1.0.2" - } - } + "acorn-node": "1.3.0", + "defined": "1.0.0", + "minimist": "1.2.0" } }, "duplexer2": { @@ -13578,12 +13265,6 @@ "requires": { "readable-stream": "2.3.3" } - }, - "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 } } }, @@ -16327,36 +16008,12 @@ "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", "dev": true }, - "p-limit": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz", - "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==", - "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.2.0" - } - }, "p-map": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", "dev": true }, - "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 - }, "pac-proxy-agent": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-1.1.0.tgz", @@ -16403,16 +16060,6 @@ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==" }, - "parallel-transform": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", - "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", - "requires": { - "cyclist": "0.2.2", - "inherits": "2.0.3", - "readable-stream": "2.3.3" - } - }, "parents": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz", @@ -16726,6 +16373,7 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=", + "dev": true, "requires": { "ansi-cyan": "0.1.1", "ansi-red": "0.1.1", @@ -16738,6 +16386,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=", + "dev": true, "requires": { "arr-flatten": "1.1.0", "array-slice": "0.2.3" @@ -16746,17 +16395,20 @@ "arr-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", - "integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=" + "integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=", + "dev": true }, "array-slice": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", - "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=" + "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", + "dev": true }, "extend-shallow": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=", + "dev": true, "requires": { "kind-of": "1.1.0" } @@ -16764,7 +16416,8 @@ "kind-of": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", - "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=" + "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=", + "dev": true } } }, @@ -16837,31 +16490,31 @@ } }, "postcss": { - "version": "6.0.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.18.tgz", - "integrity": "sha512-X8MyLi3OYI1o71u0SsefWLpGBo5xnGiK1Pn+nrZFplc671Ts7L8aPwEbPIO8AWpulK5wuaVzyM9Rw6R8o7hYBw==", + "version": "6.0.19", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.19.tgz", + "integrity": "sha512-f13HRz0HtVwVaEuW6J6cOUCBLFtymhgyLPV7t4QEk2UD3twRI9IluDcQNdzQdBpiixkXj2OmzejhhTbSbDxNTg==", "requires": { - "chalk": "2.3.1", + "chalk": "2.3.2", "source-map": "0.6.1", - "supports-color": "5.2.0" + "supports-color": "5.3.0" }, "dependencies": { "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "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.1" } }, "chalk": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz", - "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.2.tgz", + "integrity": "sha512-ZM4j2/ld/YZDc3Ma8PgN7gyAk+kHMMMyzLNryCPGhWrsfAuDVeuid5bpRFTDgMH9JBK2lA4dyyAkkZYF/WcqDQ==", "requires": { - "ansi-styles": "3.2.0", + "ansi-styles": "3.2.1", "escape-string-regexp": "1.0.5", - "supports-color": "5.2.0" + "supports-color": "5.3.0" } }, "has-flag": { @@ -16875,26 +16528,15 @@ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" }, "supports-color": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz", - "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.3.0.tgz", + "integrity": "sha512-0aP01LLIskjKs3lq52EC0aGBAJhLq7B2Rd8HC/DR/PtNNpcLilNmHC12O+hu0usQpo7wtHNRqtrhBwtDb0+dNg==", "requires": { "has-flag": "3.0.0" } } } }, - "postcss-html": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/postcss-html/-/postcss-html-0.12.0.tgz", - "integrity": "sha512-KxKUpj7AY7nlCbLcTOYxdfJnGE7QFAfU2n95ADj1Q90RM/pOLdz8k3n4avOyRFs7MDQHcRzJQWM1dehCwJxisQ==", - "dev": true, - "requires": { - "htmlparser2": "3.9.2", - "remark": "8.0.0", - "unist-util-find-all-after": "1.0.1" - } - }, "postcss-less": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/postcss-less/-/postcss-less-0.14.0.tgz", @@ -16998,25 +16640,6 @@ "integrity": "sha1-Kcy8fDfe36wwTp//C/FZaz9qDk4=", "dev": true }, - "postcss-safe-parser": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-3.0.1.tgz", - "integrity": "sha1-t1Pv9sfArqXoN1++TN6L+QY/8UI=", - "dev": true, - "requires": { - "postcss": "6.0.18" - } - }, - "postcss-sass": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/postcss-sass/-/postcss-sass-0.2.0.tgz", - "integrity": "sha512-cUmYzkP747fPCQE6d+CH2l1L4VSyIlAzZsok3HPjb5Gzsq3jE+VjpAdGlPsnQ310WKWI42sw+ar0UNN59/f3hg==", - "dev": true, - "requires": { - "gonzales-pe": "4.2.3", - "postcss": "6.0.18" - } - }, "postcss-scss": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-0.4.1.tgz", @@ -17304,9 +16927,9 @@ } }, "pump": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.3.tgz", - "integrity": "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "requires": { "end-of-stream": "1.4.0", "once": "1.4.0" @@ -17320,6 +16943,17 @@ "duplexify": "3.5.1", "inherits": "2.0.3", "pump": "1.0.3" + }, + "dependencies": { + "pump": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.3.tgz", + "integrity": "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==", + "requires": { + "end-of-stream": "1.4.0", + "once": "1.4.0" + } + } } }, "punycode": { @@ -17358,12 +16992,6 @@ "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=" }, - "quick-lru": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", - "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=", - "dev": true - }, "qunitjs": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/qunitjs/-/qunitjs-2.4.1.tgz", @@ -18099,17 +17727,6 @@ } } }, - "remark": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/remark/-/remark-8.0.0.tgz", - "integrity": "sha512-K0PTsaZvJlXTl9DN6qYlvjTkqSZBFELhROZMrblm2rB+085flN84nz4g/BscKRMqDvhzlK1oQ/xnWQumdeNZYw==", - "dev": true, - "requires": { - "remark-parse": "4.0.0", - "remark-stringify": "4.0.0", - "unified": "6.1.6" - } - }, "remark-parse": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-4.0.0.tgz", @@ -18132,28 +17749,6 @@ "xtend": "4.0.1" } }, - "remark-stringify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-4.0.0.tgz", - "integrity": "sha512-xLuyKTnuQer3ke9hkU38SUYLiTmS078QOnoFavztmbt/pAJtNSkNtFgR0U//uCcmG0qnyxao+PDuatQav46F1w==", - "dev": true, - "requires": { - "ccount": "1.0.2", - "is-alphanumeric": "1.0.0", - "is-decimal": "1.0.1", - "is-whitespace-character": "1.0.1", - "longest-streak": "2.0.2", - "markdown-escapes": "1.0.1", - "markdown-table": "1.1.1", - "mdast-util-compact": "1.0.1", - "parse-entities": "1.1.1", - "repeat-string": "1.6.1", - "state-toggle": "1.0.0", - "stringify-entities": "1.3.1", - "unherit": "1.1.0", - "xtend": "4.0.1" - } - }, "remove-bom-buffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", @@ -19716,15 +19311,6 @@ } } }, - "stream-each": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.2.tgz", - "integrity": "sha512-mc1dbFhGBxvTM3bIWmAAINbqiuAk9TATcfIQC8P+/+HJefgaiTlMn2dHvkX8qlI12KeYKSQ1Ua9RrIqrn1VPoA==", - "requires": { - "end-of-stream": "1.4.0", - "stream-shift": "1.0.0" - } - }, "stream-exhaust": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", @@ -19857,18 +19443,6 @@ "safe-buffer": "5.1.1" } }, - "stringify-entities": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-1.3.1.tgz", - "integrity": "sha1-sVDsLXKsTBtfMktR+2soyc3/BYw=", - "dev": true, - "requires": { - "character-entities-html4": "1.1.1", - "character-entities-legacy": "1.1.1", - "is-alphanumerical": "1.0.1", - "is-hexadecimal": "1.0.1" - } - }, "stringstream": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", @@ -20328,18 +19902,18 @@ } }, "stylelint-config-recommended": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-1.0.0.tgz", - "integrity": "sha512-wp50rY5A6MWndIIkKNNzJv/S58lTvqQEriS7CXTBN1SwtoY/YjHhCLIOkjundLnUWMvJJska6GnciLbs76UQrA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-2.1.0.tgz", + "integrity": "sha512-ajMbivOD7JxdsnlS5945KYhvt7L/HwN6YeYF2BH6kE4UCLJR0YvXMf+2j7nQpJyYLZx9uZzU5G1ZOSBiWAc6yA==", "dev": true }, "stylelint-config-standard": { - "version": "17.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-17.0.0.tgz", - "integrity": "sha512-G8jMZ0KsaVH7leur9XLZVhwOBHZ2vdbuJV8Bgy0ta7/PpBhEHo6fjVDaNchyCGXB5sRcWVq6O9rEU/MvY9cQDQ==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-18.2.0.tgz", + "integrity": "sha512-07x0TaSIzvXlbOioUU4ORkCIM07kyIuojkbSVCyFWNVgXMXYHfhnQSCkqu+oHWJf3YADAnPGWzdJ53NxkoJ7RA==", "dev": true, "requires": { - "stylelint-config-recommended": "1.0.0" + "stylelint-config-recommended": "2.1.0" } }, "stylelint-order": { @@ -20483,6 +20057,15 @@ "babel-core": "6.26.0", "object-assign": "4.1.1" } + }, + "pump": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.3.tgz", + "integrity": "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==", + "requires": { + "end-of-stream": "1.4.0", + "once": "1.4.0" + } } } }, @@ -20515,12 +20098,12 @@ } }, "syntax-error": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.3.0.tgz", - "integrity": "sha1-HtkmbE1AvnXcVb+bsct3Biu5bKE=", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz", + "integrity": "sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==", "dev": true, "requires": { - "acorn": "4.0.13" + "acorn-node": "1.3.0" } }, "table": { @@ -21451,29 +21034,11 @@ "through2-filter": "2.0.0" } }, - "unist-util-find-all-after": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unist-util-find-all-after/-/unist-util-find-all-after-1.0.1.tgz", - "integrity": "sha1-TlUSq/734GFnga7Pex7XUcAK+Qg=", - "dev": true, - "requires": { - "unist-util-is": "2.1.1" - } - }, "unist-util-is": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-2.1.1.tgz", "integrity": "sha1-DDEmKeP5YMZukx6BLT2A53AQlHs=" }, - "unist-util-modify-children": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unist-util-modify-children/-/unist-util-modify-children-1.1.1.tgz", - "integrity": "sha1-ZtfmpEnm9nIguXarPLi166w55R0=", - "dev": true, - "requires": { - "array-iterate": "1.1.1" - } - }, "unist-util-remove-position": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-1.1.1.tgz", @@ -22000,6 +21565,181 @@ "outpipe": "1.1.1", "through2": "2.0.3", "xtend": "4.0.1" + }, + "dependencies": { + "browser-pack": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.0.4.tgz", + "integrity": "sha512-Q4Rvn7P6ObyWfc4stqLWHtG1MJ8vVtjgT24Zbu+8UTzxYuZouqZsmNRRTFVMY/Ux0eIKv1d+JWzsInTX+fdHPQ==", + "dev": true, + "requires": { + "JSONStream": "1.3.2", + "combine-source-map": "0.8.0", + "defined": "1.0.0", + "safe-buffer": "5.1.1", + "through2": "2.0.3", + "umd": "3.0.1" + } + }, + "browserify": { + "version": "14.5.0", + "resolved": "https://registry.npmjs.org/browserify/-/browserify-14.5.0.tgz", + "integrity": "sha512-gKfOsNQv/toWz+60nSPfYzuwSEdzvV2WdxrVPUbPD/qui44rAkB3t3muNtmmGYHqrG56FGwX9SUEQmzNLAeS7g==", + "dev": true, + "requires": { + "JSONStream": "1.3.2", + "assert": "1.4.1", + "browser-pack": "6.0.4", + "browser-resolve": "1.11.2", + "browserify-zlib": "0.2.0", + "buffer": "5.1.0", + "cached-path-relative": "1.0.1", + "concat-stream": "1.5.2", + "console-browserify": "1.1.0", + "constants-browserify": "1.0.0", + "crypto-browserify": "3.12.0", + "defined": "1.0.0", + "deps-sort": "2.0.0", + "domain-browser": "1.1.7", + "duplexer2": "0.1.4", + "events": "1.1.1", + "glob": "7.1.2", + "has": "1.0.1", + "htmlescape": "1.1.1", + "https-browserify": "1.0.0", + "inherits": "2.0.3", + "insert-module-globals": "7.0.2", + "labeled-stream-splicer": "2.0.0", + "module-deps": "4.1.1", + "os-browserify": "0.3.0", + "parents": "1.0.1", + "path-browserify": "0.0.0", + "process": "0.11.10", + "punycode": "1.4.1", + "querystring-es3": "0.2.1", + "read-only-stream": "2.0.0", + "readable-stream": "2.3.3", + "resolve": "1.4.0", + "shasum": "1.0.2", + "shell-quote": "1.6.1", + "stream-browserify": "2.0.1", + "stream-http": "2.7.2", + "string_decoder": "1.0.3", + "subarg": "1.0.0", + "syntax-error": "1.4.0", + "through2": "2.0.3", + "timers-browserify": "1.4.2", + "tty-browserify": "0.0.0", + "url": "0.11.0", + "util": "0.10.3", + "vm-browserify": "0.0.4", + "xtend": "4.0.1" + } + }, + "combine-source-map": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.8.0.tgz", + "integrity": "sha1-pY0N8ELBhvz4IqjoAV9UUNLXmos=", + "dev": true, + "requires": { + "convert-source-map": "1.1.3", + "inline-source-map": "0.6.2", + "lodash.memoize": "3.0.4", + "source-map": "0.5.7" + } + }, + "concat-stream": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz", + "integrity": "sha1-cIl4Yk2FavQaWnQd790mHadSwmY=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.0.6", + "typedarray": "0.0.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "0.10.31", + "util-deprecate": "1.0.2" + } + }, + "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 + } + } + }, + "duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", + "dev": true, + "requires": { + "readable-stream": "2.3.3" + } + }, + "inline-source-map": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz", + "integrity": "sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU=", + "dev": true, + "requires": { + "source-map": "0.5.7" + } + }, + "lodash.memoize": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", + "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=", + "dev": true + }, + "module-deps": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-4.1.1.tgz", + "integrity": "sha1-IyFYM/HaE/1gbMuAh7RIUty4If0=", + "dev": true, + "requires": { + "JSONStream": "1.3.2", + "browser-resolve": "1.11.2", + "cached-path-relative": "1.0.1", + "concat-stream": "1.5.2", + "defined": "1.0.0", + "detective": "4.7.1", + "duplexer2": "0.1.4", + "inherits": "2.0.3", + "parents": "1.0.1", + "readable-stream": "2.3.3", + "resolve": "1.4.0", + "stream-combiner2": "1.1.1", + "subarg": "1.0.0", + "through2": "2.0.3", + "xtend": "4.0.1" + } + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } } }, "watchpack": { diff --git a/package.json b/package.json index fd2554f73..00587ece6 100644 --- a/package.json +++ b/package.json @@ -6,23 +6,24 @@ "scripts": { "start": "npm run dev", "dev": "gulp dev --debug", - "ui": "npm run test:flat:build:states && beefy ui-dev.js:bundle.js --live --open --index=./development/index.html --cwd ./", - "mock": "beefy mock-dev.js:bundle.js --live --open --index=./development/index.html --cwd ./", + "ui": "npm run test:flat:build:states && beefy development/ui-dev.js:bundle.js --live --open --index=./development/index.html --cwd ./", + "mock": "beefy development/mock-dev.js:bundle.js --live --open --index=./development/index.html --cwd ./", "watch": "mocha watch --recursive \"test/unit/**/*.js\"", - "mascara": "gulp build && METAMASK_DEBUG=true node ./mascara/example/server", + "mascara": "gulp build && cross-env METAMASK_DEBUG=true node ./mascara/example/server", "dist": "npm run dist:clear && npm install && gulp dist", "dist:clear": "rm -rf node_modules/eth-contract-metadata && rm -rf node_modules/eth-phishing-detect", "test": "npm run lint && npm run test:coverage && npm run test:integration", - "test:unit": "METAMASK_ENV=test mocha --exit --require babel-core/register --require test/helper.js --recursive \"test/unit/**/*.js\"", - "test:single": "METAMASK_ENV=test mocha --require test/helper.js", - "test:integration": "gulp build:scss && npm run test:flat && npm run test:mascara", + "test:unit": "cross-env METAMASK_ENV=test mocha --exit --require babel-core/register --require test/helper.js --recursive \"test/unit/**/*.js\"", + "test:single": "cross-env METAMASK_ENV=test mocha --require test/helper.js", + "test:integration": "npm run test:integration:build && npm run test:flat && npm run test:mascara", + "test:integration:build": "gulp build:scss", "test:coverage": "nyc npm run test:unit && npm run test:coveralls-upload", "test:coveralls-upload": "if [ $COVERALLS_REPO_TOKEN ]; then nyc report --reporter=text-lcov | coveralls; fi", "test:flat": "npm run test:flat:build && karma start test/flat.conf.js", "test:flat:build": "npm run test:flat:build:ui && npm run test:flat:build:tests", "test:flat:build:tests": "node test/integration/index.js", "test:flat:build:states": "node development/genStates.js", - "test:flat:build:ui": "npm run test:flat:build:states && browserify ./mock-dev.js -o ./development/bundle.js", + "test:flat:build:ui": "npm run test:flat:build:states && browserify ./development/mock-dev.js -o ./development/bundle.js", "test:mascara": "npm run test:mascara:build && karma start test/mascara.conf.js", "test:mascara:build": "mkdir -p dist/mascara && npm run test:mascara:build:ui && npm run test:mascara:build:background && npm run test:mascara:build:tests", "test:mascara:build:ui": "browserify mascara/test/test-ui.js -o dist/mascara/ui.js", @@ -71,6 +72,7 @@ "clone": "^2.1.1", "copy-to-clipboard": "^3.0.8", "debounce": "^1.0.0", + "debounce-stream": "^2.0.0", "deep-extend": "^0.5.0", "detect-node": "^2.0.3", "disc": "^1.3.2", @@ -105,7 +107,7 @@ "fast-levenshtein": "^2.0.6", "fuse.js": "^3.2.0", "gulp": "github:gulpjs/gulp#4.0", - "gulp-autoprefixer": "^4.0.0", + "gulp-autoprefixer": "^5.0.0", "gulp-eslint": "^4.0.0", "gulp-sass": "^3.1.0", "hat": "0.0.3", @@ -125,7 +127,6 @@ "loglevel": "^1.4.1", "metamascara": "^2.0.0", "metamask-logo": "^2.1.2", - "mississippi": "^1.2.0", "mkdirp": "^0.5.1", "multiplex": "^6.7.0", "number-to-bn": "^1.7.0", @@ -139,7 +140,7 @@ "post-message-stream": "^3.0.0", "promise-filter": "^1.1.0", "promise-to-callback": "^1.0.0", - "pump": "^1.0.2", + "pump": "^3.0.0", "pumpify": "^1.3.4", "qrcode-npm": "0.0.3", "ramda": "^0.24.1", @@ -189,10 +190,11 @@ "babelify": "^8.0.0", "beefy": "^2.1.5", "brfs": "^1.4.3", - "browserify": "^14.4.0", + "browserify": "^16.1.1", "chai": "^4.1.0", "compression": "^1.7.1", "coveralls": "^3.0.0", + "cross-env": "^5.1.4", "deep-freeze-strict": "^1.1.1", "del": "^3.0.0", "envify": "^4.0.0", @@ -212,7 +214,7 @@ "gulp-replace": "^0.6.1", "gulp-sourcemaps": "^2.6.0", "gulp-stylefmt": "^1.1.0", - "gulp-stylelint": "^4.0.0", + "gulp-stylelint": "^7.0.0", "gulp-uglify": "^3.0.0", "gulp-uglify-es": "^1.0.1", "gulp-util": "^3.0.7", @@ -244,7 +246,7 @@ "react-testutils-additions": "^15.2.0", "redux-test-utils": "^0.2.2", "sinon": "^4.0.0", - "stylelint-config-standard": "^17.0.0", + "stylelint-config-standard": "^18.2.0", "tape": "^4.5.1", "testem": "^2.0.0", "uglifyify": "^4.0.2", diff --git a/test/base.conf.js b/test/base.conf.js index 82b9d8eec..adb5357e8 100644 --- a/test/base.conf.js +++ b/test/base.conf.js @@ -46,7 +46,9 @@ module.exports = function(config) { // start these browsers // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher - browsers: ['Chrome', 'Firefox'], + browsers: process.env.browsers ? + JSON.parse(process.env.browsers) + : ['Chrome', 'Firefox'], // Continuous Integration mode // if true, Karma captures browsers, runs the tests and exits diff --git a/test/integration/index.js b/test/integration/index.js index 144303dbb..b266ddf37 100644 --- a/test/integration/index.js +++ b/test/integration/index.js @@ -23,4 +23,4 @@ pump( console.log(`Integration test build completed: "${bundlePath}"`) process.exit(0) } -) \ No newline at end of file +) diff --git a/test/integration/lib/add-token.js b/test/integration/lib/add-token.js index dd4251cc4..42ed28dca 100644 --- a/test/integration/lib/add-token.js +++ b/test/integration/lib/add-token.js @@ -1,4 +1,9 @@ const reactTriggerChange = require('react-trigger-change') +const { + timeout, + queryAsync, + findAsync, +} = require('../../lib/util') QUnit.module('Add token flow') @@ -13,74 +18,60 @@ QUnit.test('successful add token flow', (assert) => { }) async function runAddTokenFlowTest (assert, done) { - const selectState = $('select') + const selectState = await queryAsync($, 'select') selectState.val('add token') reactTriggerChange(selectState[0]) - await timeout(2000) - // Check that no tokens have been added assert.ok($('.token-list-item').length === 0, 'no tokens added') // Go to Add Token screen - let addTokenButton = $('button.btn-clear.wallet-view__add-token-button') + let addTokenButton = await queryAsync($, 'button.btn-clear.wallet-view__add-token-button') assert.ok(addTokenButton[0], 'add token button present') addTokenButton[0].click() - await timeout(1000) - // Verify Add Token screen - let addTokenWrapper = $('.add-token__wrapper') + let addTokenWrapper = await queryAsync($, '.add-token__wrapper') assert.ok(addTokenWrapper[0], 'add token wrapper renders') - let addTokenTitle = $('.add-token__title') + let addTokenTitle = await queryAsync($, '.add-token__title') assert.equal(addTokenTitle[0].textContent, 'Add Token', 'add token title is correct') // Cancel Add Token - const cancelAddTokenButton = $('button.btn-cancel.add-token__button') + const cancelAddTokenButton = await queryAsync($, 'button.btn-cancel.add-token__button') assert.ok(cancelAddTokenButton[0], 'cancel add token button present') cancelAddTokenButton.click() - await timeout(1000) - assert.ok($('.wallet-view')[0], 'cancelled and returned to account detail wallet view') // Return to Add Token Screen - addTokenButton = $('button.btn-clear.wallet-view__add-token-button') + addTokenButton = await queryAsync($, 'button.btn-clear.wallet-view__add-token-button') assert.ok(addTokenButton[0], 'add token button present') addTokenButton[0].click() - await timeout(1000) - // Verify Add Token Screen - addTokenWrapper = $('.add-token__wrapper') - addTokenTitle = $('.add-token__title') + addTokenWrapper = await queryAsync($, '.add-token__wrapper') + addTokenTitle = await queryAsync($, '.add-token__title') assert.ok(addTokenWrapper[0], 'add token wrapper renders') assert.equal(addTokenTitle[0].textContent, 'Add Token', 'add token title is correct') // Search for token - const searchInput = $('input.add-token__input') + const searchInput = await queryAsync($, 'input.add-token__input') searchInput.val('a') reactTriggerChange(searchInput[0]) - await timeout() - // Click token to add - const tokenWrapper = $('div.add-token__token-wrapper') + const tokenWrapper = await queryAsync($, 'div.add-token__token-wrapper') assert.ok(tokenWrapper[0], 'token found') const tokenImageProp = tokenWrapper.find('.add-token__token-icon').css('background-image') const tokenImageUrl = tokenImageProp.slice(5, -2) tokenWrapper[0].click() - await timeout() - // Click Next button - let nextButton = $('button.btn-clear.add-token__button') + let nextButton = await queryAsync($, 'button.btn-clear.add-token__button') assert.equal(nextButton[0].textContent, 'Next', 'next button rendered') nextButton[0].click() - await timeout() - // Confirm Add token assert.equal( $('.add-token__description')[0].textContent, @@ -90,47 +81,35 @@ async function runAddTokenFlowTest (assert, done) { assert.ok($('button.btn-clear.add-token__button')[0], 'confirm add token button found') $('button.btn-clear.add-token__button')[0].click() - await timeout(2000) - // Verify added token image - let heroBalance = $('.hero-balance') + let heroBalance = await queryAsync($, '.hero-balance') assert.ok(heroBalance, 'rendered hero balance') assert.ok(tokenImageUrl.indexOf(heroBalance.find('img').attr('src')) > -1, 'token added') // Return to Add Token Screen - addTokenButton = $('button.btn-clear.wallet-view__add-token-button') + addTokenButton = await queryAsync($, 'button.btn-clear.wallet-view__add-token-button') assert.ok(addTokenButton[0], 'add token button present') addTokenButton[0].click() - await timeout(1000) - - const addCustom = $('.add-token__add-custom') + const addCustom = await queryAsync($, '.add-token__add-custom') assert.ok(addCustom[0], 'add custom token button present') addCustom[0].click() - await timeout() - // Input token contract address - const customInput = $('input.add-token__add-custom-input') + const customInput = await queryAsync($, 'input.add-token__add-custom-input') customInput.val('0x177af043D3A1Aed7cc5f2397C70248Fc6cDC056c') reactTriggerChange(customInput[0]) - await timeout(1000) - // Click Next button - nextButton = $('button.btn-clear.add-token__button') + nextButton = await queryAsync($, 'button.btn-clear.add-token__button') assert.equal(nextButton[0].textContent, 'Next', 'next button rendered') nextButton[0].click() - await timeout(1000) - // Verify symbol length error since contract address won't return symbol - const errorMessage = $('.add-token__add-custom-error-message') + const errorMessage = await queryAsync($, '.add-token__add-custom-error-message') assert.ok(errorMessage[0], 'error rendered') $('button.btn-cancel.add-token__button')[0].click() - await timeout(2000) - // // Confirm Add token // assert.equal( // $('.add-token__description')[0].textContent, @@ -141,13 +120,7 @@ async function runAddTokenFlowTest (assert, done) { // $('button.btn-clear.add-token__button')[0].click() // // Verify added token image - // heroBalance = $('.hero-balance') + // heroBalance = await queryAsync($, '.hero-balance') // assert.ok(heroBalance, 'rendered hero balance') // assert.ok(heroBalance.find('.identicon')[0], 'token added') } - -function timeout (time) { - return new Promise((resolve, reject) => { - setTimeout(resolve, time || 1500) - }) -} diff --git a/test/integration/lib/confirm-sig-requests.js b/test/integration/lib/confirm-sig-requests.js index e49424c37..9737a2283 100644 --- a/test/integration/lib/confirm-sig-requests.js +++ b/test/integration/lib/confirm-sig-requests.js @@ -1,5 +1,9 @@ const reactTriggerChange = require('react-trigger-change') - +const { + timeout, + queryAsync, + findAsync, +} = require('../../lib/util') const PASSWORD = 'password123' QUnit.module('confirm sig requests') @@ -13,55 +17,41 @@ QUnit.test('successful confirmation of sig requests', (assert) => { }) async function runConfirmSigRequestsTest(assert, done) { - let selectState = $('select') + let selectState = await queryAsync($, 'select') selectState.val('confirm sig requests') reactTriggerChange(selectState[0]) - await timeout(2000) - - let confirmSigHeadline = $('.request-signature__headline') + let confirmSigHeadline = await queryAsync($, '.request-signature__headline') assert.equal(confirmSigHeadline[0].textContent, 'Your signature is being requested') - let confirmSigRowValue = $('.request-signature__row-value') + let confirmSigRowValue = await queryAsync($, '.request-signature__row-value') assert.ok(confirmSigRowValue[0].textContent.match(/^\#\sTerms\sof\sUse/)) - let confirmSigSignButton = $('.request-signature__footer__sign-button') + let confirmSigSignButton = await queryAsync($, '.request-signature__footer__sign-button') confirmSigSignButton[0].click() - await timeout(2000) - - confirmSigHeadline = $('.request-signature__headline') + confirmSigHeadline = await queryAsync($, '.request-signature__headline') assert.equal(confirmSigHeadline[0].textContent, 'Your signature is being requested') - let confirmSigMessage = $('.request-signature__notice') + let confirmSigMessage = await queryAsync($, '.request-signature__notice') assert.ok(confirmSigMessage[0].textContent.match(/^Signing\sthis\smessage/)) - confirmSigRowValue = $('.request-signature__row-value') + confirmSigRowValue = await queryAsync($, '.request-signature__row-value') assert.equal(confirmSigRowValue[0].textContent, '0x879a053d4800c6354e76c7985a865d2922c82fb5b3f4577b2fe08b998954f2e0') - confirmSigSignButton = $('.request-signature__footer__sign-button') + confirmSigSignButton = await queryAsync($, '.request-signature__footer__sign-button') confirmSigSignButton[0].click() - await timeout(2000) - - confirmSigHeadline = $('.request-signature__headline') + confirmSigHeadline = await queryAsync($, '.request-signature__headline') assert.equal(confirmSigHeadline[0].textContent, 'Your signature is being requested') - confirmSigRowValue = $('.request-signature__row-value') + confirmSigRowValue = await queryAsync($, '.request-signature__row-value') assert.equal(confirmSigRowValue[0].textContent, 'Hi, Alice!') assert.equal(confirmSigRowValue[1].textContent, '1337') - confirmSigSignButton = $('.request-signature__footer__sign-button') + confirmSigSignButton = await queryAsync($, '.request-signature__footer__sign-button') confirmSigSignButton[0].click() - await timeout(2000) - - const txView = $('.tx-view') + const txView = await queryAsync($, '.tx-view') assert.ok(txView[0], 'Should return to the account details screen after confirming') } - -function timeout (time) { - return new Promise((resolve, reject) => { - setTimeout(resolve, time || 1500) - }) -} \ No newline at end of file diff --git a/test/integration/lib/first-time.js b/test/integration/lib/first-time.js index 764eae47c..052d89518 100644 --- a/test/integration/lib/first-time.js +++ b/test/integration/lib/first-time.js @@ -1,6 +1,10 @@ const reactTriggerChange = require('react-trigger-change') const PASSWORD = 'password123' const runMascaraFirstTimeTest = require('./mascara-first-time') +const { + timeout, + findAsync, +} = require('../../lib/util') QUnit.module('first time usage') @@ -21,20 +25,19 @@ async function runFirstTimeUsageTest(assert, done) { selectState.val('first time') reactTriggerChange(selectState[0]) - await timeout(2000) const app = $('#app-content') // recurse notices while (true) { - const button = app.find('button') + const button = await findAsync(app, 'button') if (button.html() === 'Accept') { // still notices to accept - const termsPage = app.find('.markdown')[0] + const termsPageRaw = await findAsync(app, '.markdown') + const termsPage = (await findAsync(app, '.markdown'))[0] + console.log('termsPageRaw', termsPageRaw) termsPage.scrollTop = termsPage.scrollHeight - await timeout() console.log('Clearing notice') button.click() - await timeout() } else { // exit loop console.log('No more notices...') @@ -42,97 +45,68 @@ async function runFirstTimeUsageTest(assert, done) { } } - await timeout() - // Scroll through terms - const title = app.find('h1')[0] + const title = (await findAsync(app, 'h1'))[0] assert.equal(title.textContent, 'MetaMask', 'title screen') // enter password - const pwBox = app.find('#password-box')[0] - const confBox = app.find('#password-box-confirm')[0] + const pwBox = (await findAsync(app, '#password-box'))[0] + const confBox = (await findAsync(app, '#password-box-confirm'))[0] pwBox.value = PASSWORD confBox.value = PASSWORD - await timeout() - // create vault - const createButton = app.find('button.primary')[0] + const createButton = (await findAsync(app, 'button.primary'))[0] createButton.click() - await timeout(3000) - - const created = app.find('h3')[0] + await timeout() + const created = (await findAsync(app, 'h3'))[0] assert.equal(created.textContent, 'Vault Created', 'Vault created screen') // Agree button - const button = app.find('button')[0] + const button = (await findAsync(app, 'button'))[0] assert.ok(button, 'button present') button.click() - await timeout(1000) - - const detail = app.find('.account-detail-section')[0] + const detail = (await findAsync(app, '.account-detail-section'))[0] assert.ok(detail, 'Account detail section loaded.') - const sandwich = app.find('.sandwich-expando')[0] + const sandwich = (await findAsync(app, '.sandwich-expando'))[0] sandwich.click() - await timeout() - - const menu = app.find('.menu-droppo')[0] + const menu = (await findAsync(app, '.menu-droppo'))[0] const children = menu.children const logout = children[2] assert.ok(logout, 'Lock menu item found') logout.click() - await timeout(1000) - - const pwBox2 = app.find('#password-box')[0] + const pwBox2 = (await findAsync(app, '#password-box'))[0] pwBox2.value = PASSWORD - const createButton2 = app.find('button.primary')[0] + const createButton2 = (await findAsync(app, 'button.primary'))[0] createButton2.click() - await timeout(1000) - - const detail2 = app.find('.account-detail-section')[0] + const detail2 = (await findAsync(app, '.account-detail-section'))[0] assert.ok(detail2, 'Account detail section loaded again.') - await timeout() - // open account settings dropdown - const qrButton = app.find('.fa.fa-ellipsis-h')[0] + const qrButton = (await findAsync(app, '.fa.fa-ellipsis-h'))[0] qrButton.click() - await timeout(1000) - // qr code item - const qrButton2 = app.find('.dropdown-menu-item')[1] + const qrButton2 = (await findAsync(app, '.dropdown-menu-item'))[1] qrButton2.click() - await timeout(1000) - - const qrHeader = app.find('.qr-header')[0] - const qrContainer = app.find('#qr-container')[0] + const qrHeader = (await findAsync(app, '.qr-header'))[0] + const qrContainer = (await findAsync(app, '#qr-container'))[0] assert.equal(qrHeader.textContent, 'Account 1', 'Should show account label.') assert.ok(qrContainer, 'QR Container found') - await timeout() - - const networkMenu = app.find('.network-indicator')[0] + const networkMenu = (await findAsync(app, '.network-indicator'))[0] networkMenu.click() - await timeout() - - const networkMenu2 = app.find('.network-indicator')[0] + const networkMenu2 = (await findAsync(app, '.network-indicator'))[0] const children2 = networkMenu2.children children2.length[3] assert.ok(children2, 'All network options present') } - -function timeout (time) { - return new Promise((resolve, reject) => { - setTimeout(resolve, time || 1500) - }) -} \ No newline at end of file diff --git a/test/integration/lib/mascara-first-time.js b/test/integration/lib/mascara-first-time.js index 515c7f383..bcbc94ff6 100644 --- a/test/integration/lib/mascara-first-time.js +++ b/test/integration/lib/mascara-first-time.js @@ -1,119 +1,95 @@ const PASSWORD = 'password123' const reactTriggerChange = require('react-trigger-change') +const { + timeout, + findAsync, + queryAsync, +} = require('../../lib/util') async function runFirstTimeUsageTest (assert, done) { await timeout(4000) - const app = $('#app-content') + const app = await queryAsync($, '#app-content') await skipNotices(app) - await timeout() - // Scroll through terms - const title = app.find('.create-password__title').text() + const title = (await findAsync(app, '.create-password__title')).text() assert.equal(title, 'Create Password', 'create password screen') // enter password - const pwBox = app.find('.first-time-flow__input')[0] - const confBox = app.find('.first-time-flow__input')[1] + const pwBox = (await findAsync(app, '.first-time-flow__input'))[0] + const confBox = (await findAsync(app, '.first-time-flow__input'))[1] pwBox.value = PASSWORD confBox.value = PASSWORD reactTriggerChange(pwBox) reactTriggerChange(confBox) - - await timeout() - // Create Password - const createButton = app.find('button.first-time-flow__button')[0] + const createButton = (await findAsync(app, 'button.first-time-flow__button'))[0] createButton.click() - await timeout(3000) - - const created = app.find('.unique-image__title')[0] + const created = (await findAsync(app, '.unique-image__title'))[0] assert.equal(created.textContent, 'Your unique account image', 'unique image screen') // Agree button - let button = app.find('button')[0] + let button = (await findAsync(app, 'button'))[0] assert.ok(button, 'button present') button.click() - await timeout(1000) - await skipNotices(app) // secret backup phrase - const seedTitle = app.find('.backup-phrase__title')[0] + const seedTitle = (await findAsync(app, '.backup-phrase__title'))[0] assert.equal(seedTitle.textContent, 'Secret Backup Phrase', 'seed phrase screen') - app.find('.backup-phrase__reveal-button').click() - - await timeout(1000) - const seedPhrase = app.find('.backup-phrase__secret-words').text().split(' ') - app.find('.first-time-flow__button').click() + ;(await findAsync(app, '.backup-phrase__reveal-button')).click() + const seedPhrase = (await findAsync(app, '.backup-phrase__secret-words')).text().split(' ') + ;(await findAsync(app, '.first-time-flow__button')).click() + await timeout() const selectPhrase = text => { const option = $('.backup-phrase__confirm-seed-option') .filter((i, d) => d.textContent === text)[0] - $(option).click() } - await timeout(1000) - seedPhrase.forEach(sp => selectPhrase(sp)) - app.find('.first-time-flow__button').click() - await timeout(1000) + ;(await findAsync(app, '.first-time-flow__button')).click() // Deposit Ether Screen - const buyEthTitle = app.find('.buy-ether__title')[0] + const buyEthTitle = (await findAsync(app, '.buy-ether__title'))[0] assert.equal(buyEthTitle.textContent, 'Deposit Ether', 'deposit ether screen') - app.find('.buy-ether__do-it-later').click() - await timeout(1000) + ;(await findAsync(app, '.buy-ether__do-it-later')).click() - const menu = app.find('.account-menu__icon')[0] + const menu = (await findAsync(app, '.account-menu__icon'))[0] menu.click() - await timeout() - - const lock = app.find('.account-menu__logout-button')[0] + const lock = (await findAsync(app, '.account-menu__logout-button'))[0] assert.ok(lock, 'Lock menu item found') lock.click() - await timeout(1000) - - const pwBox2 = app.find('#password-box')[0] + const pwBox2 = (await findAsync(app, '#password-box'))[0] pwBox2.value = PASSWORD - const createButton2 = app.find('button.primary')[0] + const createButton2 = (await findAsync(app, 'button.primary'))[0] createButton2.click() - await timeout(1000) - - const detail2 = app.find('.wallet-view')[0] + const detail2 = (await findAsync(app, '.wallet-view'))[0] assert.ok(detail2, 'Account detail section loaded again.') - await timeout() - // open account settings dropdown - const qrButton = app.find('.wallet-view__details-button')[0] + const qrButton = (await findAsync(app, '.wallet-view__details-button'))[0] qrButton.click() - await timeout(1000) - - const qrHeader = app.find('.editable-label__value')[0] - const qrContainer = app.find('.qr-wrapper')[0] + const qrHeader = (await findAsync(app, '.editable-label__value'))[0] + const qrContainer = (await findAsync(app, '.qr-wrapper'))[0] assert.equal(qrHeader.textContent, 'Account 1', 'Should show account label.') assert.ok(qrContainer, 'QR Container found') - await timeout() - - const networkMenu = app.find('.network-component')[0] + const networkMenu = (await findAsync(app, '.network-component'))[0] networkMenu.click() - await timeout() - - const networkMenu2 = app.find('.network-indicator')[0] + const networkMenu2 = (await findAsync(app, '.network-indicator'))[0] const children2 = networkMenu2.children children2.length[3] assert.ok(children2, 'All network options present') @@ -121,18 +97,12 @@ async function runFirstTimeUsageTest (assert, done) { module.exports = runFirstTimeUsageTest -function timeout (time) { - return new Promise((resolve, reject) => { - setTimeout(resolve, time || 1500) - }) -} - async function skipNotices (app) { while (true) { - const button = app.find('button') + const button = await findAsync(app, 'button') if (button && button.html() === 'Accept') { // still notices to accept - const termsPage = app.find('.markdown')[0] + const termsPage = (await findAsync(app, '.markdown'))[0] if (!termsPage) { break } diff --git a/test/integration/lib/send-new-ui.js b/test/integration/lib/send-new-ui.js index 3456f2367..faab10fdf 100644 --- a/test/integration/lib/send-new-ui.js +++ b/test/integration/lib/send-new-ui.js @@ -1,4 +1,9 @@ const reactTriggerChange = require('react-trigger-change') +const { + timeout, + queryAsync, + findAsync, +} = require('../../lib/util') const PASSWORD = 'password123' @@ -18,83 +23,65 @@ global.ethQuery = { async function runSendFlowTest(assert, done) { console.log('*** start runSendFlowTest') - const selectState = $('select') + const selectState = await queryAsync($, 'select') selectState.val('send new ui') reactTriggerChange(selectState[0]) - await timeout(2000) - - const sendScreenButton = $('button.btn-clear.hero-balance-button') + const sendScreenButton = await queryAsync($, 'button.btn-clear.hero-balance-button') assert.ok(sendScreenButton[1], 'send screen button present') sendScreenButton[1].click() - await timeout(1000) - - const sendTitle = $('.page-container__title') + const sendTitle = await queryAsync($, '.page-container__title') assert.equal(sendTitle[0].textContent, 'Send ETH', 'Send screen title is correct') - const sendCopy = $('.page-container__subtitle') + const sendCopy = await queryAsync($, '.page-container__subtitle') assert.equal(sendCopy[0].textContent, 'Only send ETH to an Ethereum address.', 'Send screen has copy') - const sendFromField = $('.send-v2__form-field') + const sendFromField = await queryAsync($, '.send-v2__form-field') assert.ok(sendFromField[0], 'send screen has a from field') - let sendFromFieldItemAddress = $('.account-list-item__account-name') + let sendFromFieldItemAddress = await queryAsync($, '.account-list-item__account-name') assert.equal(sendFromFieldItemAddress[0].textContent, 'Send Account 4', 'send from field shows correct account name') - const sendFromFieldItem = $('.account-list-item') + const sendFromFieldItem = await queryAsync($, '.account-list-item') sendFromFieldItem[0].click() - await timeout() - - const sendFromDropdownList = $('.send-v2__from-dropdown__list') + // this seems to fail if the firefox window is not in focus... + const sendFromDropdownList = await queryAsync($, '.send-v2__from-dropdown__list') assert.equal(sendFromDropdownList.children().length, 4, 'send from dropdown shows all accounts') - console.log(`!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! sendFromDropdownList.children()[1]`, sendFromDropdownList.children()[1]); sendFromDropdownList.children()[1].click() - await timeout() - - sendFromFieldItemAddress = $('.account-list-item__account-name') - console.log(`!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! sendFromFieldItemAddress[0]`, sendFromFieldItemAddress[0]); + sendFromFieldItemAddress = await queryAsync($, '.account-list-item__account-name') assert.equal(sendFromFieldItemAddress[0].textContent, 'Send Account 2', 'send from field dropdown changes account name') - let sendToFieldInput = $('.send-v2__to-autocomplete__input') + let sendToFieldInput = await queryAsync($, '.send-v2__to-autocomplete__input') sendToFieldInput[0].focus() - await timeout() - - const sendToDropdownList = $('.send-v2__from-dropdown__list') + const sendToDropdownList = await queryAsync($, '.send-v2__from-dropdown__list') assert.equal(sendToDropdownList.children().length, 5, 'send to dropdown shows all accounts and address book accounts') sendToDropdownList.children()[2].click() - await timeout() - const sendToAccountAddress = sendToFieldInput.val() assert.equal(sendToAccountAddress, '0x2f8d4a878cfa04a6e60d46362f5644deab66572d', 'send to dropdown selects the correct address') - const sendAmountField = $('.send-v2__form-row:eq(2)') + const sendAmountField = await queryAsync($, '.send-v2__form-row:eq(2)') sendAmountField.find('.currency-display')[0].click() - await timeout() - - const sendAmountFieldInput = sendAmountField.find('input:text') + const sendAmountFieldInput = await findAsync(sendAmountField, 'input:text') sendAmountFieldInput.val('5.1') reactTriggerChange(sendAmountField.find('input')[0]) - await timeout() - - let errorMessage = $('.send-v2__error') + let errorMessage = await queryAsync($, '.send-v2__error') assert.equal(errorMessage[0].textContent, 'Insufficient funds.', 'send should render an insufficient fund error message') sendAmountFieldInput.val('2.0') reactTriggerChange(sendAmountFieldInput[0]) - await timeout() errorMessage = $('.send-v2__error') assert.equal(errorMessage.length, 0, 'send should stop rendering amount error message after amount is corrected') - const sendGasField = $('.send-v2__gas-fee-display') + const sendGasField = await queryAsync($, '.send-v2__gas-fee-display') assert.equal( sendGasField.find('.currency-display__input-wrapper > input').val(), '0.000198', @@ -106,120 +93,86 @@ async function runSendFlowTest(assert, done) { 'send gas field should show estimated gas total converted to USD' ) - const sendGasOpenCustomizeModalButton = $('.send-v2__sliders-icon-container' - ) + const sendGasOpenCustomizeModalButton = await queryAsync($, '.send-v2__sliders-icon-container') sendGasOpenCustomizeModalButton[0].click() - await timeout(1000) - - const customizeGasModal = $('.send-v2__customize-gas') + const customizeGasModal = await queryAsync($, '.send-v2__customize-gas') assert.ok(customizeGasModal[0], 'should render the customize gas modal') - const customizeGasPriceInput = $('.send-v2__gas-modal-card').first().find('input') + const customizeGasPriceInput = (await queryAsync($, '.send-v2__gas-modal-card')).first().find('input') customizeGasPriceInput.val(50) reactTriggerChange(customizeGasPriceInput[0]) - const customizeGasLimitInput = $('.send-v2__gas-modal-card').last().find('input') + const customizeGasLimitInput = (await queryAsync($, '.send-v2__gas-modal-card')).last().find('input') customizeGasLimitInput.val(60000) reactTriggerChange(customizeGasLimitInput[0]) - await timeout() - - const customizeGasSaveButton = $('.send-v2__customize-gas__save') + const customizeGasSaveButton = await queryAsync($, '.send-v2__customize-gas__save') customizeGasSaveButton[0].click() - await timeout() - assert.equal( - sendGasField.find('.currency-display__input-wrapper > input').val(), + (await findAsync(sendGasField, '.currency-display__input-wrapper > input')).val(), '0.003', 'send gas field should show customized gas total' ) assert.equal( - sendGasField.find('.currency-display__converted-value')[0].textContent, + (await findAsync(sendGasField, '.currency-display__converted-value'))[0].textContent, '3.60 USD', 'send gas field should show customized gas total converted to USD' ) - const sendButton = $('button.btn-clear.page-container__footer-button') + const sendButton = await queryAsync($, 'button.btn-clear.page-container__footer-button') assert.equal(sendButton[0].textContent, 'Next', 'next button rendered') sendButton[0].click() - - await timeout(2000) + await timeout() selectState.val('send edit') reactTriggerChange(selectState[0]) - await timeout(2000) - - const confirmFromName = $('.confirm-screen-account-name').first() + const confirmFromName = (await queryAsync($, '.confirm-screen-account-name')).first() assert.equal(confirmFromName[0].textContent, 'Send Account 2', 'confirm screen should show correct from name') - const confirmToName = $('.confirm-screen-account-name').last() + const confirmToName = (await queryAsync($, '.confirm-screen-account-name')).last() assert.equal(confirmToName[0].textContent, 'Send Account 3', 'confirm screen should show correct to name') - const confirmScreenRows = $('.confirm-screen-rows') + const confirmScreenRows = await queryAsync($, '.confirm-screen-rows') const confirmScreenGas = confirmScreenRows.find('.confirm-screen-row-info')[2] assert.equal(confirmScreenGas.textContent, '3.6 USD', 'confirm screen should show correct gas') const confirmScreenTotal = confirmScreenRows.find('.confirm-screen-row-info')[3] assert.equal(confirmScreenTotal.textContent, '2405.36 USD', 'confirm screen should show correct total') - const confirmScreenBackButton = $('.confirm-screen-back-button') + const confirmScreenBackButton = await queryAsync($, '.confirm-screen-back-button') confirmScreenBackButton[0].click() - await timeout(1000) - - const sendFromFieldItemInEdit = $('.account-list-item') + const sendFromFieldItemInEdit = await queryAsync($, '.account-list-item') sendFromFieldItemInEdit[0].click() - await timeout() - - const sendFromDropdownListInEdit = $('.send-v2__from-dropdown__list') + const sendFromDropdownListInEdit = await queryAsync($, '.send-v2__from-dropdown__list') sendFromDropdownListInEdit.children()[2].click() - await timeout() - - const sendToFieldInputInEdit = $('.send-v2__to-autocomplete__input') + const sendToFieldInputInEdit = await queryAsync($, '.send-v2__to-autocomplete__input') sendToFieldInputInEdit[0].focus() sendToFieldInputInEdit.val('0xd85a4b6a394794842887b8284293d69163007bbb') - await timeout() - - const sendAmountFieldInEdit = $('.send-v2__form-row:eq(2)') + const sendAmountFieldInEdit = await queryAsync($, '.send-v2__form-row:eq(2)') sendAmountFieldInEdit.find('.currency-display')[0].click() - await timeout() - const sendAmountFieldInputInEdit = sendAmountFieldInEdit.find('input:text') sendAmountFieldInputInEdit.val('1.0') reactTriggerChange(sendAmountFieldInputInEdit[0]) - await timeout() - - const sendButtonInEdit = $('.btn-clear.page-container__footer-button') + const sendButtonInEdit = await queryAsync($, '.btn-clear.page-container__footer-button') assert.equal(sendButtonInEdit[0].textContent, 'Next', 'next button in edit rendered') sendButtonInEdit[0].click() - await timeout() - // TODO: Need a way to mock background so that we can test correct transition from editing to confirm selectState.val('confirm new ui') reactTriggerChange(selectState[0]) - - await timeout(2000) - const confirmScreenConfirmButton = $('.confirm-screen-confirm-button') + const confirmScreenConfirmButton = await queryAsync($, '.confirm-screen-confirm-button') console.log(`+++++++++++++++++++++++++++++++= confirmScreenConfirmButton[0]`, confirmScreenConfirmButton[0]); confirmScreenConfirmButton[0].click() - await timeout(2000) - - const txView = $('.tx-view') + const txView = await queryAsync($, '.tx-view') console.log(`++++++++++++++++++++++++++++++++ txView[0]`, txView[0]); assert.ok(txView[0], 'Should return to the account details screen after confirming') } - -function timeout (time) { - return new Promise((resolve, reject) => { - setTimeout(resolve, time || 1500) - }) -} \ No newline at end of file diff --git a/test/lib/util.js b/test/lib/util.js new file mode 100644 index 000000000..626280745 --- /dev/null +++ b/test/lib/util.js @@ -0,0 +1,53 @@ +module.exports = { + timeout, + queryAsync, + findAsync, + pollUntilTruthy, +} + +function timeout (time) { + return new Promise((resolve, reject) => { + setTimeout(resolve, time || 1500) + }) +} + +async function findAsync(container, selector, opts) { + try { + return await pollUntilTruthy(() => { + const result = container.find(selector) + if (result.length > 0) return result + }, opts) + } catch (err) { + throw new Error(`Failed to find element within interval: "${selector}"`) + } +} + +async function queryAsync(jQuery, selector, opts) { + try { + return await pollUntilTruthy(() => { + const result = jQuery(selector) + if (result.length > 0) return result + }, opts) + } catch (err) { + throw new Error(`Failed to find element within interval: "${selector}"`) + } +} + +async function pollUntilTruthy(fn, opts = {}){ + const pollingInterval = opts.pollingInterval || 100 + const timeoutInterval = opts.timeoutInterval || 5000 + const start = Date.now() + let result + while (!result) { + // check if timedout + const now = Date.now() + if ((now - start) > timeoutInterval) { + throw new Error(`pollUntilTruthy - failed to return truthy within interval`) + } + // check for result + result = fn() + // run again after timeout + await timeout(pollingInterval, timeoutInterval) + } + return result +} diff --git a/test/unit/network-contoller-test.js b/test/unit/network-contoller-test.js index 51ad09f87..dd0b52365 100644 --- a/test/unit/network-contoller-test.js +++ b/test/unit/network-contoller-test.js @@ -15,11 +15,8 @@ describe('# Network Controller', function () { beforeEach(function () { - nock('https://api.infura.io') - .get('/*/') - .reply(200) - nock('https://rinkeby.infura.io') + .persist() .post('/metamask') .reply(200) @@ -29,6 +26,11 @@ describe('# Network Controller', function () { networkController.initializeProvider(networkControllerProviderInit, provider) }) + + afterEach(function () { + nock.cleanAll() + }) + describe('network', function () { describe('#provider', function () { it('provider should be updatable without reassignment', function () { diff --git a/ui/app/accounts/import/index.js b/ui/app/accounts/import/index.js index adb52db74..c1b190e3d 100644 --- a/ui/app/accounts/import/index.js +++ b/ui/app/accounts/import/index.js @@ -2,6 +2,7 @@ const inherits = require('util').inherits const Component = require('react').Component const h = require('react-hyperscript') const connect = require('react-redux').connect +const t = require('../../../i18n') import Select from 'react-select' // Subviews @@ -9,8 +10,8 @@ const JsonImportView = require('./json.js') const PrivateKeyImportView = require('./private-key.js') const menuItems = [ - 'Private Key', - 'JSON File', + t('privateKey'), + t('jsonFile'), ] module.exports = connect(mapStateToProps)(AccountImportSubview) @@ -85,9 +86,9 @@ AccountImportSubview.prototype.renderImportView = function () { const current = type || menuItems[0] switch (current) { - case 'Private Key': + case t('privateKey'): return h(PrivateKeyImportView) - case 'JSON File': + case t('jsonFile'): return h(JsonImportView) default: return h(JsonImportView) diff --git a/ui/app/accounts/import/json.js b/ui/app/accounts/import/json.js index dd5dfad22..1b5e485d7 100644 --- a/ui/app/accounts/import/json.js +++ b/ui/app/accounts/import/json.js @@ -1,106 +1,131 @@ -const inherits = require('util').inherits const Component = require('react').Component +const PropTypes = require('prop-types') const h = require('react-hyperscript') const connect = require('react-redux').connect const actions = require('../../actions') const FileInput = require('react-simple-file-input').default +const t = require('../../../i18n') const HELP_LINK = 'https://support.metamask.io/kb/article/7-importing-accounts' -module.exports = connect(mapStateToProps)(JsonImportSubview) +class JsonImportSubview extends Component { + constructor (props) { + super(props) -function mapStateToProps (state) { + this.state = { + file: null, + fileContents: '', + } + } + + render () { + const { error } = this.props + + return ( + h('div.new-account-import-form__json', [ + + h('p', t('usedByClients')), + h('a.warning', { + href: HELP_LINK, + target: '_blank', + }, t('fileImportFail')), + + h(FileInput, { + readAs: 'text', + onLoad: this.onLoad.bind(this), + style: { + margin: '20px 0px 12px 34%', + fontSize: '15px', + display: 'flex', + justifyContent: 'center', + }, + }), + + h('input.new-account-import-form__input-password', { + type: 'password', + placeholder: t('enterPassword'), + id: 'json-password-box', + onKeyPress: this.createKeyringOnEnter.bind(this), + }), + + h('div.new-account-create-form__buttons', {}, [ + + h('button.new-account-create-form__button-cancel', { + onClick: () => this.props.goHome(), + }, [ + t('cancel'), + ]), + + h('button.new-account-create-form__button-create', { + onClick: () => this.createNewKeychain(), + }, [ + t('import'), + ]), + + ]), + + error ? h('span.error', error) : null, + ]) + ) + } + + onLoad (event, file) { + this.setState({file: file, fileContents: event.target.result}) + } + + createKeyringOnEnter (event) { + if (event.key === 'Enter') { + event.preventDefault() + this.createNewKeychain() + } + } + + createNewKeychain () { + const state = this.state + + if (!state) { + const message = 'You must select a valid file to import.' + return this.props.displayWarning(message) + } + + const { fileContents } = state + + if (!fileContents) { + const message = t('needImportFile') + return this.props.displayWarning(message) + } + + const passwordInput = document.getElementById('json-password-box') + const password = passwordInput.value + + if (!password) { + const message = t('needImportPassword') + return this.props.displayWarning(message) + } + + this.props.importNewJsonAccount([ fileContents, password ]) + } +} + +JsonImportSubview.propTypes = { + error: PropTypes.string, + goHome: PropTypes.func, + displayWarning: PropTypes.func, + importNewJsonAccount: PropTypes.func, +} + +const mapStateToProps = state => { return { error: state.appState.warning, } } -inherits(JsonImportSubview, Component) -function JsonImportSubview () { - Component.call(this) -} - -JsonImportSubview.prototype.render = function () { - const { error } = this.props - - return ( - h('div.new-account-import-form__json', [ - - h('p', 'Used by a variety of different clients'), - h('a.warning', { href: HELP_LINK, target: '_blank' }, 'File import not working? Click here!'), - - h(FileInput, { - readAs: 'text', - onLoad: this.onLoad.bind(this), - style: { - margin: '20px 0px 12px 34%', - fontSize: '15px', - display: 'flex', - justifyContent: 'center', - }, - }), - - h('input.new-account-import-form__input-password', { - type: 'password', - placeholder: 'Enter password', - id: 'json-password-box', - onKeyPress: this.createKeyringOnEnter.bind(this), - }), - - h('div.new-account-create-form__buttons', {}, [ - - h('button.new-account-create-form__button-cancel', { - onClick: () => this.props.goHome(), - }, [ - 'CANCEL', - ]), - - h('button.new-account-create-form__button-create', { - onClick: () => this.createNewKeychain.bind(this), - }, [ - 'IMPORT', - ]), - - ]), - - error ? h('span.error', error) : null, - ]) - ) -} - -JsonImportSubview.prototype.onLoad = function (event, file) { - this.setState({file: file, fileContents: event.target.result}) -} - -JsonImportSubview.prototype.createKeyringOnEnter = function (event) { - if (event.key === 'Enter') { - event.preventDefault() - this.createNewKeychain() +const mapDispatchToProps = dispatch => { + return { + goHome: () => dispatch(actions.goHome()), + displayWarning: warning => dispatch(actions.displayWarning(warning)), + importNewJsonAccount: options => dispatch(actions.importNewAccount('JSON File', options)), } } -JsonImportSubview.prototype.createNewKeychain = function () { - const state = this.state - - if (!state) { - const message = 'You must select a valid file to import.' - return this.props.dispatch(actions.displayWarning(message)) - } - - const { fileContents } = state - - if (!fileContents) { - const message = 'You must select a file to import.' - return this.props.dispatch(actions.displayWarning(message)) - } - - const passwordInput = document.getElementById('json-password-box') - const password = passwordInput.value - - if (!password) { - const message = 'You must enter a password for the selected file.' - return this.props.dispatch(actions.displayWarning(message)) - } - - this.props.dispatch(actions.importNewAccount('JSON File', [ fileContents, password ])) -} +module.exports = connect(mapStateToProps, mapDispatchToProps)(JsonImportSubview) diff --git a/ui/app/accounts/import/private-key.js b/ui/app/accounts/import/private-key.js index 12f3a6430..bc9e9384e 100644 --- a/ui/app/accounts/import/private-key.js +++ b/ui/app/accounts/import/private-key.js @@ -3,6 +3,7 @@ const Component = require('react').Component const h = require('react-hyperscript') const connect = require('react-redux').connect const actions = require('../../actions') +const t = require('../../../i18n') module.exports = connect(mapStateToProps, mapDispatchToProps)(PrivateKeyImportView) @@ -33,9 +34,9 @@ PrivateKeyImportView.prototype.render = function () { return ( h('div.new-account-import-form__private-key', [ - h('div.new-account-import-form__private-key-password-container', [ + h('span.new-account-create-form__instruction', t('pastePrivateKey')), - h('span.new-account-import-form__instruction', 'Paste your private key string here:'), + h('div.new-account-import-form__private-key-password-container', [ h('input.new-account-import-form__input-password', { type: 'password', @@ -47,16 +48,16 @@ PrivateKeyImportView.prototype.render = function () { h('div.new-account-import-form__buttons', {}, [ - h('button.new-account-create-form__button-cancel', { + h('button.new-account-create-form__button-cancel.allcaps', { onClick: () => goHome(), }, [ - 'CANCEL', + t('cancel'), ]), - h('button.new-account-create-form__button-create', { + h('button.new-account-create-form__button-create.allcaps', { onClick: () => this.createNewKeychain(), }, [ - 'IMPORT', + t('import'), ]), ]), diff --git a/ui/app/accounts/import/seed.js b/ui/app/accounts/import/seed.js index b4a7c0afa..9ffc669a2 100644 --- a/ui/app/accounts/import/seed.js +++ b/ui/app/accounts/import/seed.js @@ -2,6 +2,7 @@ const inherits = require('util').inherits const Component = require('react').Component const h = require('react-hyperscript') const connect = require('react-redux').connect +const t = require('../../../i18n') module.exports = connect(mapStateToProps)(SeedImportSubview) @@ -20,11 +21,10 @@ SeedImportSubview.prototype.render = function () { style: { }, }, [ - `Paste your seed phrase here!`, + t('pasteSeed'), h('textarea'), h('br'), - h('button', 'Submit'), + h('button', t('submit')), ]) ) } - diff --git a/ui/app/accounts/new-account/create-form.js b/ui/app/accounts/new-account/create-form.js index a6b3bba4b..eb34f7a2f 100644 --- a/ui/app/accounts/new-account/create-form.js +++ b/ui/app/accounts/new-account/create-form.js @@ -3,6 +3,7 @@ const PropTypes = require('prop-types') const h = require('react-hyperscript') const { connect } = require('react-redux') const actions = require('../../actions') +const t = require('../../../i18n') class NewAccountCreateForm extends Component { constructor (props) { @@ -13,7 +14,7 @@ class NewAccountCreateForm extends Component { this.state = { newAccountName: '', - defaultAccountName: `Account ${newAccountNumber}`, + defaultAccountName: t('newAccountNumberName', newAccountNumber), } } @@ -24,7 +25,7 @@ class NewAccountCreateForm extends Component { return h('div.new-account-create-form', [ h('div.new-account-create-form__input-label', {}, [ - 'Account Name', + t('accountName'), ]), h('div.new-account-create-form__input-wrapper', {}, [ @@ -37,16 +38,16 @@ class NewAccountCreateForm extends Component { h('div.new-account-create-form__buttons', {}, [ - h('button.new-account-create-form__button-cancel', { + h('button.new-account-create-form__button-cancel.allcaps', { onClick: () => this.props.goHome(), }, [ - 'CANCEL', + t('cancel'), ]), - h('button.new-account-create-form__button-create', { + h('button.new-account-create-form__button-create.allcaps', { onClick: () => this.props.createAccount(newAccountName || defaultAccountName), }, [ - 'CREATE', + t('create'), ]), ]), diff --git a/ui/app/accounts/new-account/index.js b/ui/app/accounts/new-account/index.js index acf0dc6e4..854568c77 100644 --- a/ui/app/accounts/new-account/index.js +++ b/ui/app/accounts/new-account/index.js @@ -3,6 +3,7 @@ const h = require('react-hyperscript') const inherits = require('util').inherits const connect = require('react-redux').connect const actions = require('../../actions') +const t = require('../../../i18n') const { getCurrentViewContext } = require('../../selectors') const classnames = require('classnames') @@ -42,10 +43,10 @@ AccountDetailsModal.prototype.render = function () { const { displayedForm, displayForm } = this.props return h('div.new-account', {}, [ - + h('div.new-account__header', [ - h('div.new-account__title', 'New Account'), + h('div.new-account__title', t('newAccount')), h('div.new-account__tabs', [ @@ -55,7 +56,7 @@ AccountDetailsModal.prototype.render = function () { 'new-account__tabs__unselected cursor-pointer': displayedForm !== 'CREATE', }), onClick: () => displayForm('CREATE'), - }, 'Create'), + }, t('createDen')), h('div.new-account__tabs__tab', { className: classnames('new-account__tabs__tab', { @@ -63,7 +64,7 @@ AccountDetailsModal.prototype.render = function () { 'new-account__tabs__unselected cursor-pointer': displayedForm !== 'IMPORT', }), onClick: () => displayForm('IMPORT'), - }, 'Import'), + }, t('import')), ]), diff --git a/ui/app/actions.js b/ui/app/actions.js index d6f509590..bc7ee3d07 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -75,6 +75,8 @@ var actions = { resetAccount, showNewVaultSeed: showNewVaultSeed, showInfoPage: showInfoPage, + CLOSE_WELCOME_SCREEN: 'CLOSE_WELCOME_SCREEN', + closeWelcomeScreen, // seed recovery actions REVEAL_SEED_CONFIRMATION: 'REVEAL_SEED_CONFIRMATION', revealSeedConfirmation: revealSeedConfirmation, @@ -788,7 +790,7 @@ function updateTransaction (txData) { function updateAndApproveTx (txData) { log.info('actions: updateAndApproveTx: ' + JSON.stringify(txData)) return (dispatch) => { - log.debug(`actions calling background.updateAndApproveTx`) + log.debug(`actions calling background.updateAndApproveTx.`) background.updateAndApproveTransaction(txData, (err) => { dispatch(actions.hideLoadingIndication()) dispatch(actions.updateTransactionParams(txData.id, txData.txParams)) @@ -947,6 +949,12 @@ function showNewVaultSeed (seed) { } } +function closeWelcomeScreen () { + return { + type: actions.CLOSE_WELCOME_SCREEN, + } +} + function backToUnlockView () { return { type: actions.BACK_TO_UNLOCK_VIEW, diff --git a/ui/app/add-token.js b/ui/app/add-token.js index 51c577987..b8878b772 100644 --- a/ui/app/add-token.js +++ b/ui/app/add-token.js @@ -71,13 +71,17 @@ AddTokenScreen.prototype.componentWillMount = function () { } AddTokenScreen.prototype.toggleToken = function (address, token) { - const { selectedTokens, errors } = this.state - const { [address]: selectedToken } = selectedTokens + const { selectedTokens = {}, errors } = this.state + const selectedTokensCopy = { ...selectedTokens } + + if (address in selectedTokensCopy) { + delete selectedTokensCopy[address] + } else { + selectedTokensCopy[address] = token + } + this.setState({ - selectedTokens: { - ...selectedTokens, - [address]: selectedToken ? null : token, - }, + selectedTokens: selectedTokensCopy, errors: { ...errors, tokenSelector: null, diff --git a/ui/app/app.js b/ui/app/app.js index 4e6da24c3..9708a2485 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -4,6 +4,7 @@ const connect = require('react-redux').connect const h = require('react-hyperscript') const actions = require('./actions') const classnames = require('classnames') +const t = require('../i18n') // mascara const MascaraFirstTime = require('../../mascara/src/app/first-time').default @@ -12,6 +13,8 @@ const MascaraBuyEtherScreen = require('../../mascara/src/app/first-time/buy-ethe const OldUIInitializeMenuScreen = require('./first-time/init-menu') const InitializeMenuScreen = MascaraFirstTime const NewKeyChainScreen = require('./new-keychain') +const WelcomeScreen = require('./welcome-screen').default + // accounts const MainContainer = require('./main-container') const SendTransactionScreen2 = require('./components/send/send-v2-container') @@ -91,6 +94,7 @@ function mapStateToProps (state) { betaUI: state.metamask.featureFlags.betaUI, isRevealingSeedWords: state.metamask.isRevealingSeedWords, Qr: state.appState.Qr, + welcomeScreenSeen: state.metamask.welcomeScreenSeen, // state needed to get account dropdown temporarily rendering from app bar identities, @@ -244,6 +248,7 @@ App.prototype.renderAppBar = function () { isInitialized, betaUI, isPopup, + welcomeScreenSeen, } = this.props if (window.METAMASK_UI_TYPE === 'notification') { @@ -269,7 +274,7 @@ App.prototype.renderAppBar = function () { style: {}, }, [ - h('.app-header.flex-row.flex-space-between', { + (isInitialized || welcomeScreenSeen || isPopup || !betaUI) && h('.app-header.flex-row.flex-space-between', { className: classnames({ 'app-header--initialized': !isOnboarding, }), @@ -289,12 +294,12 @@ App.prototype.renderAppBar = function () { // metamask name h('.flex-row', [ - h('h1', 'MetaMask'), - h('div.beta-label', 'BETA'), + h('h1', t('appName')), + h('div.beta-label', t('beta')), ]), ]), - h('div.header__right-actions', [ + betaUI && isInitialized && h('div.header__right-actions', [ h('div.network-component-wrapper', { style: {}, }, [ @@ -324,8 +329,12 @@ App.prototype.renderAppBar = function () { ]), ]), - !isInitialized && !isPopup && betaUI && h('h2.alpha-warning', - 'Please be aware that this version is still under development'), + !isInitialized && !isPopup && betaUI && h('h2', { + className: classnames({ + 'alpha-warning': welcomeScreenSeen, + 'alpha-warning-welcome-screen': !welcomeScreenSeen, + }), + }, 'Please be aware that this version is still under development'), ]) ) @@ -369,11 +378,18 @@ App.prototype.renderPrimary = function () { isOnboarding, betaUI, isRevealingSeedWords, + welcomeScreenSeen, Qr, + isInitialized, + isUnlocked, } = props const isMascaraOnboarding = isMascara && isOnboarding const isBetaUIOnboarding = betaUI && isOnboarding && !props.isPopup && !isRevealingSeedWords + if (!welcomeScreenSeen && betaUI && !isInitialized && !isUnlocked) { + return h(WelcomeScreen) + } + if (isMascaraOnboarding || isBetaUIOnboarding) { return h(MascaraFirstTime) } diff --git a/ui/app/components/account-dropdowns.js b/ui/app/components/account-dropdowns.js index 1cd7a0847..1612d7b6a 100644 --- a/ui/app/components/account-dropdowns.js +++ b/ui/app/components/account-dropdowns.js @@ -9,6 +9,7 @@ const DropdownMenuItem = require('./dropdown').DropdownMenuItem const Identicon = require('./identicon') const ethUtil = require('ethereumjs-util') const copyToClipboard = require('copy-to-clipboard') +const t = require('../../i18n') class AccountDropdowns extends Component { constructor (props) { @@ -79,7 +80,7 @@ class AccountDropdowns extends Component { try { // Sometimes keyrings aren't loaded yet: const type = keyring.type const isLoose = type !== 'HD Key Tree' - return isLoose ? h('.keyring-label', 'LOOSE') : null + return isLoose ? h('.keyring-label.allcaps', t('loose')) : null } catch (e) { return } } @@ -129,7 +130,7 @@ class AccountDropdowns extends Component { diameter: 32, }, ), - h('span', { style: { marginLeft: '20px', fontSize: '24px' } }, 'Create Account'), + h('span', { style: { marginLeft: '20px', fontSize: '24px' } }, t('createAccount')), ], ), h( @@ -154,7 +155,7 @@ class AccountDropdowns extends Component { fontSize: '24px', marginBottom: '5px', }, - }, 'Import Account'), + }, t('importAccount')), ] ), ] @@ -192,7 +193,7 @@ class AccountDropdowns extends Component { global.platform.openWindow({ url }) }, }, - 'View account on Etherscan', + t('etherscanView'), ), h( DropdownMenuItem, @@ -204,7 +205,7 @@ class AccountDropdowns extends Component { actions.showQrView(selected, identity ? identity.name : '') }, }, - 'Show QR Code', + t('showQRCode'), ), h( DropdownMenuItem, @@ -216,7 +217,7 @@ class AccountDropdowns extends Component { copyToClipboard(checkSumAddress) }, }, - 'Copy Address to clipboard', + t('copyAddress'), ), h( DropdownMenuItem, @@ -226,7 +227,7 @@ class AccountDropdowns extends Component { actions.requestAccountExport() }, }, - 'Export Private Key', + t('exportPrivateKey'), ), ] ) diff --git a/ui/app/components/account-export.js b/ui/app/components/account-export.js index 32b103c86..5637bc8d0 100644 --- a/ui/app/components/account-export.js +++ b/ui/app/components/account-export.js @@ -6,6 +6,7 @@ const copyToClipboard = require('copy-to-clipboard') const actions = require('../actions') const ethUtil = require('ethereumjs-util') const connect = require('react-redux').connect +const t = require('../../i18n') module.exports = connect(mapStateToProps)(ExportAccountView) @@ -35,7 +36,7 @@ ExportAccountView.prototype.render = function () { if (notExporting) return h('div') if (exportRequested) { - const warning = `Export private keys at your own risk.` + const warning = t('exportPrivateKeyWarning') return ( h('div', { style: { @@ -53,7 +54,7 @@ ExportAccountView.prototype.render = function () { h('p.error', warning), h('input#exportAccount.sizing-input', { type: 'password', - placeholder: 'confirm password', + placeholder: t('confirmPassword').toLowerCase(), onKeyPress: this.onExportKeyPress.bind(this), style: { position: 'relative', @@ -74,10 +75,10 @@ ExportAccountView.prototype.render = function () { style: { marginRight: '10px', }, - }, 'Submit'), + }, t('submit')), h('button', { onClick: () => this.props.dispatch(actions.backToAccountDetail(this.props.address)), - }, 'Cancel'), + }, t('cancel')), ]), (this.props.warning) && ( h('span.error', { @@ -98,7 +99,7 @@ ExportAccountView.prototype.render = function () { margin: '0 20px', }, }, [ - h('label', 'Your private key (click to copy):'), + h('label', t('copyPrivateKey') + ':'), h('p.error.cursor-pointer', { style: { textOverflow: 'ellipsis', @@ -112,13 +113,13 @@ ExportAccountView.prototype.render = function () { }, plainKey), h('button', { onClick: () => this.props.dispatch(actions.backToAccountDetail(this.props.address)), - }, 'Done'), + }, t('done')), h('button', { style: { marginLeft: '10px', }, onClick: () => exportAsFile(`MetaMask ${nickname} Private Key`, plainKey), - }, 'Save as File'), + }, t('saveAsFile')), ]) } } diff --git a/ui/app/components/account-menu/index.js b/ui/app/components/account-menu/index.js index 1a0103d4f..e838e8916 100644 --- a/ui/app/components/account-menu/index.js +++ b/ui/app/components/account-menu/index.js @@ -6,6 +6,7 @@ const actions = require('../../actions') const { Menu, Item, Divider, CloseArea } = require('../dropdowns/components/menu') const Identicon = require('../identicon') const { formatBalance } = require('../../util') +const t = require('../../../i18n') module.exports = connect(mapStateToProps, mapDispatchToProps)(AccountMenu) @@ -70,10 +71,10 @@ AccountMenu.prototype.render = function () { h(Item, { className: 'account-menu__header', }, [ - 'My Accounts', + t('myAccounts'), h('button.account-menu__logout-button', { onClick: lockMetamask, - }, 'Log out'), + }, t('logout')), ]), h(Divider), h('div.account-menu__accounts', this.renderAccounts()), @@ -81,23 +82,23 @@ AccountMenu.prototype.render = function () { h(Item, { onClick: () => showNewAccountPage('CREATE'), icon: h('img.account-menu__item-icon', { src: 'images/plus-btn-white.svg' }), - text: 'Create Account', + text: t('createAccount'), }), h(Item, { onClick: () => showNewAccountPage('IMPORT'), icon: h('img.account-menu__item-icon', { src: 'images/import-account.svg' }), - text: 'Import Account', + text: t('importAccount'), }), h(Divider), h(Item, { onClick: showInfoPage, - icon: h('img.account-menu__item-icon', { src: 'images/mm-info-icon.svg' }), - text: 'Info & Help', + icon: h('img', { src: 'images/mm-info-icon.svg' }), + text: t('infoHelp'), }), h(Item, { onClick: showConfigPage, icon: h('img.account-menu__item-icon', { src: 'images/settings.svg' }), - text: 'Settings', + text: t('settings'), }), ]) } @@ -155,6 +156,6 @@ AccountMenu.prototype.indicateIfLoose = function (keyring) { try { // Sometimes keyrings aren't loaded yet: const type = keyring.type const isLoose = type !== 'HD Key Tree' - return isLoose ? h('.keyring-label', 'IMPORTED') : null + return isLoose ? h('.keyring-label.allcaps', t('imported')) : null } catch (e) { return } } diff --git a/ui/app/components/bn-as-decimal-input.js b/ui/app/components/bn-as-decimal-input.js index 22e37602e..70701b039 100644 --- a/ui/app/components/bn-as-decimal-input.js +++ b/ui/app/components/bn-as-decimal-input.js @@ -4,6 +4,7 @@ const inherits = require('util').inherits const ethUtil = require('ethereumjs-util') const BN = ethUtil.BN const extend = require('xtend') +const t = require('../../i18n') module.exports = BnAsDecimalInput @@ -136,13 +137,13 @@ BnAsDecimalInput.prototype.constructWarning = function () { let message = name ? name + ' ' : '' if (min && max) { - message += `must be greater than or equal to ${newMin} ${suffix} and less than or equal to ${newMax} ${suffix}.` + message += t('betweenMinAndMax', [`${newMin} ${suffix}`, `${newMax} ${suffix}`]) } else if (min) { - message += `must be greater than or equal to ${newMin} ${suffix}.` + message += t('greaterThanMin', [`${newMin} ${suffix}`]) } else if (max) { - message += `must be less than or equal to ${newMax} ${suffix}.` + message += t('lessThanMax', [`${newMax} ${suffix}`]) } else { - message += 'Invalid input.' + message += t('invalidInput') } return message diff --git a/ui/app/components/buy-button-subview.js b/ui/app/components/buy-button-subview.js index d5958787b..1e277a94b 100644 --- a/ui/app/components/buy-button-subview.js +++ b/ui/app/components/buy-button-subview.js @@ -9,6 +9,7 @@ const Loading = require('./loading') const AccountPanel = require('./account-panel') const RadioList = require('./custom-radio-list') const networkNames = require('../../../app/scripts/config.js').networkNames +const t = require('../../i18n') module.exports = connect(mapStateToProps)(BuyButtonSubview) @@ -76,7 +77,7 @@ BuyButtonSubview.prototype.headerSubview = function () { paddingTop: '4px', paddingBottom: '4px', }, - }, 'Deposit Eth'), + }, t('depositEth')), ]), // loading indication @@ -118,7 +119,7 @@ BuyButtonSubview.prototype.headerSubview = function () { paddingTop: '4px', paddingBottom: '4px', }, - }, 'Select Service'), + }, t('selectService')), ]), ]) @@ -143,7 +144,7 @@ BuyButtonSubview.prototype.primarySubview = function () { case '4': case '42': const networkName = networkNames[network] - const label = `${networkName} Test Faucet` + const label = `${networkName} ${t('testFaucet')}` return ( h('div.flex-column', { style: { @@ -164,14 +165,14 @@ BuyButtonSubview.prototype.primarySubview = function () { style: { marginTop: '15px', }, - }, 'Borrow With Dharma (Beta)') + }, t('borrowDharma')) ) : null, ]) ) default: return ( - h('h2.error', 'Unknown network ID') + h('h2.error', t('unknownNetworkId')) ) } @@ -203,8 +204,8 @@ BuyButtonSubview.prototype.mainnetSubview = function () { 'ShapeShift', ], subtext: { - 'Coinbase': 'Crypto/FIAT (USA only)', - 'ShapeShift': 'Crypto', + 'Coinbase': `${t('crypto')}/${t('fiat')} (${t('usaOnly')})`, + 'ShapeShift': t('crypto'), }, onClick: this.radioHandler.bind(this), }), diff --git a/ui/app/components/coinbase-form.js b/ui/app/components/coinbase-form.js index f70208625..e442b43d5 100644 --- a/ui/app/components/coinbase-form.js +++ b/ui/app/components/coinbase-form.js @@ -3,6 +3,7 @@ const h = require('react-hyperscript') const inherits = require('util').inherits const connect = require('react-redux').connect const actions = require('../actions') +const t = require('../../i18n') module.exports = connect(mapStateToProps)(CoinbaseForm) @@ -37,11 +38,11 @@ CoinbaseForm.prototype.render = function () { }, [ h('button.btn-green', { onClick: this.toCoinbase.bind(this), - }, 'Continue to Coinbase'), + }, t('continueToCoinbase')), h('button.btn-red', { onClick: () => props.dispatch(actions.goHome()), - }, 'Cancel'), + }, t('cancel')), ]), ]) } diff --git a/ui/app/components/copyButton.js b/ui/app/components/copyButton.js index a25d0719c..355f78d45 100644 --- a/ui/app/components/copyButton.js +++ b/ui/app/components/copyButton.js @@ -2,6 +2,7 @@ const Component = require('react').Component const h = require('react-hyperscript') const inherits = require('util').inherits const copyToClipboard = require('copy-to-clipboard') +const t = require('../../i18n') const Tooltip = require('./tooltip') @@ -22,7 +23,7 @@ CopyButton.prototype.render = function () { const value = props.value const copied = state.copied - const message = copied ? 'Copied' : props.title || ' Copy ' + const message = copied ? t('copiedButton') : props.title || t('copyButton') return h('.copy-button', { style: { diff --git a/ui/app/components/copyable.js b/ui/app/components/copyable.js index a4f6f4bc6..fca7d3863 100644 --- a/ui/app/components/copyable.js +++ b/ui/app/components/copyable.js @@ -4,6 +4,7 @@ const inherits = require('util').inherits const Tooltip = require('./tooltip') const copyToClipboard = require('copy-to-clipboard') +const t = require('../../i18n') module.exports = Copyable @@ -22,7 +23,7 @@ Copyable.prototype.render = function () { const { copied } = state return h(Tooltip, { - title: copied ? 'Copied!' : 'Copy', + title: copied ? t('copiedExclamation') : t('copy'), position: 'bottom', }, h('span', { style: { diff --git a/ui/app/components/customize-gas-modal/index.js b/ui/app/components/customize-gas-modal/index.js index 2bac57253..291447bb4 100644 --- a/ui/app/components/customize-gas-modal/index.js +++ b/ui/app/components/customize-gas-modal/index.js @@ -3,6 +3,7 @@ const h = require('react-hyperscript') const inherits = require('util').inherits const connect = require('react-redux').connect const actions = require('../../actions') +const t = require('../../../i18n') const GasModalCard = require('./gas-modal-card') const ethUtil = require('ethereumjs-util') @@ -149,7 +150,7 @@ CustomizeGasModal.prototype.validate = function ({ gasTotal, gasLimit }) { }) if (!balanceIsSufficient) { - error = 'Insufficient balance for current gas total' + error = t('balanceIsInsufficientGas') } const gasLimitTooLow = gasLimit && conversionGreaterThan( @@ -165,7 +166,7 @@ CustomizeGasModal.prototype.validate = function ({ gasTotal, gasLimit }) { ) if (gasLimitTooLow) { - error = 'Gas limit must be at least 21000' + error = t('gasLimitTooLow') } this.setState({ error }) @@ -253,7 +254,7 @@ CustomizeGasModal.prototype.render = function () { }, [ h('div.send-v2__customize-gas__header', {}, [ - h('div.send-v2__customize-gas__title', 'Customize Gas'), + h('div.send-v2__customize-gas__title', t('customGas')), h('div.send-v2__customize-gas__close', { onClick: hideModal, @@ -269,8 +270,8 @@ CustomizeGasModal.prototype.render = function () { // max: 1000, step: multiplyCurrencies(MIN_GAS_PRICE_GWEI, 10), onChange: value => this.convertAndSetGasPrice(value), - title: 'Gas Price (GWEI)', - copy: 'We calculate the suggested gas prices based on network success rates.', + title: t('gasPrice'), + copy: t('gasPriceCalculation'), }), h(GasModalCard, { @@ -279,8 +280,8 @@ CustomizeGasModal.prototype.render = function () { // max: 100000, step: 1, onChange: value => this.convertAndSetGasLimit(value), - title: 'Gas Limit', - copy: 'We calculate the suggested gas limit based on network success rates.', + title: t('gasLimit'), + copy: t('gasLimitCalculation'), }), ]), @@ -293,16 +294,16 @@ CustomizeGasModal.prototype.render = function () { h('div.send-v2__customize-gas__revert', { onClick: () => this.revert(), - }, ['Revert']), + }, [t('revert')]), h('div.send-v2__customize-gas__buttons', [ - h('div.send-v2__customize-gas__cancel', { + h('div.send-v2__customize-gas__cancel.allcaps', { onClick: this.props.hideModal, - }, ['CANCEL']), + }, [t('cancel')]), - h(`div.send-v2__customize-gas__save${error ? '__error' : ''}`, { + h(`div.send-v2__customize-gas__save${error ? '__error' : ''}.allcaps`, { onClick: () => !error && this.save(gasPrice, gasLimit, gasTotal), - }, ['SAVE']), + }, [t('save')]), ]), ]), diff --git a/ui/app/components/dropdowns/components/account-dropdowns.js b/ui/app/components/dropdowns/components/account-dropdowns.js index 40918cc53..e5359c1d6 100644 --- a/ui/app/components/dropdowns/components/account-dropdowns.js +++ b/ui/app/components/dropdowns/components/account-dropdowns.js @@ -10,6 +10,7 @@ const Identicon = require('../../identicon') const ethUtil = require('ethereumjs-util') const copyToClipboard = require('copy-to-clipboard') const { formatBalance } = require('../../../util') +const t = require('../../../../i18n') class AccountDropdowns extends Component { constructor (props) { @@ -121,7 +122,7 @@ class AccountDropdowns extends Component { flex: '3 3 auto', }, }, [ - h('span.account-dropdown-edit-button', { + h('span.account-dropdown-edit-button.allcaps', { style: { fontSize: '16px', }, @@ -129,7 +130,7 @@ class AccountDropdowns extends Component { actions.showEditAccountModal(identity) }, }, [ - 'Edit', + t('edit'), ]), ]), @@ -143,7 +144,7 @@ class AccountDropdowns extends Component { try { // Sometimes keyrings aren't loaded yet: const type = keyring.type const isLoose = type !== 'HD Key Tree' - return isLoose ? h('.keyring-label', 'IMPORTED') : null + return isLoose ? h('.keyring-label.allcaps', t('loose')) : null } catch (e) { return } } @@ -201,7 +202,7 @@ class AccountDropdowns extends Component { fontSize: '16px', lineHeight: '23px', }, - }, 'Create Account'), + }, t('createAccount')), ], ), h( @@ -235,7 +236,7 @@ class AccountDropdowns extends Component { fontSize: '16px', lineHeight: '23px', }, - }, 'Import Account'), + }, t('importAccount')), ] ), ] @@ -286,7 +287,7 @@ class AccountDropdowns extends Component { menuItemStyles, ), }, - 'Account Details', + t('accountDetails'), ), h( DropdownMenuItem, @@ -302,7 +303,7 @@ class AccountDropdowns extends Component { menuItemStyles, ), }, - 'View account on Etherscan', + t('etherscanView'), ), h( DropdownMenuItem, @@ -318,7 +319,7 @@ class AccountDropdowns extends Component { menuItemStyles, ), }, - 'Copy Address to clipboard', + t('copyAddress'), ), h( DropdownMenuItem, @@ -330,7 +331,7 @@ class AccountDropdowns extends Component { menuItemStyles, ), }, - 'Export Private Key', + t('exportPrivateKey'), ), h( DropdownMenuItem, @@ -345,7 +346,7 @@ class AccountDropdowns extends Component { menuItemStyles, ), }, - 'Add Token', + t('addToken'), ), ] @@ -463,4 +464,3 @@ function mapStateToProps (state) { } module.exports = connect(mapStateToProps, mapDispatchToProps)(AccountDropdowns) - diff --git a/ui/app/components/dropdowns/network-dropdown.js b/ui/app/components/dropdowns/network-dropdown.js index ff10c0758..5afe730c1 100644 --- a/ui/app/components/dropdowns/network-dropdown.js +++ b/ui/app/components/dropdowns/network-dropdown.js @@ -6,6 +6,7 @@ const actions = require('../../actions') const Dropdown = require('./components/dropdown').Dropdown const DropdownMenuItem = require('./components/dropdown').DropdownMenuItem const NetworkDropdownIcon = require('./components/network-dropdown-icon') +const t = require('../../../i18n') const R = require('ramda') // classes from nodes of the toggle element. @@ -93,13 +94,13 @@ NetworkDropdown.prototype.render = function () { }, [ h('div.network-dropdown-header', {}, [ - h('div.network-dropdown-title', {}, 'Networks'), + h('div.network-dropdown-title', {}, t('networks')), h('div.network-dropdown-divider'), h('div.network-dropdown-content', {}, - 'The default network for Ether transactions is Main Net.' + t('defaultNetwork') ), ]), @@ -121,7 +122,7 @@ NetworkDropdown.prototype.render = function () { style: { color: providerType === 'mainnet' ? '#ffffff' : '#9b9b9b', }, - }, 'Main Ethereum Network'), + }, t('mainnet')), ] ), @@ -143,7 +144,7 @@ NetworkDropdown.prototype.render = function () { style: { color: providerType === 'ropsten' ? '#ffffff' : '#9b9b9b', }, - }, 'Ropsten Test Network'), + }, t('ropsten')), ] ), @@ -165,7 +166,7 @@ NetworkDropdown.prototype.render = function () { style: { color: providerType === 'kovan' ? '#ffffff' : '#9b9b9b', }, - }, 'Kovan Test Network'), + }, t('kovan')), ] ), @@ -187,7 +188,7 @@ NetworkDropdown.prototype.render = function () { style: { color: providerType === 'rinkeby' ? '#ffffff' : '#9b9b9b', }, - }, 'Rinkeby Test Network'), + }, t('rinkeby')), ] ), @@ -209,7 +210,7 @@ NetworkDropdown.prototype.render = function () { style: { color: activeNetwork === 'http://localhost:8545' ? '#ffffff' : '#9b9b9b', }, - }, 'Localhost 8545'), + }, t('localhost')), ] ), @@ -233,7 +234,7 @@ NetworkDropdown.prototype.render = function () { style: { color: activeNetwork === 'custom' ? '#ffffff' : '#9b9b9b', }, - }, 'Custom RPC'), + }, t('customRPC')), ] ), @@ -248,15 +249,15 @@ NetworkDropdown.prototype.getNetworkName = function () { let name if (providerName === 'mainnet') { - name = 'Main Ethereum Network' + name = t('mainnet') } else if (providerName === 'ropsten') { - name = 'Ropsten Test Network' + name = t('ropsten') } else if (providerName === 'kovan') { - name = 'Kovan Test Network' + name = t('kovan') } else if (providerName === 'rinkeby') { - name = 'Rinkeby Test Network' + name = t('rinkeby') } else { - name = 'Unknown Private Network' + name = t('unknownNetwork') } return name diff --git a/ui/app/components/dropdowns/token-menu-dropdown.js b/ui/app/components/dropdowns/token-menu-dropdown.js index dc7a985e3..a4f93b505 100644 --- a/ui/app/components/dropdowns/token-menu-dropdown.js +++ b/ui/app/components/dropdowns/token-menu-dropdown.js @@ -3,6 +3,7 @@ const h = require('react-hyperscript') const inherits = require('util').inherits const connect = require('react-redux').connect const actions = require('../../actions') +const t = require('../../../i18n') module.exports = connect(null, mapDispatchToProps)(TokenMenuDropdown) @@ -43,7 +44,7 @@ TokenMenuDropdown.prototype.render = function () { showHideTokenConfirmationModal(this.props.token) this.props.onClose() }, - }, 'Hide Token'), + }, t('hideToken')), ]), ]), diff --git a/ui/app/components/ens-input.js b/ui/app/components/ens-input.js index 6553053f7..add67ea35 100644 --- a/ui/app/components/ens-input.js +++ b/ui/app/components/ens-input.js @@ -8,6 +8,7 @@ const ENS = require('ethjs-ens') const networkMap = require('ethjs-ens/lib/network-map.json') const ensRE = /.+\..+$/ const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' +const t = require('../../i18n') module.exports = EnsInput @@ -89,13 +90,13 @@ EnsInput.prototype.lookupEnsName = function () { log.info(`ENS attempting to resolve name: ${recipient}`) this.ens.lookup(recipient.trim()) .then((address) => { - if (address === ZERO_ADDRESS) throw new Error('No address has been set for this name.') + if (address === ZERO_ADDRESS) throw new Error(t('noAddressForName')) if (address !== ensResolution) { this.setState({ loadingEns: false, ensResolution: address, nickname: recipient.trim(), - hoverText: address + '\nClick to Copy', + hoverText: address + '\n' + t('clickCopy'), ensFailure: false, }) } diff --git a/ui/app/components/hex-as-decimal-input.js b/ui/app/components/hex-as-decimal-input.js index 4a71e9585..a43d44f89 100644 --- a/ui/app/components/hex-as-decimal-input.js +++ b/ui/app/components/hex-as-decimal-input.js @@ -4,6 +4,7 @@ const inherits = require('util').inherits const ethUtil = require('ethereumjs-util') const BN = ethUtil.BN const extend = require('xtend') +const t = require('../../i18n') module.exports = HexAsDecimalInput @@ -126,13 +127,13 @@ HexAsDecimalInput.prototype.constructWarning = function () { let message = name ? name + ' ' : '' if (min && max) { - message += `must be greater than or equal to ${min} and less than or equal to ${max}.` + message += t('betweenMinAndMax', [min, max]) } else if (min) { - message += `must be greater than or equal to ${min}.` + message += t('greaterThanMin', [min]) } else if (max) { - message += `must be less than or equal to ${max}.` + message += t('lessThanMax', [max]) } else { - message += 'Invalid input.' + message += t('invalidInput') } return message diff --git a/ui/app/components/modals/account-details-modal.js b/ui/app/components/modals/account-details-modal.js index c1f7a3236..75f989e86 100644 --- a/ui/app/components/modals/account-details-modal.js +++ b/ui/app/components/modals/account-details-modal.js @@ -8,6 +8,7 @@ const { getSelectedIdentity } = require('../../selectors') const genAccountLink = require('../../../lib/account-link.js') const QrView = require('../qr-code') const EditableLabel = require('../editable-label') +const t = require('../../../i18n') function mapStateToProps (state) { return { @@ -64,12 +65,12 @@ AccountDetailsModal.prototype.render = function () { h('button.btn-clear.account-modal__button', { onClick: () => global.platform.openWindow({ url: genAccountLink(address, network) }), - }, 'View account on Etherscan'), + }, t('etherscanView')), // Holding on redesign for Export Private Key functionality h('button.btn-clear.account-modal__button', { onClick: () => showExportPrivateKeyModal(), - }, 'Export private key'), + }, t('exportPrivateKey')), ]) } diff --git a/ui/app/components/modals/account-modal-container.js b/ui/app/components/modals/account-modal-container.js index c548fb7b3..08540aa76 100644 --- a/ui/app/components/modals/account-modal-container.js +++ b/ui/app/components/modals/account-modal-container.js @@ -5,6 +5,7 @@ const connect = require('react-redux').connect const actions = require('../../actions') const { getSelectedIdentity } = require('../../selectors') const Identicon = require('../identicon') +const t = require('../../../i18n') function mapStateToProps (state) { return { @@ -59,7 +60,7 @@ AccountModalContainer.prototype.render = function () { h('i.fa.fa-angle-left.fa-lg'), - h('span.account-modal-back__text', ' Back'), + h('span.account-modal-back__text', ' ' + t('back')), ]), diff --git a/ui/app/components/modals/buy-options-modal.js b/ui/app/components/modals/buy-options-modal.js index 74a7a847e..7eb73c3a6 100644 --- a/ui/app/components/modals/buy-options-modal.js +++ b/ui/app/components/modals/buy-options-modal.js @@ -4,6 +4,7 @@ const inherits = require('util').inherits const connect = require('react-redux').connect const actions = require('../../actions') const networkNames = require('../../../../app/scripts/config.js').networkNames +const t = require('../../../i18n') function mapStateToProps (state) { return { @@ -56,15 +57,15 @@ BuyOptions.prototype.render = function () { }, [ h('div.buy-modal-content-title', { style: {}, - }, 'Transfers'), - h('div', {}, 'How would you like to deposit Ether?'), + }, t('transfers')), + h('div', {}, t('howToDeposit')), ]), h('div.buy-modal-content-options.flex-column.flex-center', {}, [ isTestNetwork - ? this.renderModalContentOption(networkName, 'Test Faucet', () => toFaucet(network)) - : this.renderModalContentOption('Coinbase', 'Deposit with Fiat', () => toCoinbase(address)), + ? this.renderModalContentOption(networkName, t('testFaucet'), () => toFaucet(network)) + : this.renderModalContentOption('Coinbase', t('depositFiat'), () => toCoinbase(address)), // h('div.buy-modal-content-option', {}, [ // h('div.buy-modal-content-option-title', {}, 'Shapeshift'), @@ -72,8 +73,8 @@ BuyOptions.prototype.render = function () { // ]),, this.renderModalContentOption( - 'Direct Deposit', - 'Deposit from another account', + t('directDeposit'), + t('depositFromAccount'), () => this.goToAccountDetailsModal() ), @@ -84,7 +85,7 @@ BuyOptions.prototype.render = function () { background: 'white', }, onClick: () => { this.props.hideModal() }, - }, h('div.buy-modal-content-footer#buy-modal-content-footer-text', {}, 'Cancel')), + }, h('div.buy-modal-content-footer#buy-modal-content-footer-text', {}, t('cancel'))), ]), ]) } diff --git a/ui/app/components/modals/deposit-ether-modal.js b/ui/app/components/modals/deposit-ether-modal.js index 7547dbcf5..26ff3ea03 100644 --- a/ui/app/components/modals/deposit-ether-modal.js +++ b/ui/app/components/modals/deposit-ether-modal.js @@ -5,18 +5,18 @@ const connect = require('react-redux').connect const actions = require('../../actions') const networkNames = require('../../../../app/scripts/config.js').networkNames const ShapeshiftForm = require('../shapeshift-form') +const t = require('../../../i18n') -const DIRECT_DEPOSIT_ROW_TITLE = 'Directly Deposit Ether' -const DIRECT_DEPOSIT_ROW_TEXT = `If you already have some Ether, the quickest way to get Ether in -your new wallet by direct deposit.` -const COINBASE_ROW_TITLE = 'Buy on Coinbase' -const COINBASE_ROW_TEXT = `Coinbase is the world’s most popular way to buy and sell bitcoin, -ethereum, and litecoin.` -const SHAPESHIFT_ROW_TITLE = 'Deposit with ShapeShift' -const SHAPESHIFT_ROW_TEXT = `If you own other cryptocurrencies, you can trade and deposit Ether -directly into your MetaMask wallet. No Account Needed.` -const FAUCET_ROW_TITLE = 'Test Faucet' -const facuetRowText = networkName => `Get Ether from a faucet for the ${networkName}` +const DIRECT_DEPOSIT_ROW_TITLE = t('directDepositEther') +const DIRECT_DEPOSIT_ROW_TEXT = t('directDepositEtherExplainer') +const COINBASE_ROW_TITLE = t('buyCoinbase') +const COINBASE_ROW_TEXT = t('buyCoinbaseExplainer') +const SHAPESHIFT_ROW_TITLE = t('depositShapeShift') +const SHAPESHIFT_ROW_TEXT = t('depositShapeShiftExplainer') +const FAUCET_ROW_TITLE = t('testFaucet') +const facuetRowText = (networkName) => { + return t('getEtherFromFaucet', [networkName]) +} function mapStateToProps (state) { return { @@ -83,7 +83,7 @@ DepositEtherModal.prototype.renderRow = function ({ ]), - h('div.deposit-ether-modal__buy-row__logo', [logo]), + h('div.deposit-ether-modal__buy-row__logo-container', [logo]), h('div.deposit-ether-modal__buy-row__description', [ @@ -109,17 +109,17 @@ DepositEtherModal.prototype.render = function () { const isTestNetwork = ['3', '4', '42'].find(n => n === network) const networkName = networkNames[network] - return h('div.deposit-ether-modal', {}, [ + return h('div.page-container.page-container--full-width', {}, [ - h('div.deposit-ether-modal__header', [ + h('div.page-container__header', [ - h('div.deposit-ether-modal__header__title', ['Deposit Ether']), + h('div.page-container__title', [t('depositEther')]), - h('div.deposit-ether-modal__header__description', [ - 'To interact with decentralized applications using MetaMask, you’ll need Ether in your wallet.', + h('div.page-container__subtitle', [ + t('needEtherInWallet'), ]), - h('div.deposit-ether-modal__header__close', { + h('div.page-container__header-close', { onClick: () => { this.setState({ buyingWithShapeshift: false }) this.props.hideWarning() @@ -128,55 +128,64 @@ DepositEtherModal.prototype.render = function () { }), ]), + + h('.page-container__content', {}, [ + + h('div.deposit-ether-modal__buy-rows', [ - h('div.deposit-ether-modal__buy-rows', [ - - this.renderRow({ - logo: h('img.deposit-ether-modal__buy-row__eth-logo', { src: '../../../images/eth_logo.svg' }), - title: DIRECT_DEPOSIT_ROW_TITLE, - text: DIRECT_DEPOSIT_ROW_TEXT, - buttonLabel: 'View Account', - onButtonClick: () => this.goToAccountDetailsModal(), - hide: buyingWithShapeshift, - }), - - this.renderRow({ - logo: h('i.fa.fa-tint.fa-2x'), - title: FAUCET_ROW_TITLE, - text: facuetRowText(networkName), - buttonLabel: 'Get Ether', - onButtonClick: () => toFaucet(network), - hide: !isTestNetwork || buyingWithShapeshift, - }), - - this.renderRow({ - logo: h('img.deposit-ether-modal__buy-row__coinbase-logo', { - src: '../../../images/coinbase logo.png', + this.renderRow({ + logo: h('img.deposit-ether-modal__buy-row__eth-logo', { src: '../../../images/eth_logo.svg' }), + title: DIRECT_DEPOSIT_ROW_TITLE, + text: DIRECT_DEPOSIT_ROW_TEXT, + buttonLabel: t('viewAccount'), + onButtonClick: () => this.goToAccountDetailsModal(), + hide: buyingWithShapeshift, }), - title: COINBASE_ROW_TITLE, - text: COINBASE_ROW_TEXT, - buttonLabel: 'Continue to Coinbase', - onButtonClick: () => toCoinbase(address), - hide: isTestNetwork || buyingWithShapeshift, - }), - this.renderRow({ - logo: h('img.deposit-ether-modal__buy-row__shapeshift-logo', { - src: '../../../images/shapeshift logo.png', + this.renderRow({ + logo: h('i.fa.fa-tint.fa-2x'), + title: FAUCET_ROW_TITLE, + text: facuetRowText(networkName), + buttonLabel: t('getEther'), + onButtonClick: () => toFaucet(network), + hide: !isTestNetwork || buyingWithShapeshift, }), - title: SHAPESHIFT_ROW_TITLE, - text: SHAPESHIFT_ROW_TEXT, - buttonLabel: 'Buy with Shapeshift', - onButtonClick: () => this.setState({ buyingWithShapeshift: true }), - hide: isTestNetwork, - hideButton: buyingWithShapeshift, - hideTitle: buyingWithShapeshift, - onBackClick: () => this.setState({ buyingWithShapeshift: false }), - showBackButton: this.state.buyingWithShapeshift, - className: buyingWithShapeshift && 'deposit-ether-modal__buy-row__shapeshift-buy', - }), - buyingWithShapeshift && h(ShapeshiftForm), + this.renderRow({ + logo: h('div.deposit-ether-modal__logo', { + style: { + backgroundImage: 'url(\'../../../images/coinbase logo.png\')', + height: '40px', + }, + }), + title: COINBASE_ROW_TITLE, + text: COINBASE_ROW_TEXT, + buttonLabel: t('continueToCoinbase'), + onButtonClick: () => toCoinbase(address), + hide: isTestNetwork || buyingWithShapeshift, + }), + + this.renderRow({ + logo: h('div.deposit-ether-modal__logo', { + style: { + backgroundImage: 'url(\'../../../images/shapeshift logo.png\')', + }, + }), + title: SHAPESHIFT_ROW_TITLE, + text: SHAPESHIFT_ROW_TEXT, + buttonLabel: t('shapeshiftBuy'), + onButtonClick: () => this.setState({ buyingWithShapeshift: true }), + hide: isTestNetwork, + hideButton: buyingWithShapeshift, + hideTitle: buyingWithShapeshift, + onBackClick: () => this.setState({ buyingWithShapeshift: false }), + showBackButton: this.state.buyingWithShapeshift, + className: buyingWithShapeshift && 'deposit-ether-modal__buy-row__shapeshift-buy', + }), + + buyingWithShapeshift && h(ShapeshiftForm), + + ]), ]), ]) diff --git a/ui/app/components/modals/edit-account-name-modal.js b/ui/app/components/modals/edit-account-name-modal.js index e2361140d..6efa8d476 100644 --- a/ui/app/components/modals/edit-account-name-modal.js +++ b/ui/app/components/modals/edit-account-name-modal.js @@ -4,6 +4,7 @@ const inherits = require('util').inherits const connect = require('react-redux').connect const actions = require('../../actions') const { getSelectedAccount } = require('../../selectors') +const t = require('../../../i18n') function mapStateToProps (state) { return { @@ -50,7 +51,7 @@ EditAccountNameModal.prototype.render = function () { ]), h('div.edit-account-name-modal-title', { - }, ['Edit Account Name']), + }, [t('editAccountName')]), h('input.edit-account-name-modal-input', { placeholder: identity.name, @@ -60,7 +61,7 @@ EditAccountNameModal.prototype.render = function () { value: this.state.inputText, }, []), - h('button.btn-clear.edit-account-name-modal-save-button', { + h('button.btn-clear.edit-account-name-modal-save-button.allcaps', { onClick: () => { if (this.state.inputText.length !== 0) { saveAccountLabel(identity.address, this.state.inputText) @@ -69,7 +70,7 @@ EditAccountNameModal.prototype.render = function () { }, disabled: this.state.inputText.length === 0, }, [ - 'SAVE', + t('save'), ]), ]), diff --git a/ui/app/components/modals/export-private-key-modal.js b/ui/app/components/modals/export-private-key-modal.js index 422f23f44..017177cfd 100644 --- a/ui/app/components/modals/export-private-key-modal.js +++ b/ui/app/components/modals/export-private-key-modal.js @@ -7,6 +7,7 @@ const actions = require('../../actions') const AccountModalContainer = require('./account-modal-container') const { getSelectedIdentity } = require('../../selectors') const ReadOnlyInput = require('../readonly-input') +const t = require('../../../i18n') const copyToClipboard = require('copy-to-clipboard') function mapStateToProps (state) { @@ -48,8 +49,8 @@ ExportPrivateKeyModal.prototype.exportAccountAndGetPrivateKey = function (passwo ExportPrivateKeyModal.prototype.renderPasswordLabel = function (privateKey) { return h('span.private-key-password-label', privateKey - ? 'This is your private key (click to copy)' - : 'Type Your Password' + ? t('copyPrivateKey') + : t('typePassword') ) } @@ -86,8 +87,8 @@ ExportPrivateKeyModal.prototype.renderButtons = function (privateKey, password, ), (privateKey - ? this.renderButton('btn-clear export-private-key__button', () => hideModal(), 'Done') - : this.renderButton('btn-clear export-private-key__button', () => this.exportAccountAndGetPrivateKey(this.state.password, address), 'Confirm') + ? this.renderButton('btn-clear export-private-key__button', () => hideModal(), t('done')) + : this.renderButton('btn-clear export-private-key__button', () => this.exportAccountAndGetPrivateKey(this.state.password, address), t('confirm')) ), ]) @@ -120,7 +121,7 @@ ExportPrivateKeyModal.prototype.render = function () { h('div.account-modal-divider'), - h('span.modal-body-title', 'Show Private Keys'), + h('span.modal-body-title', t('showPrivateKeys')), h('div.private-key-password', {}, [ this.renderPasswordLabel(privateKey), @@ -130,10 +131,7 @@ ExportPrivateKeyModal.prototype.render = function () { !warning ? null : h('span.private-key-password-error', warning), ]), - h('div.private-key-password-warning', `Warning: Never disclose this key. - Anyone with your private keys can take steal any assets held in your - account.` - ), + h('div.private-key-password-warning', t('privateKeyWarning')), this.renderButtons(privateKey, this.state.password, address, hideModal), diff --git a/ui/app/components/modals/hide-token-confirmation-modal.js b/ui/app/components/modals/hide-token-confirmation-modal.js index 56c7ba299..33d8062c6 100644 --- a/ui/app/components/modals/hide-token-confirmation-modal.js +++ b/ui/app/components/modals/hide-token-confirmation-modal.js @@ -4,6 +4,7 @@ const inherits = require('util').inherits const connect = require('react-redux').connect const actions = require('../../actions') const Identicon = require('../identicon') +const t = require('../../../i18n') function mapStateToProps (state) { return { @@ -41,7 +42,7 @@ HideTokenConfirmationModal.prototype.render = function () { h('div.hide-token-confirmation__container', { }, [ h('div.hide-token-confirmation__title', {}, [ - 'Hide Token?', + t('hideTokenPrompt'), ]), h(Identicon, { @@ -54,19 +55,19 @@ HideTokenConfirmationModal.prototype.render = function () { h('div.hide-token-confirmation__symbol', {}, symbol), h('div.hide-token-confirmation__copy', {}, [ - 'You can add this token back in the future by going go to “Add token” in your accounts options menu.', + t('readdToken'), ]), h('div.hide-token-confirmation__buttons', {}, [ - h('button.btn-cancel.hide-token-confirmation__button', { + h('button.btn-cancel.hide-token-confirmation__button.allcaps', { onClick: () => hideModal(), }, [ - 'CANCEL', + t('cancel'), ]), - h('button.btn-clear.hide-token-confirmation__button', { + h('button.btn-clear.hide-token-confirmation__button.allcaps', { onClick: () => hideToken(address), }, [ - 'HIDE', + t('hide'), ]), ]), ]), diff --git a/ui/app/components/modals/modal.js b/ui/app/components/modals/modal.js index 8e9e58985..501b83430 100644 --- a/ui/app/components/modals/modal.js +++ b/ui/app/components/modals/modal.js @@ -6,6 +6,7 @@ const FadeModal = require('boron').FadeModal const actions = require('../../actions') const isMobileView = require('../../../lib/is-mobile-view') const isPopupOrNotification = require('../../../../app/scripts/lib/is-popup-or-notification') +const t = require('../../../i18n') // Modal Components const BuyOptions = require('./buy-options-modal') @@ -92,18 +93,20 @@ const MODALS = { display: 'flex', }, laptopModalStyle: { - width: '900px', - maxWidth: '900px', + width: '850px', top: 'calc(10% + 10px)', left: '0', right: '0', margin: '0 auto', boxShadow: '0 0 6px 0 rgba(0,0,0,0.3)', - borderRadius: '8px', + borderRadius: '7px', transform: 'none', + height: 'calc(80% - 20px)', + overflowY: 'hidden', }, contentStyle: { - borderRadius: '8px', + borderRadius: '7px', + height: '100%', }, }, @@ -171,9 +174,8 @@ const MODALS = { BETA_UI_NOTIFICATION_MODAL: { contents: [ h(NotifcationModal, { - header: 'Welcome to the New UI (Beta)', - message: `You are now using the new Metamask UI. Take a look around, try out new features like sending tokens, - and let us know if you have any issues.`, + header: t('uiWelcome'), + message: t('uiWelcomeMessage'), }), ], mobileModalStyle: { @@ -189,9 +191,8 @@ const MODALS = { OLD_UI_NOTIFICATION_MODAL: { contents: [ h(NotifcationModal, { - header: 'Old UI', - message: `You have returned to the old UI. You can switch back to the New UI through the option in the top - right dropdown menu.`, + header: t('oldUI'), + message: t('oldUIMessage'), }), ], mobileModalStyle: { diff --git a/ui/app/components/modals/new-account-modal.js b/ui/app/components/modals/new-account-modal.js index fc1fd413d..298b76af4 100644 --- a/ui/app/components/modals/new-account-modal.js +++ b/ui/app/components/modals/new-account-modal.js @@ -3,6 +3,7 @@ const PropTypes = require('prop-types') const h = require('react-hyperscript') const { connect } = require('react-redux') const actions = require('../../actions') +const t = require('../../../i18n') class NewAccountModal extends Component { constructor (props) { @@ -11,7 +12,7 @@ class NewAccountModal extends Component { const newAccountNumber = numberOfExistingAccounts + 1 this.state = { - newAccountName: `Account ${newAccountNumber}`, + newAccountName: `${t('account')} ${newAccountNumber}`, } } @@ -22,7 +23,7 @@ class NewAccountModal extends Component { h('div.new-account-modal-wrapper', { }, [ h('div.new-account-modal-header', {}, [ - 'New Account', + t('newAccount'), ]), h('div.modal-close-x', { @@ -30,19 +31,19 @@ class NewAccountModal extends Component { }), h('div.new-account-modal-content', {}, [ - 'Account Name', + t('accountName'), ]), h('div.new-account-input-wrapper', {}, [ h('input.new-account-input', { value: this.state.newAccountName, - placeholder: 'E.g. My new account', + placeholder: t('sampleAccountName'), onChange: event => this.setState({ newAccountName: event.target.value }), }, []), ]), h('div.new-account-modal-content.after-input', {}, [ - 'or', + t('or'), ]), h('div.new-account-modal-content.after-input.pointer', { @@ -50,13 +51,13 @@ class NewAccountModal extends Component { this.props.hideModal() this.props.showImportPage() }, - }, 'Import an account'), + }, t('importAnAccount')), - h('div.new-account-modal-content.button', {}, [ + h('div.new-account-modal-content.button.allcaps', {}, [ h('button.btn-clear', { onClick: () => this.props.createAccount(newAccountName), }, [ - 'SAVE', + t('save'), ]), ]), ]), diff --git a/ui/app/components/network.js b/ui/app/components/network.js index 3e91fa807..f3df2242a 100644 --- a/ui/app/components/network.js +++ b/ui/app/components/network.js @@ -3,6 +3,7 @@ const h = require('react-hyperscript') const classnames = require('classnames') const inherits = require('util').inherits const NetworkDropdownIcon = require('./dropdowns/components/network-dropdown-icon') +const t = require('../../i18n') module.exports = Network @@ -33,7 +34,7 @@ Network.prototype.render = function () { onClick: (event) => this.props.onClick(event), }, [ h('img', { - title: 'Attempting to connect to blockchain.', + title: t('attemptingConnect'), style: { width: '27px', }, @@ -41,22 +42,22 @@ Network.prototype.render = function () { }), ]) } else if (providerName === 'mainnet') { - hoverText = 'Main Ethereum Network' + hoverText = t('mainnet') iconName = 'ethereum-network' } else if (providerName === 'ropsten') { - hoverText = 'Ropsten Test Network' + hoverText = t('ropsten') iconName = 'ropsten-test-network' } else if (parseInt(networkNumber) === 3) { - hoverText = 'Ropsten Test Network' + hoverText = t('ropsten') iconName = 'ropsten-test-network' } else if (providerName === 'kovan') { - hoverText = 'Kovan Test Network' + hoverText = t('kovan') iconName = 'kovan-test-network' } else if (providerName === 'rinkeby') { - hoverText = 'Rinkeby Test Network' + hoverText = t('rinkeby') iconName = 'rinkeby-test-network' } else { - hoverText = 'Unknown Private Network' + hoverText = t('unknownNetwork') iconName = 'unknown-private-network' } @@ -84,7 +85,7 @@ Network.prototype.render = function () { backgroundColor: '#038789', // $blue-lagoon nonSelectBackgroundColor: '#15afb2', }), - h('.network-name', 'Main Network'), + h('.network-name', t('mainnet')), h('i.fa.fa-chevron-down.fa-lg.network-caret'), ]) case 'ropsten-test-network': @@ -93,7 +94,7 @@ Network.prototype.render = function () { backgroundColor: '#e91550', // $crimson nonSelectBackgroundColor: '#ec2c50', }), - h('.network-name', 'Ropsten Test Net'), + h('.network-name', t('ropsten')), h('i.fa.fa-chevron-down.fa-lg.network-caret'), ]) case 'kovan-test-network': @@ -102,7 +103,7 @@ Network.prototype.render = function () { backgroundColor: '#690496', // $purple nonSelectBackgroundColor: '#b039f3', }), - h('.network-name', 'Kovan Test Net'), + h('.network-name', t('kovan')), h('i.fa.fa-chevron-down.fa-lg.network-caret'), ]) case 'rinkeby-test-network': @@ -111,7 +112,7 @@ Network.prototype.render = function () { backgroundColor: '#ebb33f', // $tulip-tree nonSelectBackgroundColor: '#ecb23e', }), - h('.network-name', 'Rinkeby Test Net'), + h('.network-name', t('rinkeby')), h('i.fa.fa-chevron-down.fa-lg.network-caret'), ]) default: @@ -123,7 +124,7 @@ Network.prototype.render = function () { }, }), - h('.network-name', 'Private Network'), + h('.network-name', t('privateNetwork')), h('i.fa.fa-chevron-down.fa-lg.network-caret'), ]) } diff --git a/ui/app/components/notice.js b/ui/app/components/notice.js index 9d2e89cb0..8b0ce1e8b 100644 --- a/ui/app/components/notice.js +++ b/ui/app/components/notice.js @@ -4,6 +4,7 @@ const h = require('react-hyperscript') const ReactMarkdown = require('react-markdown') const linker = require('extension-link-enabler') const findDOMNode = require('react-dom').findDOMNode +const t = require('../../i18n') module.exports = Notice @@ -110,7 +111,7 @@ Notice.prototype.render = function () { style: { marginTop: '18px', }, - }, 'Accept'), + }, t('accept')), ]) ) } diff --git a/ui/app/components/pending-msg-details.js b/ui/app/components/pending-msg-details.js index 718a22de0..87e66855d 100644 --- a/ui/app/components/pending-msg-details.js +++ b/ui/app/components/pending-msg-details.js @@ -1,6 +1,7 @@ const Component = require('react').Component const h = require('react-hyperscript') const inherits = require('util').inherits +const t = require('../../i18n') const AccountPanel = require('./account-panel') @@ -39,7 +40,7 @@ PendingMsgDetails.prototype.render = function () { // message data h('.tx-data.flex-column.flex-justify-center.flex-grow.select-none', [ h('.flex-column.flex-space-between', [ - h('label.font-small', 'MESSAGE'), + h('label.font-small.allcaps', t('message')), h('span.font-small', msgParams.data), ]), ]), @@ -47,4 +48,3 @@ PendingMsgDetails.prototype.render = function () { ]) ) } - diff --git a/ui/app/components/pending-msg.js b/ui/app/components/pending-msg.js index 834719c53..dc406b955 100644 --- a/ui/app/components/pending-msg.js +++ b/ui/app/components/pending-msg.js @@ -2,6 +2,7 @@ const Component = require('react').Component const h = require('react-hyperscript') const inherits = require('util').inherits const PendingTxDetails = require('./pending-msg-details') +const t = require('../../i18n') module.exports = PendingMsg @@ -29,17 +30,14 @@ PendingMsg.prototype.render = function () { fontWeight: 'bold', textAlign: 'center', }, - }, 'Sign Message'), + }, t('signMessage')), h('.error', { style: { margin: '10px', }, }, [ - `Signing this message can have - dangerous side effects. Only sign messages from - sites you fully trust with your entire account. - This dangerous method will be removed in a future version. `, + t('signNotice'), h('a', { href: 'https://medium.com/metamask/the-new-secure-way-to-sign-data-in-your-browser-6af9dd2a1527', style: { color: 'rgb(247, 134, 28)' }, @@ -48,7 +46,7 @@ PendingMsg.prototype.render = function () { const url = 'https://medium.com/metamask/the-new-secure-way-to-sign-data-in-your-browser-6af9dd2a1527' global.platform.openWindow({ url }) }, - }, 'Read more here.'), + }, t('readMore')), ]), // message details @@ -58,13 +56,12 @@ PendingMsg.prototype.render = function () { h('.flex-row.flex-space-around', [ h('button', { onClick: state.cancelMessage, - }, 'Cancel'), + }, t('cancel')), h('button', { onClick: state.signMessage, - }, 'Sign'), + }, t('sign')), ]), ]) ) } - diff --git a/ui/app/components/pending-personal-msg-details.js b/ui/app/components/pending-personal-msg-details.js index 1050513f2..b896e9a7e 100644 --- a/ui/app/components/pending-personal-msg-details.js +++ b/ui/app/components/pending-personal-msg-details.js @@ -1,6 +1,7 @@ const Component = require('react').Component const h = require('react-hyperscript') const inherits = require('util').inherits +const t = require('../../i18n') const AccountPanel = require('./account-panel') const BinaryRenderer = require('./binary-renderer') @@ -45,7 +46,7 @@ PendingMsgDetails.prototype.render = function () { height: '260px', }, }, [ - h('label.font-small', { style: { display: 'block' } }, 'MESSAGE'), + h('label.font-small.allcaps', { style: { display: 'block' } }, t('message')), h(BinaryRenderer, { value: data, style: { @@ -57,4 +58,3 @@ PendingMsgDetails.prototype.render = function () { ]) ) } - diff --git a/ui/app/components/pending-tx/confirm-deploy-contract.js b/ui/app/components/pending-tx/confirm-deploy-contract.js index ae6c6ef7b..49fbe6387 100644 --- a/ui/app/components/pending-tx/confirm-deploy-contract.js +++ b/ui/app/components/pending-tx/confirm-deploy-contract.js @@ -9,6 +9,7 @@ const ethUtil = require('ethereumjs-util') const BN = ethUtil.BN const hexToBn = require('../../../../app/scripts/lib/hex-to-bn') const { conversionUtil } = require('../../conversion-util') +const t = require('../../../i18n') const { MIN_GAS_PRICE_HEX } = require('../send/send-constants') @@ -55,7 +56,7 @@ ConfirmDeployContract.prototype.onSubmit = function (event) { if (valid && this.verifyGasParams()) { this.props.sendTransaction(txMeta, event) } else { - this.props.dispatch(actions.displayWarning('Invalid Gas Parameters')) + this.props.dispatch(actions.displayWarning(t('invalidGasParams'))) this.setState({ submitting: false }) } } @@ -200,7 +201,7 @@ ConfirmDeployContract.prototype.renderGasFee = function () { return ( h('section.flex-row.flex-center.confirm-screen-row', [ - h('span.confirm-screen-label.confirm-screen-section-column', [ 'Gas Fee' ]), + h('span.confirm-screen-label.confirm-screen-section-column', [ t('gasFee') ]), h('div.confirm-screen-section-column', [ h('div.confirm-screen-row-info', `${fiatGas} ${currentCurrency.toUpperCase()}`), @@ -239,8 +240,8 @@ ConfirmDeployContract.prototype.renderTotalPlusGas = function () { return ( h('section.flex-row.flex-center.confirm-screen-total-box ', [ h('div.confirm-screen-section-column', [ - h('span.confirm-screen-label', [ 'Total ' ]), - h('div.confirm-screen-total-box__subtitle', [ 'Amount + Gas' ]), + h('span.confirm-screen-label', [ t('total') + ' ' ]), + h('div.confirm-screen-total-box__subtitle', [ t('amountPlusGas') ]), ]), h('div.confirm-screen-section-column', [ @@ -271,10 +272,10 @@ ConfirmDeployContract.prototype.render = function () { // Main Send token Card h('div.confirm-screen-wrapper.flex-column.flex-grow', [ h('h3.flex-center.confirm-screen-header', [ - h('button.confirm-screen-back-button', { + h('button.confirm-screen-back-button.allcaps', { onClick: () => backToAccountDetail(selectedAddress), - }, 'BACK'), - h('div.confirm-screen-title', 'Confirm Contract'), + }, t('back')), + h('div.confirm-screen-title', t('confirmContract')), h('div.confirm-screen-header-tip'), ]), h('div.flex-row.flex-center.confirm-screen-identicons', [ @@ -292,7 +293,7 @@ ConfirmDeployContract.prototype.render = function () { h('i.fa.fa-arrow-right.fa-lg'), h('div.confirm-screen-account-wrapper', [ h('i.fa.fa-file-text-o'), - h('span.confirm-screen-account-name', 'New Contract'), + h('span.confirm-screen-account-name', t('newContract')), h('span.confirm-screen-account-number', ' '), ]), ]), @@ -310,7 +311,7 @@ ConfirmDeployContract.prototype.render = function () { h('div.confirm-screen-rows', [ h('section.flex-row.flex-center.confirm-screen-row', [ - h('span.confirm-screen-label.confirm-screen-section-column', [ 'From' ]), + h('span.confirm-screen-label.confirm-screen-section-column', [ t('from') ]), h('div.confirm-screen-section-column', [ h('div.confirm-screen-row-info', fromName), h('div.confirm-screen-row-detail', `...${fromAddress.slice(fromAddress.length - 4)}`), @@ -318,9 +319,9 @@ ConfirmDeployContract.prototype.render = function () { ]), h('section.flex-row.flex-center.confirm-screen-row', [ - h('span.confirm-screen-label.confirm-screen-section-column', [ 'To' ]), + h('span.confirm-screen-label.confirm-screen-section-column', [ t('to') ]), h('div.confirm-screen-section-column', [ - h('div.confirm-screen-row-info', 'New Contract'), + h('div.confirm-screen-row-info', t('newContract')), ]), ]), @@ -335,12 +336,12 @@ ConfirmDeployContract.prototype.render = function () { onSubmit: this.onSubmit, }, [ // Cancel Button - h('div.cancel.btn-light.confirm-screen-cancel-button', { + h('div.cancel.btn-light.confirm-screen-cancel-button.allcaps', { onClick: (event) => this.cancel(event, txMeta), - }, 'CANCEL'), + }, t('cancel')), // Accept Button - h('button.confirm-screen-confirm-button', ['CONFIRM']), + h('button.confirm-screen-confirm-button.allcaps', [t('confirm')]), ]), ]) diff --git a/ui/app/components/pending-tx/confirm-send-ether.js b/ui/app/components/pending-tx/confirm-send-ether.js index 4666e047d..6624a378d 100644 --- a/ui/app/components/pending-tx/confirm-send-ether.js +++ b/ui/app/components/pending-tx/confirm-send-ether.js @@ -14,6 +14,7 @@ const { multiplyCurrencies, } = require('../../conversion-util') const GasFeeDisplay = require('../send/gas-fee-display-v2') +const t = require('../../../i18n') const { MIN_GAS_PRICE_HEX } = require('../send/send-constants') @@ -197,7 +198,7 @@ ConfirmSendEther.prototype.getData = function () { }, to: { address: txParams.to, - name: identities[txParams.to] ? identities[txParams.to].name : 'New Recipient', + name: identities[txParams.to] ? identities[txParams.to].name : t('newRecipient'), }, memo: txParams.memo || '', gasFeeInFIAT, @@ -311,7 +312,7 @@ ConfirmSendEther.prototype.render = function () { h('div.confirm-screen-rows', [ h('section.flex-row.flex-center.confirm-screen-row', [ - h('span.confirm-screen-label.confirm-screen-section-column', [ 'From' ]), + h('span.confirm-screen-label.confirm-screen-section-column', [ t('from') ]), h('div.confirm-screen-section-column', [ h('div.confirm-screen-row-info', fromName), h('div.confirm-screen-row-detail', `...${fromAddress.slice(fromAddress.length - 4)}`), @@ -319,7 +320,7 @@ ConfirmSendEther.prototype.render = function () { ]), h('section.flex-row.flex-center.confirm-screen-row', [ - h('span.confirm-screen-label.confirm-screen-section-column', [ 'To' ]), + h('span.confirm-screen-label.confirm-screen-section-column', [ t('to') ]), h('div.confirm-screen-section-column', [ h('div.confirm-screen-row-info', toName), h('div.confirm-screen-row-detail', `...${toAddress.slice(toAddress.length - 4)}`), @@ -327,7 +328,7 @@ ConfirmSendEther.prototype.render = function () { ]), h('section.flex-row.flex-center.confirm-screen-row', [ - h('span.confirm-screen-label.confirm-screen-section-column', [ 'Gas Fee' ]), + h('span.confirm-screen-label.confirm-screen-section-column', [ t('gasFee') ]), h('div.confirm-screen-section-column', [ h(GasFeeDisplay, { gasTotal: gasTotal || gasFeeInHex, @@ -341,8 +342,8 @@ ConfirmSendEther.prototype.render = function () { h('section.flex-row.flex-center.confirm-screen-total-box ', [ h('div.confirm-screen-section-column', [ - h('span.confirm-screen-label', [ 'Total ' ]), - h('div.confirm-screen-total-box__subtitle', [ 'Amount + Gas' ]), + h('span.confirm-screen-label', [ t('total') + ' ' ]), + h('div.confirm-screen-total-box__subtitle', [ t('amountPlusGas') ]), ]), h('div.confirm-screen-section-column', [ @@ -437,15 +438,15 @@ ConfirmSendEther.prototype.render = function () { onSubmit: this.onSubmit, }, [ // Cancel Button - h('div.cancel.btn-light.confirm-screen-cancel-button', { + h('div.cancel.btn-light.confirm-screen-cancel-button.allcaps', { onClick: (event) => { clearSend() this.cancel(event, txMeta) }, - }, 'CANCEL'), + }, t('cancel')), // Accept Button - h('button.confirm-screen-confirm-button', ['CONFIRM']), + h('button.confirm-screen-confirm-button.allcaps', [t('confirm')]), ]), ]) ) @@ -460,7 +461,7 @@ ConfirmSendEther.prototype.onSubmit = function (event) { if (valid && this.verifyGasParams()) { this.props.sendTransaction(txMeta, event) } else { - this.props.dispatch(actions.displayWarning('Invalid Gas Parameters')) + this.props.dispatch(actions.displayWarning(t('invalidGasParams'))) this.setState({ submitting: false }) } } diff --git a/ui/app/components/pending-tx/confirm-send-token.js b/ui/app/components/pending-tx/confirm-send-token.js index 476c1d259..ac773b1d2 100644 --- a/ui/app/components/pending-tx/confirm-send-token.js +++ b/ui/app/components/pending-tx/confirm-send-token.js @@ -6,6 +6,7 @@ const tokenAbi = require('human-standard-token-abi') const abiDecoder = require('abi-decoder') abiDecoder.addABI(tokenAbi) const actions = require('../../actions') +const t = require('../../../i18n') const clone = require('clone') const Identicon = require('../identicon') const GasFeeDisplay = require('../send/gas-fee-display-v2.js') @@ -64,8 +65,8 @@ function mapDispatchToProps (dispatch, ownProps) { updateTokenExchangeRate: () => dispatch(actions.updateTokenExchangeRate(symbol)), editTransaction: txMeta => { const { token: { address } } = ownProps - const { txParams, id } = txMeta - const tokenData = txParams.data && abiDecoder.decodeMethod(txParams.data) + const { txParams = {}, id } = txMeta + const tokenData = txParams.data && abiDecoder.decodeMethod(txParams.data) || {} const { params = [] } = tokenData const { value: to } = params[0] || {} const { value: tokenAmountInDec } = params[1] || {} @@ -169,7 +170,7 @@ ConfirmSendToken.prototype.getAmount = function () { ? +(sendTokenAmount * tokenExchangeRate * conversionRate).toFixed(2) : null, token: typeof value === 'undefined' - ? 'Unknown' + ? t('unknown') : +sendTokenAmount.toFixed(decimals), } @@ -241,7 +242,7 @@ ConfirmSendToken.prototype.getData = function () { }, to: { address: value, - name: identities[value] ? identities[value].name : 'New Recipient', + name: identities[value] ? identities[value].name : t('newRecipient'), }, memo: txParams.memo || '', } @@ -287,7 +288,7 @@ ConfirmSendToken.prototype.renderGasFee = function () { return ( h('section.flex-row.flex-center.confirm-screen-row', [ - h('span.confirm-screen-label.confirm-screen-section-column', [ 'Gas Fee' ]), + h('span.confirm-screen-label.confirm-screen-section-column', [ t('gasFee') ]), h('div.confirm-screen-section-column', [ h(GasFeeDisplay, { gasTotal: gasTotal || gasFeeInHex, @@ -309,8 +310,8 @@ ConfirmSendToken.prototype.renderTotalPlusGas = function () { ? ( h('section.flex-row.flex-center.confirm-screen-total-box ', [ h('div.confirm-screen-section-column', [ - h('span.confirm-screen-label', [ 'Total ' ]), - h('div.confirm-screen-total-box__subtitle', [ 'Amount + Gas' ]), + h('span.confirm-screen-label', [ t('total') + ' ' ]), + h('div.confirm-screen-total-box__subtitle', [ t('amountPlusGas') ]), ]), h('div.confirm-screen-section-column', [ @@ -322,13 +323,13 @@ ConfirmSendToken.prototype.renderTotalPlusGas = function () { : ( h('section.flex-row.flex-center.confirm-screen-total-box ', [ h('div.confirm-screen-section-column', [ - h('span.confirm-screen-label', [ 'Total ' ]), - h('div.confirm-screen-total-box__subtitle', [ 'Amount + Gas' ]), + h('span.confirm-screen-label', [ t('total') + ' ' ]), + h('div.confirm-screen-total-box__subtitle', [ t('amountPlusGas') ]), ]), h('div.confirm-screen-section-column', [ h('div.confirm-screen-row-info', `${tokenAmount} ${symbol}`), - h('div.confirm-screen-row-detail', `+ ${fiatGas} ${currentCurrency} Gas`), + h('div.confirm-screen-row-detail', `+ ${fiatGas} ${currentCurrency} ${t('gas')}`), ]), ]) ) @@ -350,10 +351,10 @@ ConfirmSendToken.prototype.render = function () { this.inputs = [] - const title = txMeta.lastGasPrice ? 'Reprice Transaction' : 'Confirm' + const title = txMeta.lastGasPrice ? 'Reprice Transaction' : t('confirm') const subtitle = txMeta.lastGasPrice ? 'Increase your gas fee to attempt to overwrite and speed up your transaction' - : 'Please review your transaction.' + : t('pleaseReviewTransaction') return ( h('div.confirm-screen-container.confirm-send-token', [ @@ -362,7 +363,7 @@ ConfirmSendToken.prototype.render = function () { h('div.page-container__header', [ !txMeta.lastGasPrice && h('button.confirm-screen-back-button', { onClick: () => editTransaction(txMeta), - }, 'Edit'), + }, t('edit')), h('div.page-container__title', title), h('div.page-container__subtitle', subtitle), ]), @@ -405,7 +406,7 @@ ConfirmSendToken.prototype.render = function () { h('div.confirm-screen-rows', [ h('section.flex-row.flex-center.confirm-screen-row', [ - h('span.confirm-screen-label.confirm-screen-section-column', [ 'From' ]), + h('span.confirm-screen-label.confirm-screen-section-column', [ t('from') ]), h('div.confirm-screen-section-column', [ h('div.confirm-screen-row-info', fromName), h('div.confirm-screen-row-detail', `...${fromAddress.slice(fromAddress.length - 4)}`), @@ -413,7 +414,7 @@ ConfirmSendToken.prototype.render = function () { ]), toAddress && h('section.flex-row.flex-center.confirm-screen-row', [ - h('span.confirm-screen-label.confirm-screen-section-column', [ 'To' ]), + h('span.confirm-screen-label.confirm-screen-section-column', [ t('to') ]), h('div.confirm-screen-section-column', [ h('div.confirm-screen-row-info', toName), h('div.confirm-screen-row-detail', `...${toAddress.slice(toAddress.length - 4)}`), @@ -431,12 +432,12 @@ ConfirmSendToken.prototype.render = function () { onSubmit: this.onSubmit, }, [ // Cancel Button - h('div.cancel.btn-light.confirm-screen-cancel-button', { + h('div.cancel.btn-light.confirm-screen-cancel-button.allcaps', { onClick: (event) => this.cancel(event, txMeta), - }, 'CANCEL'), + }, t('cancel')), // Accept Button - h('button.confirm-screen-confirm-button', ['CONFIRM']), + h('button.confirm-screen-confirm-button.allcaps', [t('confirm')]), ]), @@ -453,7 +454,7 @@ ConfirmSendToken.prototype.onSubmit = function (event) { if (valid && this.verifyGasParams()) { this.props.sendTransaction(txMeta, event) } else { - this.props.dispatch(actions.displayWarning('Invalid Gas Parameters')) + this.props.dispatch(actions.displayWarning(t('invalidGasParams'))) this.setState({ submitting: false }) } } diff --git a/ui/app/components/pending-typed-msg-details.js b/ui/app/components/pending-typed-msg-details.js index b5fd29f71..ae0a1171e 100644 --- a/ui/app/components/pending-typed-msg-details.js +++ b/ui/app/components/pending-typed-msg-details.js @@ -4,6 +4,7 @@ const inherits = require('util').inherits const AccountPanel = require('./account-panel') const TypedMessageRenderer = require('./typed-message-renderer') +const t = require('../../i18n') module.exports = PendingMsgDetails @@ -45,7 +46,7 @@ PendingMsgDetails.prototype.render = function () { height: '260px', }, }, [ - h('label.font-small', { style: { display: 'block' } }, 'YOU ARE SIGNING'), + h('label.font-small.allcaps', { style: { display: 'block' } }, t('youSign')), h(TypedMessageRenderer, { value: data, style: { diff --git a/ui/app/components/pending-typed-msg.js b/ui/app/components/pending-typed-msg.js index f8926d0a3..ccde5e8af 100644 --- a/ui/app/components/pending-typed-msg.js +++ b/ui/app/components/pending-typed-msg.js @@ -2,6 +2,7 @@ const Component = require('react').Component const h = require('react-hyperscript') const inherits = require('util').inherits const PendingTxDetails = require('./pending-typed-msg-details') +const t = require('../../i18n') module.exports = PendingMsg @@ -26,19 +27,19 @@ PendingMsg.prototype.render = function () { fontWeight: 'bold', textAlign: 'center', }, - }, 'Sign Message'), + }, t('signMessage')), // message details h(PendingTxDetails, state), // sign + cancel h('.flex-row.flex-space-around', [ - h('button', { + h('button.allcaps', { onClick: state.cancelTypedMessage, - }, 'Cancel'), - h('button', { + }, t('cancel')), + h('button.allcaps', { onClick: state.signTypedMessage, - }, 'Sign'), + }, t('sign')), ]), ]) diff --git a/ui/app/components/send-token/index.js b/ui/app/components/send-token/index.js index 99d078251..58743b641 100644 --- a/ui/app/components/send-token/index.js +++ b/ui/app/components/send-token/index.js @@ -7,6 +7,7 @@ const inherits = require('util').inherits const actions = require('../../actions') const selectors = require('../../selectors') const { isValidAddress, allNull } = require('../../util') +const t = require('../../../i18n') // const BalanceComponent = require('./balance-component') const Identicon = require('../identicon') @@ -126,14 +127,14 @@ SendTokenScreen.prototype.validate = function () { const amount = Number(stringAmount) const errors = { - to: !to ? 'Required' : null, - amount: !amount ? 'Required' : null, - gasPrice: !gasPrice ? 'Gas Price Required' : null, - gasLimit: !gasLimit ? 'Gas Limit Required' : null, + to: !to ? t('required') : null, + amount: !amount ? t('required') : null, + gasPrice: !gasPrice ? t('gasPriceRequired') : null, + gasLimit: !gasLimit ? t('gasLimitRequired') : null, } if (to && !isValidAddress(to)) { - errors.to = 'Invalid address' + errors.to = t('invalidAddress') } const isValid = Object.entries(errors).every(([key, value]) => value === null) @@ -233,11 +234,11 @@ SendTokenScreen.prototype.renderToAddressInput = function () { 'send-screen-input-wrapper--error': errorMessage, }), }, [ - h('div', ['To:']), + h('div', [t('to') + ':']), h('input.large-input.send-screen-input', { name: 'address', list: 'addresses', - placeholder: 'Address', + placeholder: t('address'), value: to, onChange: e => this.setState({ to: e.target.value, @@ -290,7 +291,7 @@ SendTokenScreen.prototype.renderAmountInput = function () { }), }, [ h('div.send-screen-amount-labels', [ - h('span', ['Amount']), + h('span', [t('amount')]), h(CurrencyToggle, { currentCurrency: tokenExchangeRate ? selectedCurrency : 'USD', currencies: tokenExchangeRate ? [ symbol, 'USD' ] : [], @@ -355,8 +356,8 @@ SendTokenScreen.prototype.renderGasInput = function () { }), h('div.send-screen-gas-labels', {}, [ - h('span', [ h('i.fa.fa-bolt'), 'Gas fee:']), - h('span', ['What\'s this?']), + h('span', [ h('i.fa.fa-bolt'), t('gasFee') + ':']), + h('span', [t('whatsThis')]), ]), h('div.large-input.send-screen-gas-input', [ h(GasFeeDisplay, { @@ -370,7 +371,7 @@ SendTokenScreen.prototype.renderGasInput = function () { h( 'div.send-screen-gas-input-customize', { onClick: () => this.setState({ isGasTooltipOpen: !isGasTooltipOpen }) }, - ['Customize'] + [t('customize')] ), ]), h('div.send-screen-input-wrapper__error-message', [ @@ -381,7 +382,7 @@ SendTokenScreen.prototype.renderGasInput = function () { SendTokenScreen.prototype.renderMemoInput = function () { return h('div.send-screen-input-wrapper', [ - h('div', {}, ['Transaction memo (optional)']), + h('div', {}, [t('transactionMemo')]), h( 'input.large-input.send-screen-input', { onChange: e => this.setState({ memo: e.target.value }) } @@ -397,10 +398,10 @@ SendTokenScreen.prototype.renderButtons = function () { h('button.send-token__button-next.btn-secondary', { className: !isValid && 'send-screen__send-button__disabled', onClick: () => isValid && this.submit(), - }, ['Next']), + }, [t('next')]), h('button.send-token__button-cancel.btn-tertiary', { onClick: () => backToAccountDetail(selectedAddress), - }, ['Cancel']), + }, [t('cancel')]), ]) } @@ -417,9 +418,9 @@ SendTokenScreen.prototype.render = function () { diameter: 75, address: selectedTokenAddress, }), - h('div.send-token__title', ['Send Tokens']), - h('div.send-token__description', ['Send Tokens to anyone with an Ethereum account']), - h('div.send-token__balance-text', ['Your Token Balance is:']), + h('div.send-token__title', [t('sendTokens')]), + h('div.send-token__description', [t('sendTokensAnywhere')]), + h('div.send-token__balance-text', [t('tokenBalance')]), h('div.send-token__token-balance', [ h(TokenBalance, { token: selectedToken, balanceOnly: true }), ]), diff --git a/ui/app/components/send/gas-fee-display-v2.js b/ui/app/components/send/gas-fee-display-v2.js index da1bdd05e..ca7fbd9a0 100644 --- a/ui/app/components/send/gas-fee-display-v2.js +++ b/ui/app/components/send/gas-fee-display-v2.js @@ -2,6 +2,7 @@ const Component = require('react').Component const h = require('react-hyperscript') const inherits = require('util').inherits const CurrencyDisplay = require('./currency-display') +const t = require('../../../i18n') module.exports = GasFeeDisplay @@ -30,7 +31,7 @@ GasFeeDisplay.prototype.render = function () { convertedPrefix: '$', readOnly: true, }) - : h('div.currency-display', 'Loading...'), + : h('div.currency-display', t('loading')), h('button.sliders-icon-container', { onClick, @@ -41,4 +42,3 @@ GasFeeDisplay.prototype.render = function () { ]) } - diff --git a/ui/app/components/send/gas-tooltip.js b/ui/app/components/send/gas-tooltip.js index 46aff3499..d925d3ed8 100644 --- a/ui/app/components/send/gas-tooltip.js +++ b/ui/app/components/send/gas-tooltip.js @@ -2,6 +2,7 @@ const Component = require('react').Component const h = require('react-hyperscript') const inherits = require('util').inherits const InputNumber = require('../input-number.js') +const t = require('../../../i18n') module.exports = GasTooltip @@ -81,7 +82,7 @@ GasTooltip.prototype.render = function () { 'marginTop': '81px', }, }, [ - h('span.gas-tooltip-label', {}, ['Gas Limit']), + h('span.gas-tooltip-label', {}, [t('gasLimit')]), h('i.fa.fa-info-circle'), ]), h(InputNumber, { @@ -97,4 +98,3 @@ GasTooltip.prototype.render = function () { ]), ]) } - diff --git a/ui/app/components/send/to-autocomplete.js b/ui/app/components/send/to-autocomplete.js index e0cdd0a58..72074229e 100644 --- a/ui/app/components/send/to-autocomplete.js +++ b/ui/app/components/send/to-autocomplete.js @@ -2,6 +2,7 @@ const Component = require('react').Component const h = require('react-hyperscript') const inherits = require('util').inherits const AccountListItem = require('./account-list-item') +const t = require('../../../i18n') module.exports = ToAutoComplete @@ -92,7 +93,7 @@ ToAutoComplete.prototype.render = function () { return h('div.send-v2__to-autocomplete', {}, [ h('input.send-v2__to-autocomplete__input', { - placeholder: 'Recipient Address', + placeholder: t('recipientAddress'), className: inError ? `send-v2__error-border` : '', value: to, onChange: event => onChange(event.target.value), @@ -111,4 +112,3 @@ ToAutoComplete.prototype.render = function () { ]) } - diff --git a/ui/app/components/shapeshift-form.js b/ui/app/components/shapeshift-form.js index 87eb1588a..f75d6176e 100644 --- a/ui/app/components/shapeshift-form.js +++ b/ui/app/components/shapeshift-form.js @@ -7,6 +7,7 @@ const { qrcode } = require('qrcode-npm') const { shapeShiftSubview, pairUpdate, buyWithShapeShift } = require('../actions') const { isValidAddress } = require('../util') const SimpleDropdown = require('./dropdowns/simple-dropdown') +const t = require('../../i18n') function mapStateToProps (state) { const { @@ -15,7 +16,7 @@ function mapStateToProps (state) { selectedAddress, } = state.metamask const { warning } = state.appState - + return { coinOptions, tokenExchangeRates, @@ -93,7 +94,7 @@ ShapeshiftForm.prototype.onBuyWithShapeShift = function () { })) .catch(() => this.setState({ showQrCode: false, - errorMessage: 'Invalid Request', + errorMessage: t('invalidRequest'), isLoading: false, })) } @@ -125,10 +126,10 @@ ShapeshiftForm.prototype.renderMarketInfo = function () { return h('div.shapeshift-form__metadata', {}, [ - this.renderMetadata('Status', limit ? 'Available' : 'Unavailable'), - this.renderMetadata('Limit', limit), - this.renderMetadata('Exchange Rate', rate), - this.renderMetadata('Minimum', minimum), + this.renderMetadata(t('status'), limit ? t('available') : t('unavailable')), + this.renderMetadata(t('limit'), limit), + this.renderMetadata(t('exchangeRate'), rate), + this.renderMetadata(t('min'), minimum), ]) } @@ -142,7 +143,7 @@ ShapeshiftForm.prototype.renderQrCode = function () { return h('div.shapeshift-form', {}, [ h('div.shapeshift-form__deposit-instruction', [ - `Deposit your ${depositCoin.toUpperCase()} to the address below:`, + t('depositCoin', depositCoin.toUpperCase()), ]), h('div', depositAddress), @@ -179,7 +180,7 @@ ShapeshiftForm.prototype.render = function () { h('div.shapeshift-form__selector', [ - h('div.shapeshift-form__selector-label', 'Deposit'), + h('div.shapeshift-form__selector-label', t('deposit')), h(SimpleDropdown, { selectedOption: this.state.depositCoin, @@ -199,7 +200,7 @@ ShapeshiftForm.prototype.render = function () { h('div.shapeshift-form__selector', [ h('div.shapeshift-form__selector-label', [ - 'Receive', + t('receive'), ]), h('div.shapeshift-form__selector-input', ['ETH']), @@ -217,7 +218,7 @@ ShapeshiftForm.prototype.render = function () { }, [ h('div.shapeshift-form__address-input-label', [ - 'Your Refund Address', + t('refundAddress'), ]), h('input.shapeshift-form__address-input', { @@ -239,7 +240,7 @@ ShapeshiftForm.prototype.render = function () { className: btnClass, disabled: !token, onClick: () => this.onBuyWithShapeShift(), - }, ['Buy']), + }, [t('buy')]), ]) } diff --git a/ui/app/components/shift-list-item.js b/ui/app/components/shift-list-item.js index 017bf9f0c..fddbc6821 100644 --- a/ui/app/components/shift-list-item.js +++ b/ui/app/components/shift-list-item.js @@ -6,6 +6,7 @@ const vreme = new (require('vreme'))() const explorerLink = require('etherscan-link').createExplorerLink const actions = require('../actions') const addressSummary = require('../util').addressSummary +const t = require('../../i18n') const CopyButton = require('./copyButton') const EthBalance = require('./eth-balance') @@ -75,7 +76,7 @@ ShiftListItem.prototype.renderUtilComponents = function () { value: this.props.depositAddress, }), h(Tooltip, { - title: 'QR Code', + title: t('qrCode'), }, [ h('i.fa.fa-qrcode.pointer.pop-hover', { onClick: () => props.dispatch(actions.reshowQrCode(props.depositAddress, props.depositType)), @@ -135,8 +136,8 @@ ShiftListItem.prototype.renderInfo = function () { color: '#ABA9AA', width: '100%', }, - }, `${props.depositType} to ETH via ShapeShift`), - h('div', 'No deposits received'), + }, t('toETHviaShapeShift', [props.depositType])), + h('div', t('noDeposits')), h('div', { style: { fontSize: 'x-small', @@ -158,8 +159,8 @@ ShiftListItem.prototype.renderInfo = function () { color: '#ABA9AA', width: '100%', }, - }, `${props.depositType} to ETH via ShapeShift`), - h('div', 'Conversion in progress'), + }, t('toETHviaShapeShift', [props.depositType])), + h('div', t('conversionProgress')), h('div', { style: { fontSize: 'x-small', @@ -184,7 +185,7 @@ ShiftListItem.prototype.renderInfo = function () { color: '#ABA9AA', width: '100%', }, - }, 'From ShapeShift'), + }, t('fromShapeShift')), h('div', formatDate(props.time)), h('div', { style: { @@ -196,7 +197,7 @@ ShiftListItem.prototype.renderInfo = function () { ]) case 'failed': - return h('span.error', '(Failed)') + return h('span.error', '(' + t('failed') + ')') default: return '' } diff --git a/ui/app/components/signature-request.js b/ui/app/components/signature-request.js index c5cc23aa8..7bf34e7b6 100644 --- a/ui/app/components/signature-request.js +++ b/ui/app/components/signature-request.js @@ -9,6 +9,7 @@ const classnames = require('classnames') const AccountDropdownMini = require('./dropdowns/account-dropdown-mini') const actions = require('../actions') +const t = require('../../i18n') const { conversionUtil } = require('../conversion-util') const { @@ -54,7 +55,7 @@ SignatureRequest.prototype.renderHeader = function () { h('div.request-signature__header-background'), - h('div.request-signature__header__text', 'Signature Request'), + h('div.request-signature__header__text', t('sigRequest')), h('div.request-signature__header__tip-container', [ h('div.request-signature__header__tip'), @@ -75,7 +76,7 @@ SignatureRequest.prototype.renderAccountDropdown = function () { return h('div.request-signature__account', [ - h('div.request-signature__account-text', ['Account:']), + h('div.request-signature__account-text', [t('account') + ':']), h(AccountDropdownMini, { selectedAccount, @@ -102,7 +103,7 @@ SignatureRequest.prototype.renderBalance = function () { return h('div.request-signature__balance', [ - h('div.request-signature__balance-text', ['Balance:']), + h('div.request-signature__balance-text', [t('balance')]), h('div.request-signature__balance-value', `${balanceInEther} ETH`), @@ -136,7 +137,7 @@ SignatureRequest.prototype.renderRequestInfo = function () { return h('div.request-signature__request-info', [ h('div.request-signature__headline', [ - `Your signature is being requested`, + t('yourSigRequested'), ]), ]) @@ -154,21 +155,18 @@ SignatureRequest.prototype.msgHexToText = function (hex) { SignatureRequest.prototype.renderBody = function () { let rows - let notice = 'You are signing:' + let notice = t('youSign') + ':' const { txData } = this.props const { type, msgParams: { data } } = txData if (type === 'personal_sign') { - rows = [{ name: 'Message', value: this.msgHexToText(data) }] + rows = [{ name: t('message'), value: this.msgHexToText(data) }] } else if (type === 'eth_signTypedData') { rows = data } else if (type === 'eth_sign') { - rows = [{ name: 'Message', value: data }] - notice = `Signing this message can have - dangerous side effects. Only sign messages from - sites you fully trust with your entire account. - This dangerous method will be removed in a future version. ` + rows = [{ name: t('message'), value: data }] + notice = t('signNotice') } return h('div.request-signature__body', {}, [ @@ -227,10 +225,10 @@ SignatureRequest.prototype.renderFooter = function () { return h('div.request-signature__footer', [ h('button.request-signature__footer__cancel-button', { onClick: cancel, - }, 'CANCEL'), + }, t('cancel')), h('button.request-signature__footer__sign-button', { onClick: sign, - }, 'SIGN'), + }, t('sign')), ]) } @@ -250,4 +248,3 @@ SignatureRequest.prototype.render = function () { ) } - diff --git a/ui/app/components/token-list.js b/ui/app/components/token-list.js index 8e06e0f27..01529aeda 100644 --- a/ui/app/components/token-list.js +++ b/ui/app/components/token-list.js @@ -5,6 +5,7 @@ const TokenTracker = require('eth-token-tracker') const TokenCell = require('./token-cell.js') const connect = require('react-redux').connect const selectors = require('../selectors') +const t = require('../../i18n') function mapStateToProps (state) { return { @@ -42,7 +43,7 @@ TokenList.prototype.render = function () { const { tokens, isLoading, error } = state if (isLoading) { - return this.message('Loading Tokens...') + return this.message(t('loadingTokens')) } if (error) { @@ -52,7 +53,7 @@ TokenList.prototype.render = function () { padding: '80px', }, }, [ - 'We had trouble loading your token balances. You can view them ', + t('troubleTokenBalances'), h('span.hotFix', { style: { color: 'rgba(247, 134, 28, 1)', @@ -63,7 +64,7 @@ TokenList.prototype.render = function () { url: `https://ethplorer.io/address/${userAddress}`, }) }, - }, 'here'), + }, t('here')), ]) } diff --git a/ui/app/components/transaction-list-item.js b/ui/app/components/transaction-list-item.js index a45cd441a..6d6e79bd5 100644 --- a/ui/app/components/transaction-list-item.js +++ b/ui/app/components/transaction-list-item.js @@ -11,6 +11,7 @@ const vreme = new (require('vreme'))() const Tooltip = require('./tooltip') const numberToBN = require('number-to-bn') const actions = require('../actions') +const t = require('../../i18n') const TransactionIcon = require('./transaction-list-item-icon') const ShiftListItem = require('./shift-list-item') @@ -85,7 +86,7 @@ TransactionListItem.prototype.render = function () { ]), h(Tooltip, { - title: 'Transaction Number', + title: t('transactionNumber'), position: 'right', }, [ h('span', { @@ -142,12 +143,12 @@ TransactionListItem.prototype.render = function () { style: { paddingRight: '2px', }, - }, 'Taking too long?'), + }, t('takesTooLong')), h('div', { style: { textDecoration: 'underline', }, - }, 'Retry with a higher gas price here'), + }, t('retryWithMoreGas')), ]), ]) ) @@ -176,11 +177,11 @@ function recipientField (txParams, transaction, isTx, isMsg) { let message if (isMsg) { - message = 'Signature Requested' + message = t('sigRequested') } else if (txParams.to) { message = addressSummary(txParams.to) } else { - message = 'Contract Deployment' + message = t('contractDeployment') } return h('div', { @@ -203,7 +204,7 @@ function renderErrorOrWarning (transaction) { // show rejected if (status === 'rejected') { - return h('span.error', ' (Rejected)') + return h('span.error', ' (' + t('rejected') + ')') } if (transaction.err || transaction.warning) { const { err, warning = {} } = transaction @@ -219,7 +220,7 @@ function renderErrorOrWarning (transaction) { title: message, position: 'bottom', }, [ - h(`span.error`, ` (Failed)`), + h(`span.error`, ` (` + t('failed') + `)`), ]) ) } @@ -231,7 +232,7 @@ function renderErrorOrWarning (transaction) { title: message, position: 'bottom', }, [ - h(`span.warning`, ` (Warning)`), + h(`span.warning`, ` (` + t('warning') + `)`), ]) } } diff --git a/ui/app/components/transaction-list.js b/ui/app/components/transaction-list.js index 69b72614c..07f7a06ae 100644 --- a/ui/app/components/transaction-list.js +++ b/ui/app/components/transaction-list.js @@ -3,6 +3,7 @@ const h = require('react-hyperscript') const inherits = require('util').inherits const TransactionListItem = require('./transaction-list-item') +const t = require('../../i18n') module.exports = TransactionList @@ -78,10 +79,9 @@ TransactionList.prototype.render = function () { style: { marginTop: '50px', }, - }, 'No transaction history.'), + }, t('noTransactionHistory')), ]), ]), ]) ) } - diff --git a/ui/app/components/tx-list-item.js b/ui/app/components/tx-list-item.js index 4c2b77af9..5ff1820a6 100644 --- a/ui/app/components/tx-list-item.js +++ b/ui/app/components/tx-list-item.js @@ -14,6 +14,7 @@ const { conversionUtil, multiplyCurrencies } = require('../conversion-util') const { calcTokenAmount } = require('../token-util') const { getCurrentCurrency } = require('../selectors') +const t = require('../../i18n') module.exports = connect(mapStateToProps, mapDispatchToProps)(TxListItem) @@ -74,7 +75,7 @@ TxListItem.prototype.getAddressText = function () { default: return address ? `${address.slice(0, 10)}...${address.slice(-4)}` - : 'Contract Deployment' + : t('contractDeployment') } } diff --git a/ui/app/components/tx-list.js b/ui/app/components/tx-list.js index 782de0266..08e37ebc8 100644 --- a/ui/app/components/tx-list.js +++ b/ui/app/components/tx-list.js @@ -10,6 +10,7 @@ const { formatDate } = require('../util') const { showConfTxPage } = require('../actions') const classnames = require('classnames') const { tokenInfoGetter } = require('../token-util') +const t = require('../../i18n') module.exports = connect(mapStateToProps, mapDispatchToProps)(TxList) @@ -56,7 +57,7 @@ TxList.prototype.renderTransaction = function () { : [h( 'div.tx-list-item.tx-list-item--empty', { key: 'tx-list-none' }, - [ 'No Transactions' ], + [ t('noTransactions') ], )] } @@ -110,7 +111,7 @@ TxList.prototype.renderTransactionListItem = function (transaction, conversionRa if (isUnapproved) { opts.onClick = () => showConfTxPage({id: transactionId}) - opts.transactionStatus = 'Not Started' + opts.transactionStatus = t('Not Started') } else if (transactionHash) { opts.onClick = () => this.view(transactionHash, transactionNetworkId) } diff --git a/ui/app/components/tx-view.js b/ui/app/components/tx-view.js index b25d8e0f9..96d776270 100644 --- a/ui/app/components/tx-view.js +++ b/ui/app/components/tx-view.js @@ -5,6 +5,7 @@ const ethUtil = require('ethereumjs-util') const inherits = require('util').inherits const actions = require('../actions') const selectors = require('../selectors') +const t = require('../../i18n') const BalanceComponent = require('./balance-component') const TxList = require('./tx-list') @@ -68,25 +69,25 @@ TxView.prototype.renderButtons = function () { return !selectedToken ? ( h('div.flex-row.flex-center.hero-balance-buttons', [ - h('button.btn-clear.hero-balance-button', { + h('button.btn-clear.hero-balance-button.allcaps', { onClick: () => showModal({ name: 'DEPOSIT_ETHER', }), - }, 'DEPOSIT'), + }, t('deposit')), - h('button.btn-clear.hero-balance-button', { + h('button.btn-clear.hero-balance-button.allcaps', { style: { marginLeft: '0.8em', }, onClick: showSendPage, - }, 'SEND'), + }, t('send')), ]) ) : ( h('div.flex-row.flex-center.hero-balance-buttons', [ h('button.btn-clear.hero-balance-button', { onClick: showSendTokenPage, - }, 'SEND'), + }, t('send')), ]) ) } @@ -100,9 +101,10 @@ TxView.prototype.render = function () { h('div.flex-row.phone-visible', { style: { - margin: '1.5em 1.2em 0', justifyContent: 'space-between', alignItems: 'center', + flex: '0 0 auto', + margin: '10px', }, }, [ @@ -110,11 +112,10 @@ TxView.prototype.render = function () { style: { fontSize: '1.3em', cursor: 'pointer', + padding: '10px', }, - onClick: () => { - this.props.sidebarOpen ? this.props.hideSidebar() : this.props.showSidebar() - }, - }, []), + onClick: () => this.props.sidebarOpen ? this.props.hideSidebar() : this.props.showSidebar(), + }), h('.identicon-wrapper.select-none', { style: { diff --git a/ui/app/components/wallet-view.js b/ui/app/components/wallet-view.js index 34f27ca2a..18452205c 100644 --- a/ui/app/components/wallet-view.js +++ b/ui/app/components/wallet-view.js @@ -11,6 +11,7 @@ const actions = require('../actions') const BalanceComponent = require('./balance-component') const TokenList = require('./token-list') const selectors = require('../selectors') +const t = require('../../i18n') module.exports = connect(mapStateToProps, mapDispatchToProps)(WalletView) @@ -116,7 +117,7 @@ WalletView.prototype.render = function () { onClick: hideSidebar, }), - h('div.wallet-view__keyring-label', isLoose ? 'IMPORTED' : ''), + h('div.wallet-view__keyring-label.allcaps', isLoose ? t('imported') : ''), h('div.flex-column.flex-center.wallet-view__name-container', { style: { margin: '0 auto' }, @@ -133,13 +134,13 @@ WalletView.prototype.render = function () { selectedIdentity.name, ]), - h('button.btn-clear.wallet-view__details-button', 'DETAILS'), + h('button.btn-clear.wallet-view__details-button.allcaps', t('details')), ]), ]), h(Tooltip, { position: 'bottom', - title: this.state.hasCopied ? 'Copied!' : 'Copy to clipboard', + title: this.state.hasCopied ? t('copiedExclamation') : t('copyToClipboard'), wrapperClassName: 'wallet-view__tooltip', }, [ h('button.wallet-view__address', { @@ -172,7 +173,7 @@ WalletView.prototype.render = function () { showAddTokenPage() hideSidebar() }, - }, 'Add Token'), + }, t('addToken')), ]) } diff --git a/ui/app/css/itcss/components/hero-balance.scss b/ui/app/css/itcss/components/hero-balance.scss index 4af0c2c55..a3f051361 100644 --- a/ui/app/css/itcss/components/hero-balance.scss +++ b/ui/app/css/itcss/components/hero-balance.scss @@ -5,9 +5,6 @@ flex-direction: column; justify-content: flex-start; align-items: center; - margin: .3em .9em 0; - // height: 80vh; - // max-height: 225px; flex: 0 0 auto; } diff --git a/ui/app/css/itcss/components/index.scss b/ui/app/css/itcss/components/index.scss index 0219f9fb2..f107b7aca 100644 --- a/ui/app/css/itcss/components/index.scss +++ b/ui/app/css/itcss/components/index.scss @@ -55,3 +55,6 @@ @import './new-account.scss'; @import './tooltip.scss'; + +@import './welcome-screen.scss'; + diff --git a/ui/app/css/itcss/components/modal.scss b/ui/app/css/itcss/components/modal.scss index 53e3bff00..6c4106f8b 100644 --- a/ui/app/css/itcss/components/modal.scss +++ b/ui/app/css/itcss/components/modal.scss @@ -641,32 +641,40 @@ &__buy-rows { width: 100%; - padding: 33px; - padding-top: 0px; + padding: 0 30px; display: flex; flex-flow: column nowrap; flex: 1; - overflow-y: auto; @media screen and (max-width: 575px) { height: 0; } } + &__logo { + height: 60px; + background-repeat: no-repeat; + background-size: contain; + background-position: center; + width: 100%; + display: flex; + justify-content: center; + align-items: center; + } + &__buy-row { border-bottom: 1px solid $alto; display: flex; justify-content: space-between; align-items: center; - flex: 1; - padding-bottom: 25px; - padding-top: 25px; + flex: 1 0 auto; + padding: 30px 0 20px; + min-height: 170px; @media screen and (max-width: 575px) { - min-height: 360px; + min-height: 270px; flex-flow: column; - justify-content: center; - padding-top: 45px; + justify-content: flex-start; } &__back { @@ -692,30 +700,35 @@ } } - &__logo { + &__logo-container { display: flex; justify-content: center; - flex: 0.3 1 auto; + flex: 0 0 auto; + padding: 0 20px; - @media screen and (min-width: 575px) { - min-width: 215px; + @media screen and (min-width: 576px) { + width: 12rem; + } + + @media screen and (max-width: 575px) { + width: 100%; + max-height: 6rem; + padding-bottom: 20px; } } &__coinbase-logo { height: 40px; - width: 180px; } &__shapeshift-logo { height: 60px; - width: 174px; } &__eth-logo { border-radius: 50%; - width: 68px; height: 68px; + width: 68px; border: 3px solid $tundora; z-index: 25; padding: 4px; @@ -728,10 +741,11 @@ &__description { color: $cape-cod; - flex: 0.5 1 auto; + padding-bottom: 20px; + align-self: flex-start; @media screen and (min-width: 575px) { - min-width: 315px; + width: 15rem; } &__title { diff --git a/ui/app/css/itcss/components/new-account.scss b/ui/app/css/itcss/components/new-account.scss index c6c254ede..c2cefe4ad 100644 --- a/ui/app/css/itcss/components/new-account.scss +++ b/ui/app/css/itcss/components/new-account.scss @@ -161,15 +161,14 @@ display: flex; flex-flow: column; align-items: center; + padding: 30px 30px 0; &__input-label { color: $scorpion; font-family: Roboto; font-size: 16px; line-height: 21px; - margin-top: 29px; align-self: flex-start; - margin-left: 30px; } &__input { @@ -190,7 +189,7 @@ margin-top: 39px; display: flex; width: 100%; - justify-content: space-evenly; + justify-content: space-between; } &__button-cancel, diff --git a/ui/app/css/itcss/components/newui-sections.scss b/ui/app/css/itcss/components/newui-sections.scss index ecf5e1036..5cdda5e6c 100644 --- a/ui/app/css/itcss/components/newui-sections.scss +++ b/ui/app/css/itcss/components/newui-sections.scss @@ -51,6 +51,7 @@ $wallet-view-bg: $alabaster; cursor: pointer; display: flex; justify-content: center; + padding: 10px; } // wallet view and sidebar @@ -291,7 +292,6 @@ $wallet-view-bg: $alabaster; padding-right: 6px; } - // first time .first-view-main { display: flex; @@ -313,4 +313,23 @@ $wallet-view-bg: $alabaster; @media screen and (min-width: 1281px) { width: 62vw; } -} \ No newline at end of file +} + +.unlock-screen-container { + z-index: $main-container-z-index; + font-family: Roboto; + display: flex; + justify-content: center; + align-items: center; + flex: 1 0 auto; + background: #f7f7f7; + width: 100%; +} + +.unlock-screen { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + flex: 1 0 auto; +} diff --git a/ui/app/css/itcss/components/welcome-screen.scss b/ui/app/css/itcss/components/welcome-screen.scss new file mode 100644 index 000000000..bfd174ad9 --- /dev/null +++ b/ui/app/css/itcss/components/welcome-screen.scss @@ -0,0 +1,59 @@ +.welcome-screen { + display: flex; + flex-flow: column; + justify-content: center; + align-items: center; + font-family: Roboto; + font-weight: 400; + width: 100%; + flex: 1 0 auto; + padding: 70px 0; + background: $white; + + @media screen and (max-width: 575px) { + padding: 0; + } + + &__info { + display: flex; + flex-flow: column; + width: 100%; + height: 100%; + align-items: center; + + &__header { + font-size: 1.65em; + margin-bottom: 14px; + + @media screen and (max-width: 575px) { + font-size: 1.5em; + } + } + + &__copy { + font-size: 1em; + width: 400px; + max-width: 90vw; + text-align: center; + + @media screen and (max-width: 575px) { + font-size: 0.9em; + } + } + } + + &__button { + height: 54px; + width: 198px; + box-shadow: 0 2px 4px 0 rgba(0,0,0,0.14); + color: #FFFFFF; + font-size: 20px; + font-weight: 500; + line-height: 26px; + text-align: center; + text-transform: uppercase; + margin: 35px 0 14px; + transition: 200ms ease-in-out; + background-color: rgba(247, 134, 28, 0.9); + } +} diff --git a/ui/app/css/itcss/generic/index.scss b/ui/app/css/itcss/generic/index.scss index 9b3d7475b..0077cb661 100644 --- a/ui/app/css/itcss/generic/index.scss +++ b/ui/app/css/itcss/generic/index.scss @@ -70,6 +70,10 @@ input.large-input { height: 36px; } +.allcaps { + text-transform: uppercase; +} + .page-container { width: 400px; background-color: $white; @@ -77,25 +81,30 @@ input.large-input { z-index: 25; display: flex; flex-flow: column; + border-radius: 7px; + height: 100%; &__header { display: flex; flex-flow: column; border-bottom: 1px solid $geyser; - padding: 1.15rem 0.95rem; + padding: 20px; flex: 0 0 auto; - background: $alabaster; position: relative; } - &__header-close::after { - content: '\00D7'; - font-size: 40px; + &__header-close { color: $tundora; position: absolute; - top: 21.5px; - right: 28.5px; + top: 20px; + right: 20px; cursor: pointer; + overflow: hidden; + + &::after { + content: '\00D7'; + font-size: 40px; + } } &__footer { @@ -114,7 +123,7 @@ input.large-input { &__footer-button { width: 165px; - height: 60px; + height: 55px; font-size: 1rem; text-transform: uppercase; margin-right: 1rem; @@ -130,7 +139,7 @@ input.large-input { font-family: Roboto; font-size: 2rem; font-weight: 500; - line-height: initial; + line-height: 2rem; } &__subtitle { @@ -174,6 +183,15 @@ input.large-input { } } } + + &--full-width { + width: initial; + } + + &__content { + height: 100%; + overflow-y: auto; + } } @media screen and (max-width: 250px) { @@ -200,5 +218,6 @@ input.large-input { width: 100%; overflow-y: auto; background-color: $white; + border-radius: 0; } } diff --git a/ui/app/first-time/init-menu.js b/ui/app/first-time/init-menu.js index 0e08da8db..370fdd5b7 100644 --- a/ui/app/first-time/init-menu.js +++ b/ui/app/first-time/init-menu.js @@ -6,6 +6,7 @@ const h = require('react-hyperscript') const Mascot = require('../components/mascot') const actions = require('../actions') const Tooltip = require('../components/tooltip') +const t = require('../../i18n') const getCaretCoordinates = require('textarea-caret') const environmentType = require('../../../app/scripts/lib/environment-type') const { OLD_UI_NETWORK_TYPE } = require('../../../app/scripts/config').enums @@ -59,7 +60,7 @@ InitializeMenuScreen.prototype.renderMenu = function (state) { color: '#7F8082', marginBottom: 10, }, - }, 'MetaMask'), + }, t('appName')), h('div', [ @@ -69,10 +70,10 @@ InitializeMenuScreen.prototype.renderMenu = function (state) { color: '#7F8082', display: 'inline', }, - }, 'Encrypt your new DEN'), + }, t('encryptNewDen')), h(Tooltip, { - title: 'Your DEN is your password-encrypted storage within MetaMask.', + title: t('denExplainer'), }, [ h('i.fa.fa-question-circle.pointer', { style: { @@ -92,7 +93,7 @@ InitializeMenuScreen.prototype.renderMenu = function (state) { h('input.large-input.letter-spacey', { type: 'password', id: 'password-box', - placeholder: 'New Password (min 8 chars)', + placeholder: t('newPassword'), onInput: this.inputChanged.bind(this), style: { width: 260, @@ -104,7 +105,7 @@ InitializeMenuScreen.prototype.renderMenu = function (state) { h('input.large-input.letter-spacey', { type: 'password', id: 'password-box-confirm', - placeholder: 'Confirm Password', + placeholder: t('confirmPassword'), onKeyPress: this.createVaultOnEnter.bind(this), onInput: this.inputChanged.bind(this), style: { @@ -119,7 +120,7 @@ InitializeMenuScreen.prototype.renderMenu = function (state) { style: { margin: 12, }, - }, 'Create'), + }, t('createDen')), h('.flex-row.flex-center.flex-grow', [ h('p.pointer', { @@ -129,7 +130,7 @@ InitializeMenuScreen.prototype.renderMenu = function (state) { color: 'rgb(247, 134, 28)', textDecoration: 'underline', }, - }, 'Import Existing DEN'), + }, t('importDen')), ]), h('.flex-row.flex-center.flex-grow', [ @@ -178,12 +179,12 @@ InitializeMenuScreen.prototype.createNewVaultAndKeychain = function () { var passwordConfirm = passwordConfirmBox.value if (password.length < 8) { - this.warning = 'password not long enough' + this.warning = t('passwordShort') this.props.dispatch(actions.displayWarning(this.warning)) return } if (password !== passwordConfirm) { - this.warning = 'passwords don\'t match' + this.warning = t('passwordMismatch') this.props.dispatch(actions.displayWarning(this.warning)) return } diff --git a/ui/app/keychains/hd/restore-vault.js b/ui/app/keychains/hd/restore-vault.js index d1761f17d..685094854 100644 --- a/ui/app/keychains/hd/restore-vault.js +++ b/ui/app/keychains/hd/restore-vault.js @@ -144,6 +144,19 @@ RestoreVaultScreen.prototype.createNewVaultAndRestore = function () { // check seed var seedBox = document.querySelector('textarea.twelve-word-phrase') var seed = seedBox.value.trim() + + // true if the string has more than a space between words. + if (seed.split(' ').length > 1) { + this.warning = 'there can only a space between words' + this.props.dispatch(actions.displayWarning(this.warning)) + return + } + // true if seed contains a character that is not between a-z or a space + if (!seed.match(/^[a-z ]+$/)) { + this.warning = 'seed words only have lowercase characters' + this.props.dispatch(actions.displayWarning(this.warning)) + return + } if (seed.split(' ').length !== 12) { this.warning = 'seed phrases are 12 words long' this.props.dispatch(actions.displayWarning(this.warning)) diff --git a/ui/app/main-container.js b/ui/app/main-container.js index 292abcc3d..eed4bd164 100644 --- a/ui/app/main-container.js +++ b/ui/app/main-container.js @@ -32,19 +32,7 @@ MainContainer.prototype.render = function () { return h(Settings, {key: 'config'}) default: log.debug('rendering locked screen') - contents = { - component: UnlockScreen, - style: { - boxShadow: 'none', - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - background: '#F7F7F7', - // must force 100%, because lock screen is full-width - width: '100%', - }, - key: 'locked', - } + return h('.unlock-screen-container', {}, h(UnlockScreen, { key: 'locked' })) } } diff --git a/ui/app/reducers/metamask.js b/ui/app/reducers/metamask.js index 2c93172cc..e6e02d057 100644 --- a/ui/app/reducers/metamask.js +++ b/ui/app/reducers/metamask.js @@ -45,6 +45,7 @@ function reduceMetamask (state, action) { featureFlags: {}, networkEndpointType: OLD_UI_NETWORK_TYPE, isRevealingSeedWords: false, + welcomeScreenSeen: false, }, state.metamask) switch (action.type) { @@ -349,6 +350,11 @@ function reduceMetamask (state, action) { networkEndpointType: action.value, }) + case actions.CLOSE_WELCOME_SCREEN: + return extend(metamaskState, { + welcomeScreenSeen: true, + }) + default: return metamaskState diff --git a/ui/app/unlock.js b/ui/app/unlock.js index cafe3a859..ac97d04d0 100644 --- a/ui/app/unlock.js +++ b/ui/app/unlock.js @@ -5,6 +5,7 @@ const connect = require('react-redux').connect const actions = require('./actions') const getCaretCoordinates = require('textarea-caret') const EventEmitter = require('events').EventEmitter +const t = require('../i18n') const { OLD_UI_NETWORK_TYPE } = require('../../app/scripts/config').enums const environmentType = require('../../app/scripts/lib/environment-type') @@ -28,83 +29,72 @@ UnlockScreen.prototype.render = function () { const state = this.props const warning = state.warning return ( - h('.flex-column', { - style: { - width: 'inherit', - }, - }, [ - h('.unlock-screen.flex-column.flex-center.flex-grow', [ + h('.unlock-screen', [ - h(Mascot, { - animationEventEmitter: this.animationEventEmitter, - }), + h(Mascot, { + animationEventEmitter: this.animationEventEmitter, + }), - h('h1', { - style: { - fontSize: '1.4em', - textTransform: 'uppercase', - color: '#7F8082', - }, - }, 'MetaMask'), + h('h1', { + style: { + fontSize: '1.4em', + textTransform: 'uppercase', + color: '#7F8082', + }, + }, t('appName')), - h('input.large-input', { - type: 'password', - id: 'password-box', - placeholder: 'enter password', - style: { - background: 'white', - }, - onKeyPress: this.onKeyPress.bind(this), - onInput: this.inputChanged.bind(this), - }), + h('input.large-input', { + type: 'password', + id: 'password-box', + placeholder: 'enter password', + style: { + background: 'white', + }, + onKeyPress: this.onKeyPress.bind(this), + onInput: this.inputChanged.bind(this), + }), - h('.error', { - style: { - display: warning ? 'block' : 'none', - padding: '0 20px', - textAlign: 'center', - }, - }, warning), + h('.error', { + style: { + display: warning ? 'block' : 'none', + padding: '0 20px', + textAlign: 'center', + }, + }, warning), - h('button.primary.cursor-pointer', { - onClick: this.onSubmit.bind(this), - style: { - margin: 10, - }, - }, 'Log In'), - ]), + h('button.primary.cursor-pointer', { + onClick: this.onSubmit.bind(this), + style: { + margin: 10, + }, + }, 'Log In'), - h('.flex-row.flex-center.flex-grow', [ - h('p.pointer', { - onClick: () => { - this.props.dispatch(actions.markPasswordForgotten()) - if (environmentType() === 'popup') { - global.platform.openExtensionInBrowser() - } - }, - style: { - fontSize: '0.8em', - color: 'rgb(247, 134, 28)', - textDecoration: 'underline', - }, - }, 'Restore from seed phrase'), - ]), - - h('.flex-row.flex-center.flex-grow', [ - h('p.pointer', { - onClick: () => { - this.props.dispatch(actions.setFeatureFlag('betaUI', false, 'OLD_UI_NOTIFICATION_MODAL')) - .then(() => this.props.dispatch(actions.setNetworkEndpoints(OLD_UI_NETWORK_TYPE))) - }, - style: { - fontSize: '0.8em', - color: '#aeaeae', - textDecoration: 'underline', - marginTop: '32px', - }, - }, 'Use classic interface'), - ]), + h('p.pointer', { + onClick: () => { + this.props.dispatch(actions.markPasswordForgotten()) + if (environmentType() === 'popup') { + global.platform.openExtensionInBrowser() + } + }, + style: { + fontSize: '0.8em', + color: 'rgb(247, 134, 28)', + textDecoration: 'underline', + }, + }, 'Restore from seed phrase'), + h('p.pointer', { + onClick: () => { + this.props.dispatch(actions.setFeatureFlag('betaUI', false, 'OLD_UI_NOTIFICATION_MODAL')) + .then(() => this.props.dispatch(actions.setNetworkEndpoints(OLD_UI_NETWORK_TYPE))) + }, + style: { + fontSize: '0.8em', + color: '#aeaeae', + textDecoration: 'underline', + marginTop: '32px', + }, + }, 'Use classic interface'), ]) ) } diff --git a/ui/app/welcome-screen.js b/ui/app/welcome-screen.js new file mode 100644 index 000000000..cdbb6dba8 --- /dev/null +++ b/ui/app/welcome-screen.js @@ -0,0 +1,56 @@ +import EventEmitter from 'events' +import h from 'react-hyperscript' +import { Component } from 'react' +import PropTypes from 'prop-types' +import {connect} from 'react-redux' +import {closeWelcomeScreen} from './actions' +import Mascot from './components/mascot' + +class WelcomeScreen extends Component { + static propTypes = { + closeWelcomeScreen: PropTypes.func.isRequired, + } + + constructor(props) { + super(props) + this.animationEventEmitter = new EventEmitter() + } + + initiateAccountCreation = () => { + this.props.closeWelcomeScreen() + } + + render () { + return h('div.welcome-screen', [ + + h('div.welcome-screen__info', [ + + h(Mascot, { + animationEventEmitter: this.animationEventEmitter, + width: '225', + height: '225', + }), + + h('div.welcome-screen__info__header', 'Welcome to MetaMask Beta'), + + h('div.welcome-screen__info__copy', 'MetaMask is a secure identity vault for Ethereum.'), + + h('div.welcome-screen__info__copy', `It allows you to hold ether & tokens, + and serves as your bridge to decentralized applications.`), + + h('button.welcome-screen__button', { + onClick: this.initiateAccountCreation, + }, 'Continue'), + + ]), + + ]) + } +} + +export default connect( + null, + dispatch => ({ + closeWelcomeScreen: () => dispatch(closeWelcomeScreen()), + }) +)(WelcomeScreen) diff --git a/ui/i18n.js b/ui/i18n.js new file mode 100644 index 000000000..abfece426 --- /dev/null +++ b/ui/i18n.js @@ -0,0 +1,33 @@ + +// cross-browser connection to extension i18n API + +const chrome = chrome || null +const browser = browser || null +const extension = require('extensionizer') +var log = require('loglevel') +window.log = log +let getMessage + +if (extension.i18n && extension.i18n.getMessage) { + getMessage = extension.i18n.getMessage +} else { + // fallback function + log.warn('browser.i18n API not available, calling back to english.') + const msg = require('../app/_locales/en/messages.json') + getMessage = function (key, substitutions) { + if (!msg[key]) { + log.error(key) + throw new Error(key) + } + let phrase = msg[key].message + if (substitutions && substitutions.length) { + phrase = phrase.replace(/\$1/g, substitutions[0]) + if (substitutions.length > 1) { + phrase = phrase.replace(/\$2/g, substitutions[1]) + } + } + return phrase + } +} + +module.exports = getMessage diff --git a/yarn.lock b/yarn.lock index cd7ebd844..888175c92 100644 --- a/yarn.lock +++ b/yarn.lock @@ -167,6 +167,10 @@ acorn@^4.0.3: version "4.0.13" resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787" +addressparser@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/addressparser/-/addressparser-1.0.1.tgz#47afbe1a2a9262191db6838e4fd1d39b40821746" + aes-js@^0.2.3: version "0.2.4" resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-0.2.4.tgz#94b881ab717286d015fa219e08fb66709dda5a3d" @@ -179,6 +183,13 @@ after@0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" +agent-base@2: + version "2.1.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-2.1.1.tgz#d6de10d5af6132d5bd692427d46fc538539094c7" + dependencies: + extend "~3.0.0" + semver "~5.0.1" + ajv-keywords@^1.1.1: version "1.5.1" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c" @@ -215,6 +226,16 @@ amdefine@>=0.0.4: version "1.0.1" resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" +amqplib@^0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/amqplib/-/amqplib-0.5.2.tgz#d2d7313c7ffaa4d10bcf1e6252de4591b6cc7b63" + dependencies: + bitsyntax "~0.0.4" + bluebird "^3.4.6" + buffer-more-ints "0.0.2" + readable-stream "1.x >=1.1.9" + safe-buffer "^5.0.1" + ansi-colors@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-1.0.1.tgz#e94c6c306005af8b482240241e2f3dea4b855ff3" @@ -444,6 +465,10 @@ arraybuffer.slice@0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz#f33b2159f0532a3f3107a272c0ccfbd1ad2979ca" +arraybuffer.slice@~0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675" + arrify@^1.0.0, arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" @@ -494,6 +519,10 @@ ast-types@0.9.6: version "0.9.6" resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.6.tgz#102c9e9e9005d3e7e3829bf0c4fa24ee862ee9b9" +ast-types@0.x.x: + version "0.11.2" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.11.2.tgz#cc4e1d15a36b39979a1986fe1e91321cbfae7783" + astw@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/astw/-/astw-2.2.0.tgz#7bd41784d32493987aeb239b6b4e1c57a873b917" @@ -529,6 +558,10 @@ async-foreach@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542" +async-limiter@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" + async-reduce@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/async-reduce/-/async-reduce-0.0.1.tgz#b236b5f376d6fae381cded9006aa7f2c73b17f31" @@ -561,6 +594,12 @@ async@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/async/-/async-1.0.0.tgz#f8fc04ca3a13784ade9e1641af98578cfbd647a9" +async@~2.1.2: + version "2.1.5" + resolved "https://registry.yarnpkg.com/async/-/async-2.1.5.tgz#e587c68580994ac67fc56ff86d3ac56bdbe810bc" + dependencies: + lodash "^4.14.0" + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -611,6 +650,12 @@ aws4@^1.2.1, aws4@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" +axios@^0.15.3: + version "0.15.3" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.15.3.tgz#2c9d638b2e191a08ea1d6cc988eadd6ba5bdc053" + dependencies: + follow-redirects "1.0.0" + babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" @@ -1525,12 +1570,24 @@ bip66@^1.1.3: dependencies: safe-buffer "^5.0.1" +bitsyntax@~0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/bitsyntax/-/bitsyntax-0.0.4.tgz#eb10cc6f82b8c490e3e85698f07e83d46e0cba82" + dependencies: + buffer-more-ints "0.0.2" + bl@^1.2.0, bl@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.1.tgz#cac328f7bee45730d404b692203fcb590e172d5e" dependencies: readable-stream "^2.0.5" +bl@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/bl/-/bl-1.1.2.tgz#fdca871a99713aa00d19e3bbba41c44787a65398" + dependencies: + readable-stream "~2.0.5" + blob@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.4.tgz#bcf13052ca54463f30f9fc7e95b9a47630a94921" @@ -1796,7 +1853,7 @@ browserify-zlib@^0.2.0, browserify-zlib@~0.2.0: dependencies: pako "~1.0.5" -browserify@^14.0.0, browserify@^14.4.0: +browserify@^14.0.0, browserify@^14.4.0, browserify@^14.5.0: version "14.5.0" resolved "https://registry.yarnpkg.com/browserify/-/browserify-14.5.0.tgz#0bbbce521acd6e4d1d54d8e9365008efb85a9cc5" dependencies: @@ -1891,6 +1948,10 @@ buffer-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-1.0.0.tgz#59616b498304d556abd466966b22eeda3eca5fbe" +buffer-more-ints@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/buffer-more-ints/-/buffer-more-ints-0.0.2.tgz#26b3885d10fa13db7fc01aae3aab870199e0124c" + buffer-xor@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" @@ -1910,6 +1971,18 @@ buffer@^5.0.2: base64-js "^1.0.2" ieee754 "^1.1.4" +buildmail@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/buildmail/-/buildmail-4.0.1.tgz#877f7738b78729871c9a105e3b837d2be11a7a72" + dependencies: + addressparser "1.0.1" + libbase64 "0.1.0" + libmime "3.0.0" + libqp "1.1.0" + nodemailer-fetch "1.6.0" + nodemailer-shared "1.1.0" + punycode "1.4.1" + builtin-modules@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" @@ -2193,6 +2266,10 @@ circular-json@^0.3.1: version "0.3.3" resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66" +circular-json@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.5.1.tgz#b8942a09e535863dc21b04417a91971e1d9cd91f" + class-utils@^0.3.5: version "0.3.5" resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.5.tgz#17e793103750f9627b2176ea34cfd1b565903c80" @@ -2276,6 +2353,10 @@ co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" +co@~3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/co/-/co-3.0.6.tgz#1445f226c5eb956138e68c9ac30167ea7d2e6bda" + code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" @@ -2808,6 +2889,14 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" +data-uri-to-buffer@1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz#77163ea9c20d8641b4707e8f18abdf9a78f34835" + +date-format@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/date-format/-/date-format-1.2.0.tgz#615e828e233dd1ab9bb9ae0950e0ceccfa6ecad8" + date-now@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" @@ -2832,6 +2921,12 @@ debug-log@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debug-log/-/debug-log-1.0.1.tgz#2307632d4c04382b8df8a32f70b895046d52745f" +debug@2, debug@2.6.9, debug@^2.1.0, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.8, debug@~2.6.4, debug@~2.6.6: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + dependencies: + ms "2.0.0" + debug@2.2.0, debug@~2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" @@ -2844,13 +2939,7 @@ debug@2.3.3: dependencies: ms "0.7.2" -debug@2.6.9, debug@^2.1.0, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.8: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - dependencies: - ms "2.0.0" - -debug@3.1.0, debug@3.X, debug@^3.0.0, debug@^3.0.1, debug@^3.1.0: +debug@3.1.0, debug@3.X, debug@^3.0.0, debug@^3.0.1, debug@^3.1.0, debug@~3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" dependencies: @@ -2967,6 +3056,14 @@ defined@^1.0.0, defined@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" +degenerator@~1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-1.0.4.tgz#fcf490a37ece266464d9cc431ab98c5819ced095" + dependencies: + ast-types "0.x.x" + escodegen "1.x.x" + esprima "3.x.x" + del@^2.0.2: version "2.2.2" resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" @@ -3234,6 +3331,10 @@ dot-prop@^4.1.1: dependencies: is-obj "^1.0.0" +double-ended-queue@^2.1.0-0: + version "2.1.0-0" + resolved "https://registry.yarnpkg.com/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz#103d3527fd31528f40188130c841efdd78264e5c" + drbg.js@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/drbg.js/-/drbg.js-1.0.1.tgz#3e36b6c42b37043823cdbc332d58f31e2445480b" @@ -3353,21 +3454,20 @@ engine.io-client@1.8.0: xmlhttprequest-ssl "1.5.3" yeast "0.1.2" -engine.io-client@1.8.3: - version "1.8.3" - resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-1.8.3.tgz#1798ed93451246453d4c6f635d7a201fe940d5ab" +engine.io-client@~3.1.0: + version "3.1.5" + resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.1.5.tgz#85de17666560327ef1817978f6e3f8101ded2c47" dependencies: component-emitter "1.2.1" component-inherit "0.0.3" - debug "2.3.3" - engine.io-parser "1.3.2" + debug "~3.1.0" + engine.io-parser "~2.1.1" has-cors "1.1.0" indexof "0.0.1" - parsejson "0.0.3" parseqs "0.0.5" parseuri "0.0.5" - ws "1.1.2" - xmlhttprequest-ssl "1.5.3" + ws "~3.3.1" + xmlhttprequest-ssl "~1.5.4" yeast "0.1.2" engine.io-parser@1.3.1: @@ -3381,16 +3481,15 @@ engine.io-parser@1.3.1: has-binary "0.1.6" wtf-8 "1.0.0" -engine.io-parser@1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-1.3.2.tgz#937b079f0007d0893ec56d46cb220b8cb435220a" +engine.io-parser@~2.1.0, engine.io-parser@~2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-2.1.2.tgz#4c0f4cff79aaeecbbdcfdea66a823c6085409196" dependencies: after "0.8.2" - arraybuffer.slice "0.0.6" + arraybuffer.slice "~0.0.7" base64-arraybuffer "0.1.5" blob "0.0.4" - has-binary "0.1.7" - wtf-8 "1.0.0" + has-binary2 "~1.0.2" engine.io@1.8.0: version "1.8.0" @@ -3403,16 +3502,18 @@ engine.io@1.8.0: engine.io-parser "1.3.1" ws "1.1.1" -engine.io@1.8.3: - version "1.8.3" - resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-1.8.3.tgz#8de7f97895d20d39b85f88eeee777b2bd42b13d4" +engine.io@~3.1.0: + version "3.1.5" + resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-3.1.5.tgz#0e7ef9d690eb0b35597f1d4ad02a26ca2dba3845" dependencies: - accepts "1.3.3" + accepts "~1.3.4" base64id "1.0.0" cookie "0.3.1" - debug "2.3.3" - engine.io-parser "1.3.2" - ws "1.1.2" + debug "~3.1.0" + engine.io-parser "~2.1.0" + ws "~3.3.1" + optionalDependencies: + uws "~9.14.0" enhanced-resolve@^3.3.0: version "3.4.1" @@ -3577,6 +3678,17 @@ escape-string-regexp@1.0.5, escape-string-regexp@^1.0.0, escape-string-regexp@^1 version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" +escodegen@1.x.x: + version "1.9.1" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.9.1.tgz#dbae17ef96c8e4bedb1356f4504fa4cc2f7cb7e2" + dependencies: + esprima "^3.1.3" + estraverse "^4.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + escodegen@^1.9.0: version "1.9.0" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.9.0.tgz#9811a2f265dc1cd3894420ee3717064b632b8852" @@ -3699,7 +3811,7 @@ esprima-fb@13001.1001.0-dev-harmony-fb: version "13001.1001.0-dev-harmony-fb" resolved "https://registry.yarnpkg.com/esprima-fb/-/esprima-fb-13001.1001.0-dev-harmony-fb.tgz#633acdb40d9bd4db8a1c1d68c06a942959fad2b0" -esprima@^3.1.3, esprima@~3.1.0: +esprima@3.x.x, esprima@^3.1.3, esprima@~3.1.0: version "3.1.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" @@ -4378,14 +4490,14 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" +extend@3, extend@^3.0.0, extend@~3.0.0, extend@~3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" + extend@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/extend/-/extend-1.3.0.tgz#d1516fb0ff5624d2ebf9123ea1dac5a1994004f8" -extend@^3.0.0, extend@~3.0.0, extend@~3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" - extension-link-enabler@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/extension-link-enabler/-/extension-link-enabler-1.0.0.tgz#57b919aeeedf38be97270b9898cee78a637e46f3" @@ -4534,6 +4646,10 @@ file-tree@^1.0.0: commondir "0.0.1" flat "~1.0.0" +file-uri-to-path@1: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + filename-regex@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" @@ -4684,6 +4800,12 @@ flush-write-stream@^1.0.0, flush-write-stream@^1.0.2: inherits "^2.0.1" readable-stream "^2.0.4" +follow-redirects@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.0.0.tgz#8e34298cbd2e176f254effec75a1c78cc849fd37" + dependencies: + debug "^2.2.0" + for-each@^0.3.2, for-each@~0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.2.tgz#2c40450b9348e97f281322593ba96704b9abd4d4" @@ -4725,6 +4847,14 @@ fork-stream@^0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/fork-stream/-/fork-stream-0.0.4.tgz#db849fce77f6708a5f8f386ae533a0907b54ae70" +form-data@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.0.0.tgz#6f0aebadcc5da16c13e1ecc11137d85f9b883b25" + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.5" + mime-types "^2.1.11" + form-data@~2.1.1: version "2.1.4" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1" @@ -4843,6 +4973,13 @@ fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2: mkdirp ">=0.5 0" rimraf "2" +ftp@~0.3.10: + version "0.3.10" + resolved "https://registry.yarnpkg.com/ftp/-/ftp-0.3.10.tgz#9197d861ad8142f3e63d5a83bfe4c59f7330885d" + dependencies: + readable-stream "1.1.x" + xregexp "2.0.0" + function-bind@^1.0.2, function-bind@^1.1.0, function-bind@^1.1.1, function-bind@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -4920,6 +5057,17 @@ get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" +get-uri@2: + version "2.0.1" + resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-2.0.1.tgz#dbdcacacd8c608a38316869368117697a1631c59" + dependencies: + data-uri-to-buffer "1" + debug "2" + extend "3" + file-uri-to-path "1" + ftp "~0.3.10" + readable-stream "2" + get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" @@ -5423,6 +5571,12 @@ has-ansi@^2.0.0: dependencies: ansi-regex "^2.0.0" +has-binary2@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-binary2/-/has-binary2-1.0.2.tgz#e83dba49f0b9be4d026d27365350d9f03f54be98" + dependencies: + isarray "2.0.1" + has-binary@0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/has-binary/-/has-binary-0.1.6.tgz#25326f39cfa4f616ad8787894e3af2cfbc7b6e10" @@ -5547,6 +5701,13 @@ he@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" +hipchat-notifier@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/hipchat-notifier/-/hipchat-notifier-1.1.0.tgz#b6d249755437c191082367799d3ba9a0f23b231e" + dependencies: + lodash "^4.0.0" + request "^2.0.0" + hmac-drbg@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" @@ -5651,6 +5812,14 @@ http-parser-js@>=0.4.0: version "0.4.9" resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.4.9.tgz#ea1a04fb64adff0242e9974f297dd4c3cad271e1" +http-proxy-agent@1: + version "1.0.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-1.0.0.tgz#cc1ce38e453bf984a0f7702d2dd59c73d081284a" + dependencies: + agent-base "2" + debug "2" + extend "3" + http-proxy@^1.13.0, http-proxy@^1.13.1: version "1.16.2" resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.16.2.tgz#06dff292952bf64dbe8471fa9df73066d4f37742" @@ -5674,10 +5843,29 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" +httpntlm@1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/httpntlm/-/httpntlm-1.6.1.tgz#ad01527143a2e8773cfae6a96f58656bb52a34b2" + dependencies: + httpreq ">=0.4.22" + underscore "~1.7.0" + +httpreq@>=0.4.22: + version "0.4.24" + resolved "https://registry.yarnpkg.com/httpreq/-/httpreq-0.4.24.tgz#4335ffd82cd969668a39465c929ac61d6393627f" + https-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" +https-proxy-agent@1: + version "1.0.0" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz#35f7da6c48ce4ddbfa264891ac593ee5ff8671e6" + dependencies: + agent-base "2" + debug "2" + extend "3" + human-standard-token-abi@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/human-standard-token-abi/-/human-standard-token-abi-1.0.2.tgz#207d7846796ee5bb85fdd336e769cb38045b2ae0" @@ -5690,6 +5878,10 @@ iconv-lite@0.4.13: version "0.4.13" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2" +iconv-lite@0.4.15: + version "0.4.15" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb" + iconv-lite@0.4.19, iconv-lite@^0.4.17, iconv-lite@^0.4.5, iconv-lite@~0.4.13: version "0.4.19" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" @@ -5762,6 +5954,14 @@ indexof@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" +inflection@~1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/inflection/-/inflection-1.10.0.tgz#5bffcb1197ad3e81050f8e17e21668087ee9eb2f" + +inflection@~1.3.0: + version "1.3.8" + resolved "https://registry.yarnpkg.com/inflection/-/inflection-1.3.8.tgz#cbd160da9f75b14c3cc63578d4f396784bf3014e" + inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -5843,6 +6043,14 @@ invert-kv@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" +ip@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.0.1.tgz#c7e356cdea225ae71b36d70f2e71a92ba4e42590" + +ip@^1.1.2, ip@^1.1.4: + version "1.1.5" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" + ipaddr.js@1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.5.2.tgz#d4b505bde9946987ccf0fc58d9010ff9607e3fa0" @@ -6218,6 +6426,10 @@ isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" +isarray@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e" + isbinaryfile@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-3.0.2.tgz#4a3e974ec0cba9004d3fc6cde7209ea69368a621" @@ -6486,7 +6698,7 @@ json-stable-stringify@~0.0.0: dependencies: jsonify "~0.0.0" -json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: +json-stringify-safe@5.0.x, json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" @@ -6581,12 +6793,13 @@ karma-qunit@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/karma-qunit/-/karma-qunit-1.2.1.tgz#88252afd2127bc03b0cc31978ed6882b139f470a" -karma@^1.7.1: - version "1.7.1" - resolved "https://registry.yarnpkg.com/karma/-/karma-1.7.1.tgz#85cc08e9e0a22d7ce9cca37c4a1be824f6a2b1ae" +karma@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/karma/-/karma-2.0.0.tgz#a02698dd7f0f05ff5eb66ab8f65582490b512e58" dependencies: bluebird "^3.3.0" body-parser "^1.16.1" + browserify "^14.5.0" chokidar "^1.4.1" colors "^1.1.0" combine-lists "^1.0.0" @@ -6599,8 +6812,8 @@ karma@^1.7.1: graceful-fs "^4.1.2" http-proxy "^1.13.0" isbinaryfile "^3.0.0" - lodash "^3.8.0" - log4js "^0.6.31" + lodash "^4.17.4" + log4js "^2.3.9" mime "^1.3.4" minimatch "^3.0.2" optimist "^0.6.1" @@ -6608,9 +6821,9 @@ karma@^1.7.1: range-parser "^1.2.0" rimraf "^2.6.0" safe-buffer "^5.0.1" - socket.io "1.7.3" - source-map "^0.5.3" - tmp "0.0.31" + socket.io "2.0.4" + source-map "^0.6.1" + tmp "0.0.33" useragent "^2.1.12" keccak@^1.0.2: @@ -6782,6 +6995,22 @@ lexical-scope@^1.2.0: dependencies: astw "^2.0.0" +libbase64@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/libbase64/-/libbase64-0.1.0.tgz#62351a839563ac5ff5bd26f12f60e9830bb751e6" + +libmime@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/libmime/-/libmime-3.0.0.tgz#51a1a9e7448ecbd32cda54421675bb21bc093da6" + dependencies: + iconv-lite "0.4.15" + libbase64 "0.1.0" + libqp "1.1.0" + +libqp@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/libqp/-/libqp-1.1.0.tgz#f5e6e06ad74b794fb5b5b66988bf728ef1dedbe8" + liftoff@^2.3.0: version "2.5.0" resolved "https://registry.yarnpkg.com/liftoff/-/liftoff-2.5.0.tgz#2009291bb31cea861bbf10a7c15a28caf75c31ec" @@ -7032,10 +7261,6 @@ lodash.uniqby@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz#d99c07a669e9e6d24e1362dfe266c67616af1302" -lodash@^3.8.0: - version "3.10.1" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" - lodash@^4.0.0, lodash@^4.1.0, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.5.0, lodash@~4.17.2, lodash@~4.17.4: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" @@ -7056,12 +7281,32 @@ log-symbols@^2.0.0: dependencies: chalk "^2.0.1" -log4js@^0.6.31: - version "0.6.38" - resolved "https://registry.yarnpkg.com/log4js/-/log4js-0.6.38.tgz#2c494116695d6fb25480943d3fc872e662a522fd" +log4js@^2.3.9: + version "2.5.3" + resolved "https://registry.yarnpkg.com/log4js/-/log4js-2.5.3.tgz#38bb7bde5e9c1c181bd75e8bc128c5cd0409caf1" dependencies: - readable-stream "~1.0.2" - semver "~4.3.3" + circular-json "^0.5.1" + date-format "^1.2.0" + debug "^3.1.0" + semver "^5.3.0" + streamroller "^0.7.0" + optionalDependencies: + amqplib "^0.5.2" + axios "^0.15.3" + hipchat-notifier "^1.1.0" + loggly "^1.1.0" + mailgun-js "^0.7.0" + nodemailer "^2.5.0" + redis "^2.7.1" + slack-node "~0.2.0" + +loggly@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/loggly/-/loggly-1.1.1.tgz#0a0fc1d3fa3a5ec44fdc7b897beba2a4695cebee" + dependencies: + json-stringify-safe "5.0.x" + request "2.75.x" + timespan "2.3.x" loglevel@^1.4.1, loglevel@^1.5.0: version "1.6.0" @@ -7113,6 +7358,10 @@ lru-cache@^4.0.1: pseudomap "^1.0.2" yallist "^2.1.2" +lru-cache@~2.6.5: + version "2.6.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.6.5.tgz#e56d6354148ede8d7707b58d143220fd08df0fd5" + lru-queue@0.1: version "0.1.0" resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3" @@ -7123,6 +7372,27 @@ ltgt@~2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.0.tgz#b65ba5fcb349a29924c8e333f7c6a5562f2e4842" +mailcomposer@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/mailcomposer/-/mailcomposer-4.0.1.tgz#0e1c44b2a07cf740ee17dc149ba009f19cadfeb4" + dependencies: + buildmail "4.0.1" + libmime "3.0.0" + +mailgun-js@^0.7.0: + version "0.7.15" + resolved "https://registry.yarnpkg.com/mailgun-js/-/mailgun-js-0.7.15.tgz#ee366a20dac64c3c15c03d6c1b3e0ed795252abb" + dependencies: + async "~2.1.2" + debug "~2.2.0" + form-data "~2.1.1" + inflection "~1.10.0" + is-stream "^1.1.0" + path-proxy "~1.0.0" + proxy-agent "~2.0.0" + q "~1.4.0" + tsscmp "~1.0.0" + make-error-cause@^1.1.1: version "1.2.2" resolved "https://registry.yarnpkg.com/make-error-cause/-/make-error-cause-1.2.2.tgz#df0388fcd0b37816dff0a5fb8108939777dcbc9d" @@ -7415,6 +7685,16 @@ mime-db@~1.30.0: version "1.30.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01" +mime-db@~1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" + +mime-types@^2.1.11: + version "2.1.18" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" + dependencies: + mime-db "~1.33.0" + mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.15, mime-types@~2.1.16, mime-types@~2.1.17, mime-types@~2.1.7: version "2.1.17" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a" @@ -7693,6 +7973,10 @@ negotiator@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" +netmask@~1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/netmask/-/netmask-1.0.6.tgz#20297e89d86f6f6400f250d9f4f6b4c1945fcd35" + next-tick@1: version "1.0.0" resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" @@ -7823,6 +8107,59 @@ node-sass@^4.2.0, node-sass@^4.7.2: stdout-stream "^1.4.0" "true-case-path" "^1.0.2" +node-uuid@~1.4.7: + version "1.4.8" + resolved "https://registry.yarnpkg.com/node-uuid/-/node-uuid-1.4.8.tgz#b040eb0923968afabf8d32fb1f17f1167fdab907" + +nodemailer-direct-transport@3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/nodemailer-direct-transport/-/nodemailer-direct-transport-3.3.2.tgz#e96fafb90358560947e569017d97e60738a50a86" + dependencies: + nodemailer-shared "1.1.0" + smtp-connection "2.12.0" + +nodemailer-fetch@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/nodemailer-fetch/-/nodemailer-fetch-1.6.0.tgz#79c4908a1c0f5f375b73fe888da9828f6dc963a4" + +nodemailer-shared@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/nodemailer-shared/-/nodemailer-shared-1.1.0.tgz#cf5994e2fd268d00f5cf0fa767a08169edb07ec0" + dependencies: + nodemailer-fetch "1.6.0" + +nodemailer-smtp-pool@2.8.2: + version "2.8.2" + resolved "https://registry.yarnpkg.com/nodemailer-smtp-pool/-/nodemailer-smtp-pool-2.8.2.tgz#2eb94d6cf85780b1b4725ce853b9cbd5e8da8c72" + dependencies: + nodemailer-shared "1.1.0" + nodemailer-wellknown "0.1.10" + smtp-connection "2.12.0" + +nodemailer-smtp-transport@2.7.2: + version "2.7.2" + resolved "https://registry.yarnpkg.com/nodemailer-smtp-transport/-/nodemailer-smtp-transport-2.7.2.tgz#03d71c76314f14ac7dbc7bf033a6a6d16d67fb77" + dependencies: + nodemailer-shared "1.1.0" + nodemailer-wellknown "0.1.10" + smtp-connection "2.12.0" + +nodemailer-wellknown@0.1.10: + version "0.1.10" + resolved "https://registry.yarnpkg.com/nodemailer-wellknown/-/nodemailer-wellknown-0.1.10.tgz#586db8101db30cb4438eb546737a41aad0cf13d5" + +nodemailer@^2.5.0: + version "2.7.2" + resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-2.7.2.tgz#f242e649aeeae39b6c7ed740ef7b061c404d30f9" + dependencies: + libmime "3.0.0" + mailcomposer "4.0.1" + nodemailer-direct-transport "3.3.2" + nodemailer-shared "1.1.0" + nodemailer-smtp-pool "2.8.2" + nodemailer-smtp-transport "2.7.2" + socks "1.1.9" + nomnom@~1.6.2: version "1.6.2" resolved "https://registry.yarnpkg.com/nomnom/-/nomnom-1.6.2.tgz#84a66a260174408fc5b77a18f888eccc44fb6971" @@ -8189,7 +8526,7 @@ os-locale@^2.0.0: lcid "^1.0.0" mem "^1.1.0" -os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.1, os-tmpdir@~1.0.2: +os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" @@ -8224,6 +8561,30 @@ p-map@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b" +pac-proxy-agent@1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-1.1.0.tgz#34a385dfdf61d2f0ecace08858c745d3e791fd4d" + dependencies: + agent-base "2" + debug "2" + extend "3" + get-uri "2" + http-proxy-agent "1" + https-proxy-agent "1" + pac-resolver "~2.0.0" + raw-body "2" + socks-proxy-agent "2" + +pac-resolver@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-2.0.0.tgz#99b88d2f193fbdeefc1c9a529c1f3260ab5277cd" + dependencies: + co "~3.0.6" + degenerator "~1.0.2" + ip "1.0.1" + netmask "~1.0.4" + thunkify "~2.1.1" + pako@~1.0.5: version "1.0.6" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258" @@ -8380,6 +8741,12 @@ path-platform@~0.11.15: version "0.11.15" resolved "https://registry.yarnpkg.com/path-platform/-/path-platform-0.11.15.tgz#e864217f74c36850f0852b78dc7bf7d4a5721bf2" +path-proxy@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/path-proxy/-/path-proxy-1.0.0.tgz#18e8a36859fc9d2f1a53b48dee138543c020de5e" + dependencies: + inflection "~1.3.0" + path-root-regex@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/path-root-regex/-/path-root-regex-0.1.2.tgz#bfccdc8df5b12dc52c8b43ec38d18d72c04ba96d" @@ -8702,6 +9069,10 @@ process-nextick-args@^1.0.6, process-nextick-args@^1.0.7, process-nextick-args@~ version "1.0.7" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" +process-nextick-args@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" + process@^0.11.10, process@~0.11.0: version "0.11.10" resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" @@ -8773,6 +9144,19 @@ proxy-addr@~2.0.2: forwarded "~0.1.2" ipaddr.js "1.5.2" +proxy-agent@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-2.0.0.tgz#57eb5347aa805d74ec681cb25649dba39c933499" + dependencies: + agent-base "2" + debug "2" + extend "3" + http-proxy-agent "1" + https-proxy-agent "1" + lru-cache "~2.6.5" + pac-proxy-agent "1" + socks-proxy-agent "2" + prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" @@ -8810,7 +9194,7 @@ punycode@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" -punycode@^1.2.4, punycode@^1.3.2, punycode@^1.4.1: +punycode@1.4.1, punycode@^1.2.4, punycode@^1.3.2, punycode@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" @@ -8822,6 +9206,10 @@ q@^1.1.2: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" +q@~1.4.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e" + qjobs@^1.1.4: version "1.1.5" resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.1.5.tgz#659de9f2cf8dcc27a1481276f205377272382e73" @@ -8842,6 +9230,10 @@ qs@~2.2.3: version "2.2.5" resolved "https://registry.yarnpkg.com/qs/-/qs-2.2.5.tgz#1088abaf9dcc0ae5ae45b709e6c6b5888b23923c" +qs@~6.2.0: + version "6.2.3" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.3.tgz#1cfcb25c10a9b2b483053ff39f5dfc9233908cfe" + qs@~6.3.0: version "6.3.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.2.tgz#e75bd5f6e268122a2a0e0bda630b2550c166502c" @@ -8940,7 +9332,7 @@ raphael@^2.2.0: dependencies: eve-raphael "0.5.0" -raw-body@2.3.2: +raw-body@2, raw-body@2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.2.tgz#bcd60c77d3eb93cde0050295c3f379389bc88f89" dependencies: @@ -9172,18 +9564,30 @@ read@1.0.x: dependencies: mute-stream "~0.0.4" -"readable-stream@>=1.0.33-1 <1.1.0-0", readable-stream@~1.0.15, readable-stream@~1.0.17, readable-stream@~1.0.2, readable-stream@~1.0.27-1: - version "1.0.34" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" +readable-stream@1.1.x, "readable-stream@1.x >=1.1.9", "readable-stream@>=1.1.13-1 <1.2.0-0", readable-stream@^1.0.27-1, readable-stream@^1.0.33, readable-stream@^1.1.13-1, readable-stream@~1.1.9: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" dependencies: core-util-is "~1.0.0" inherits "~2.0.1" isarray "0.0.1" string_decoder "~0.10.x" -"readable-stream@>=1.1.13-1 <1.2.0-0", readable-stream@^1.0.27-1, readable-stream@^1.0.33, readable-stream@^1.1.13-1, readable-stream@~1.1.9: - version "1.1.14" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" +readable-stream@2, readable-stream@^2.3.0: + version "2.3.5" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.5.tgz#b4f85003a938cbb6ecbce2a124fb1012bd1a838d" + dependencies: + 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.0.3" + util-deprecate "~1.0.1" + +"readable-stream@>=1.0.33-1 <1.1.0-0", readable-stream@~1.0.15, readable-stream@~1.0.17, readable-stream@~1.0.27-1: + version "1.0.34" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" dependencies: core-util-is "~1.0.0" inherits "~2.0.1" @@ -9202,7 +9606,7 @@ readable-stream@^2, readable-stream@^2.0.0, readable-stream@^2.0.1, readable-str string_decoder "~1.0.3" util-deprecate "~1.0.1" -readable-stream@~2.0.0: +readable-stream@~2.0.0, readable-stream@~2.0.5: version "2.0.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" dependencies: @@ -9266,6 +9670,22 @@ redent@^2.0.0: indent-string "^3.0.0" strip-indent "^2.0.0" +redis-commands@^1.2.0: + version "1.3.5" + resolved "https://registry.yarnpkg.com/redis-commands/-/redis-commands-1.3.5.tgz#4495889414f1e886261180b1442e7295602d83a2" + +redis-parser@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/redis-parser/-/redis-parser-2.6.0.tgz#52ed09dacac108f1a631c07e9b69941e7a19504b" + +redis@^2.7.1: + version "2.8.0" + resolved "https://registry.yarnpkg.com/redis/-/redis-2.8.0.tgz#202288e3f58c49f6079d97af7a10e1303ae14b02" + dependencies: + double-ended-queue "^2.1.0-0" + redis-commands "^1.2.0" + redis-parser "^2.6.0" + redux-logger@^3.0.6: version "3.0.6" resolved "https://registry.yarnpkg.com/redux-logger/-/redux-logger-3.0.6.tgz#f7555966f3098f3c88604c449cf0baf5778274bf" @@ -9480,7 +9900,7 @@ request-promise@^4.2.1: stealthy-require "^1.1.0" tough-cookie ">=2.3.3" -request@2, request@^2.67.0, request@^2.79.0, request@^2.83.0: +request@2, request@^2.0.0, request@^2.67.0, request@^2.74.0, request@^2.79.0, request@^2.83.0: version "2.83.0" resolved "https://registry.yarnpkg.com/request/-/request-2.83.0.tgz#ca0b65da02ed62935887808e6f510381034e3356" dependencies: @@ -9507,6 +9927,32 @@ request@2, request@^2.67.0, request@^2.79.0, request@^2.83.0: tunnel-agent "^0.6.0" uuid "^3.1.0" +request@2.75.x: + version "2.75.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.75.0.tgz#d2b8268a286da13eaa5d01adf5d18cc90f657d93" + dependencies: + aws-sign2 "~0.6.0" + aws4 "^1.2.1" + bl "~1.1.2" + caseless "~0.11.0" + combined-stream "~1.0.5" + extend "~3.0.0" + forever-agent "~0.6.1" + form-data "~2.0.0" + har-validator "~2.0.6" + hawk "~3.1.3" + http-signature "~1.1.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.7" + node-uuid "~1.4.7" + oauth-sign "~0.8.1" + qs "~6.2.0" + stringstream "~0.0.4" + tough-cookie "~2.3.0" + tunnel-agent "~0.4.1" + request@2.81.0: version "2.81.0" resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" @@ -9559,6 +10005,15 @@ request@~2.79.0: tunnel-agent "~0.4.1" uuid "^3.0.0" +requestretry@^1.2.2: + version "1.13.0" + resolved "https://registry.yarnpkg.com/requestretry/-/requestretry-1.13.0.tgz#213ec1006eeb750e8b8ce54176283d15a8d55d94" + dependencies: + extend "^3.0.0" + lodash "^4.15.0" + request "^2.74.0" + when "^3.7.7" + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -9819,9 +10274,9 @@ semver-greatest-satisfied-range@^1.0.0: version "5.4.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" -semver@~4.3.3: - version "4.3.6" - resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da" +semver@~5.0.1: + version "5.0.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.0.3.tgz#77466de589cd5d3c95f138aa78bc569a3cb5d27a" semver@~5.3.0: version "5.3.0" @@ -9969,6 +10424,12 @@ sizzle@2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/sizzle/-/sizzle-2.3.3.tgz#4eb078c37231a56b52e4193f701e7ef8937e606b" +slack-node@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/slack-node/-/slack-node-0.2.0.tgz#de4b8dddaa8b793f61dbd2938104fdabf37dfa30" + dependencies: + requestretry "^1.2.2" + slash@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" @@ -9983,6 +10444,17 @@ slide@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" +smart-buffer@^1.0.13, smart-buffer@^1.0.4: + version "1.1.15" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-1.1.15.tgz#7f114b5b65fab3e2a35aa775bb12f0d1c649bf16" + +smtp-connection@2.12.0: + version "2.12.0" + resolved "https://registry.yarnpkg.com/smtp-connection/-/smtp-connection-2.12.0.tgz#d76ef9127cb23c2259edb1e8349c2e8d5e2d74c1" + dependencies: + httpntlm "1.6.1" + nodemailer-shared "1.1.0" + snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" @@ -10029,6 +10501,10 @@ socket.io-adapter@0.5.0: debug "2.3.3" socket.io-parser "2.3.1" +socket.io-adapter@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz#2a805e8a14d6372124dd9159ad4502f8cb07f06b" + socket.io-client@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-1.6.0.tgz#5b668f4f771304dfeed179064708386fa6717853" @@ -10045,20 +10521,22 @@ socket.io-client@1.6.0: socket.io-parser "2.3.1" to-array "0.1.4" -socket.io-client@1.7.3: - version "1.7.3" - resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-1.7.3.tgz#b30e86aa10d5ef3546601c09cde4765e381da377" +socket.io-client@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-2.0.4.tgz#0918a552406dc5e540b380dcd97afc4a64332f8e" dependencies: backo2 "1.0.2" + base64-arraybuffer "0.1.5" component-bind "1.0.0" component-emitter "1.2.1" - debug "2.3.3" - engine.io-client "1.8.3" - has-binary "0.1.7" + debug "~2.6.4" + engine.io-client "~3.1.0" + has-cors "1.1.0" indexof "0.0.1" object-component "0.0.3" + parseqs "0.0.5" parseuri "0.0.5" - socket.io-parser "2.3.1" + socket.io-parser "~3.1.1" to-array "0.1.4" socket.io-parser@2.3.1: @@ -10070,6 +10548,15 @@ socket.io-parser@2.3.1: isarray "0.0.1" json3 "3.3.2" +socket.io-parser@~3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.1.3.tgz#ed2da5ee79f10955036e3da413bfd7f1e4d86c8e" + dependencies: + component-emitter "1.2.1" + debug "~3.1.0" + has-binary2 "~1.0.2" + isarray "2.0.1" + socket.io@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-1.6.0.tgz#3e40d932637e6bd923981b25caf7c53e83b6e2e1" @@ -10082,17 +10569,37 @@ socket.io@1.6.0: socket.io-client "1.6.0" socket.io-parser "2.3.1" -socket.io@1.7.3: - version "1.7.3" - resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-1.7.3.tgz#b8af9caba00949e568e369f1327ea9be9ea2461b" +socket.io@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-2.0.4.tgz#c1a4590ceff87ecf13c72652f046f716b29e6014" dependencies: - debug "2.3.3" - engine.io "1.8.3" - has-binary "0.1.7" - object-assign "4.1.0" - socket.io-adapter "0.5.0" - socket.io-client "1.7.3" - socket.io-parser "2.3.1" + debug "~2.6.6" + engine.io "~3.1.0" + socket.io-adapter "~1.1.0" + socket.io-client "2.0.4" + socket.io-parser "~3.1.1" + +socks-proxy-agent@2: + version "2.1.1" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-2.1.1.tgz#86ebb07193258637870e13b7bd99f26c663df3d3" + dependencies: + agent-base "2" + extend "3" + socks "~1.1.5" + +socks@1.1.9: + version "1.1.9" + resolved "https://registry.yarnpkg.com/socks/-/socks-1.1.9.tgz#628d7e4d04912435445ac0b6e459376cb3e6d691" + dependencies: + ip "^1.1.2" + smart-buffer "^1.0.4" + +socks@~1.1.5: + version "1.1.10" + resolved "https://registry.yarnpkg.com/socks/-/socks-1.1.10.tgz#5b8b7fc7c8f341c53ed056e929b7bf4de8ba7b5a" + dependencies: + ip "^1.1.4" + smart-buffer "^1.0.13" solc@^0.4.2: version "0.4.19" @@ -10369,6 +10876,15 @@ stream-splicer@^2.0.0: inherits "^2.0.1" readable-stream "^2.0.2" +streamroller@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-0.7.0.tgz#a1d1b7cf83d39afb0d63049a5acbf93493bdf64b" + dependencies: + date-format "^1.2.0" + debug "^3.1.0" + mkdirp "^0.5.1" + readable-stream "^2.3.0" + string-length@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/string-length/-/string-length-1.0.1.tgz#56970fb1c38558e9e70b728bf3de269ac45adfac" @@ -10911,6 +11427,10 @@ through@~2.2.0: version "2.2.7" resolved "https://registry.yarnpkg.com/through/-/through-2.2.7.tgz#6e8e21200191d4eb6a99f6f010df46aa1c6eb2bd" +thunkify@~2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/thunkify/-/thunkify-2.1.2.tgz#faa0e9d230c51acc95ca13a361ac05ca7e04553d" + time-stamp@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.1.0.tgz#764a5a11af50561921b133f3b44e618687e0f5c3" @@ -10934,13 +11454,11 @@ timers-ext@^0.1.2: es5-ext "~0.10.14" next-tick "1" -tmp@0.0.31: - version "0.0.31" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7" - dependencies: - os-tmpdir "~1.0.1" +timespan@2.3.x: + version "2.3.0" + resolved "https://registry.yarnpkg.com/timespan/-/timespan-2.3.0.tgz#4902ce040bd13d845c8f59b27e9d59bad6f39929" -tmp@0.0.x, tmp@^0.0.33: +tmp@0.0.33, tmp@0.0.x, tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" dependencies: @@ -11061,6 +11579,10 @@ trumpet@^1.7.1: readable-stream "^1.0.27-1" through2 "^1.0.0" +tsscmp@~1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.5.tgz#7dc4a33af71581ab4337da91d85ca5427ebd9a97" + tty-browserify@0.0.0, tty-browserify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" @@ -11164,6 +11686,10 @@ ultron@1.0.x: version "1.0.2" resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa" +ultron@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" + umd@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/umd/-/umd-3.0.1.tgz#8ae556e11011f63c2596708a8837259f01b3d60e" @@ -11180,6 +11706,10 @@ underscore@~1.4.4: version "1.4.4" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.4.4.tgz#61a6a32010622afa07963bf325203cf12239d604" +underscore@~1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.7.0.tgz#6bbaf0877500d36be34ecaa584e0db9fef035209" + undertaker-registry@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/undertaker-registry/-/undertaker-registry-1.0.1.tgz#5e4bda308e4a8a2ae584f9b9a4359a499825cc50" @@ -11351,6 +11881,10 @@ uuid@^3.0.0, uuid@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" +uws@~9.14.0: + version "9.14.0" + resolved "https://registry.yarnpkg.com/uws/-/uws-9.14.0.tgz#fac8386befc33a7a3705cbd58dc47b430ca4dd95" + v8flags@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-3.0.1.tgz#dce8fc379c17d9f2c9e9ed78d89ce00052b1b76b" @@ -11693,6 +12227,10 @@ whatwg-url@^6.3.0: tr46 "^1.0.0" webidl-conversions "^4.0.1" +when@^3.7.7: + version "3.7.8" + resolved "https://registry.yarnpkg.com/when/-/when-3.7.8.tgz#c7130b6a7ea04693e842cdc9e7a1f2aa39a39f82" + which-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" @@ -11789,12 +12327,13 @@ ws@1.1.1: options ">=0.0.5" ultron "1.0.x" -ws@1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.2.tgz#8a244fa052401e08c9886cf44a85189e1fd4067f" +ws@~3.3.1: + version "3.3.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" dependencies: - options ">=0.0.5" - ultron "1.0.x" + async-limiter "~1.0.0" + safe-buffer "~5.1.0" + ultron "~1.1.0" wtf-8@1.0.0: version "1.0.0" @@ -11837,10 +12376,18 @@ xmlhttprequest-ssl@1.5.3: version "1.5.3" resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.3.tgz#185a888c04eca46c3e4070d99f7b49de3528992d" +xmlhttprequest-ssl@~1.5.4: + version "1.5.5" + resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e" + xmlhttprequest@*: version "1.8.0" resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" +xregexp@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943" + "xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0, xtend@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"