diff --git a/.babelrc b/.babelrc
index 9b1d5409bc17..cfa759dd9047 100644
--- a/.babelrc
+++ b/.babelrc
@@ -1,4 +1,23 @@
{
- "presets": [["env", { "targets": { "browsers": [">0.25%", "not ie 11", "not op_mini all"] } } ], "react", "stage-0"],
- "plugins": ["transform-runtime", "transform-async-to-generator", "transform-class-properties"]
-}
+ "presets": [
+ [
+ "env",
+ {
+ "targets": {
+ "browsers": [
+ ">0.25%",
+ "not ie 11",
+ "not op_mini all"
+ ]
+ }
+ }
+ ],
+ "react",
+ "stage-0"
+ ],
+ "plugins": [
+ "transform-runtime",
+ "transform-async-to-generator",
+ "transform-class-properties"
+ ]
+}
\ No newline at end of file
diff --git a/.circleci/config.yml b/.circleci/config.yml
index cf5afc3a86eb..807ed042c79b 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -82,28 +82,21 @@ workflows:
jobs:
prep-deps-npm:
docker:
- - image: circleci/node:8.11.3-browsers
+ - image: circleci/node:8.15.1-browsers
steps:
- checkout
- - restore_cache:
- keys:
- - v1.0-dependency-cache-{{ checksum "package-lock.json" }}
- run:
- name: Install npm 6 + deps via npm
+ name: Install deps via npm
command: |
- sudo npm install -g npm@6 && npm install --no-save --no-audit
+ npm ci
- persist_to_workspace:
root: .
paths:
- node_modules
- - save_cache:
- key: v1.0-dependency-cache-{{ checksum "package-lock.json" }}
- paths:
- - node_modules
prep-build:
docker:
- - image: circleci/node:8.11.3-browsers
+ - image: circleci/node:8.15.1-browsers
steps:
- checkout
- attach_workspace:
@@ -122,7 +115,7 @@ jobs:
prep-docs:
docker:
- - image: circleci/node:8.11.3-browsers
+ - image: circleci/node:8.15.1-browsers
steps:
- checkout
- attach_workspace:
@@ -137,7 +130,7 @@ jobs:
prep-scss:
docker:
- - image: circleci/node:8.11.3-browsers
+ - image: circleci/node:8.15.1-browsers
steps:
- checkout
- attach_workspace:
@@ -156,7 +149,7 @@ jobs:
test-lint:
docker:
- - image: circleci/node:8.11.3-browsers
+ - image: circleci/node:8.15.1-browsers
steps:
- checkout
- attach_workspace:
@@ -191,7 +184,7 @@ jobs:
# destination: test-artifacts
test-e2e-chrome:
docker:
- - image: circleci/node:8.11.3-browsers
+ - image: circleci/node:8.15.1-browsers
steps:
- checkout
- attach_workspace:
@@ -205,7 +198,7 @@ jobs:
test-e2e-firefox:
docker:
- - image: circleci/node:8.11.3-browsers
+ - image: circleci/node:8.15.1-browsers
steps:
- checkout
- run:
@@ -222,7 +215,7 @@ jobs:
job-screens:
docker:
- - image: circleci/node:8.11.3-browsers
+ - image: circleci/node:8.15.1-browsers
steps:
- checkout
- attach_workspace:
@@ -237,7 +230,7 @@ jobs:
job-publish-prerelease:
docker:
- - image: circleci/node:8.11.3-browsers
+ - image: circleci/node:8.15.1-browsers
steps:
- checkout
- attach_workspace:
@@ -257,7 +250,7 @@ jobs:
job-publish-release:
docker:
- - image: circleci/node:8.11.3-browsers
+ - image: circleci/node:8.15.1-browsers
steps:
- checkout
- attach_workspace:
@@ -274,7 +267,7 @@ jobs:
test-unit:
docker:
- - image: circleci/node:8.11.3-browsers
+ - image: circleci/node:8.15.1-browsers
steps:
- checkout
- attach_workspace:
@@ -284,7 +277,7 @@ jobs:
command: npm run test:coverage
test-mozilla-lint:
docker:
- - image: circleci/node:8.11.3-browsers
+ - image: circleci/node:8.15.1-browsers
steps:
- checkout
- attach_workspace:
@@ -295,7 +288,7 @@ jobs:
test-integration-flat-firefox:
docker:
- - image: circleci/node:8.11.3-browsers
+ - image: circleci/node:8.15.1-browsers
steps:
- checkout
- attach_workspace:
@@ -311,7 +304,7 @@ jobs:
environment:
browsers: '["Chrome"]'
docker:
- - image: circleci/node:8.11.3-browsers
+ - image: circleci/node:8.15.1-browsers
steps:
- checkout
- attach_workspace:
@@ -320,36 +313,9 @@ jobs:
name: test:integration:flat
command: npm run test:flat
- test-integration-mascara-firefox:
- docker:
- - image: circleci/node:8.11.3-browsers
- steps:
- - checkout
- - attach_workspace:
- at: .
- - run:
- name: Install Firefox
- command: ./.circleci/scripts/firefox-install
- - run:
- name: test:integration:mascara
- command: npm run test:mascara
-
- test-integration-mascara-chrome:
- environment:
- browsers: '["Chrome"]'
- docker:
- - image: circleci/node:8.11.3-browsers
- steps:
- - checkout
- - attach_workspace:
- at: .
- - run:
- name: test:integration:mascara
- command: npm run test:mascara
-
all-tests-pass:
docker:
- - image: circleci/node:8.11.3-browsers
+ - image: circleci/node:8.15.1-browsers
steps:
- run:
name: All Tests Passed
diff --git a/.eslintignore b/.eslintignore
index f96d39a168a0..5923e1bc214d 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -14,9 +14,6 @@ app/vendor/**
ui/lib/blockies.js
-mascara/src/app/first-time/spinner.js
-mascara/test/jquery-3.1.0.min.js
-
test/integration/bundle.js
test/integration/jquery-3.1.0.min.js
test/integration/helpers.js
diff --git a/.nvmrc b/.nvmrc
index 812d970dc98d..9235dd0adaa1 100644
--- a/.nvmrc
+++ b/.nvmrc
@@ -1 +1 @@
-v8.13
+v8.15
diff --git a/.stylelintignore b/.stylelintignore
index 854829a546a6..a42709a90e0b 100644
--- a/.stylelintignore
+++ b/.stylelintignore
@@ -4,7 +4,6 @@ dist/
docs/
fonts/
images/
-mascara/
node_modules/
notices/
test/
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bf897c864d75..bada9b8b5973 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,30 @@
## Current Develop Branch
+## 6.3.0 Mon Mar 25 2019
+
+- [#6300](https://github.com/MetaMask/metamask-extension/pull/6300): Gas chart hidden on custom networks
+- [#6301](https://github.com/MetaMask/metamask-extension/pull/6301): Fix gas fee in the submitted step of the transaction details activity log
+- [#6302](https://github.com/MetaMask/metamask-extension/pull/6302): Replaces the coinbase link in the deposit modal with one for wyre
+- [#6307](https://github.com/MetaMask/metamask-extension/pull/6307): Centre the notification in the current window
+- [#6312](https://github.com/MetaMask/metamask-extension/pull/6312): Fixes popups not showing when screen size is odd
+- [#6326](https://github.com/MetaMask/metamask-extension/pull/6326): Fix oversized loading overlay on gas customization modal.
+- [#6330](https://github.com/MetaMask/metamask-extension/pull/6330): Stop reloading dapps on network change allowing dapps to decide if it should refresh or not
+- [#6332](https://github.com/MetaMask/metamask-extension/pull/6332): Enable mobile sync
+- [#6333](https://github.com/MetaMask/metamask-extension/pull/6333): Redesign of the settings screen
+- [#6340](https://github.com/MetaMask/metamask-extension/pull/6340): Cancel transactions and signature requests on the closing of notification windows
+- [#6341](https://github.com/MetaMask/metamask-extension/pull/6341): Disable transaction "Cancel" button when balance is insufficient
+- [#6347](https://github.com/MetaMask/metamask-extension/pull/6347): Enable privacy mode by default for first time users
+
+## 6.2.2 Tue Mar 12 2019
+
+- [#6271](https://github.com/MetaMask/metamask-extension/pull/6271): Centre all notification popups
+- [#6268](https://github.com/MetaMask/metamask-extension/pull/6268): Improve Korean translations
+- [#6279](https://github.com/MetaMask/metamask-extension/pull/6279): Nonmultiple notifications for batch txs
+- [#6280](https://github.com/MetaMask/metamask-extension/pull/6280): No longer check network when validating checksum addresses
+
+## 6.2.1 Wed Mar 06 2019
+
## 6.2.0 Tue Mar 05 2019
- [#6192](https://github.com/MetaMask/metamask-extension/pull/6192): Improves design and UX of onboarding flow
- [#6195](https://github.com/MetaMask/metamask-extension/pull/6195): Fixes gas estimation when sending to contracts
@@ -12,7 +36,7 @@
- [#6182](https://github.com/MetaMask/metamask-extension/pull/6182): Change "Token Address" to "Token Contract Address"
- [#6177](https://github.com/MetaMask/metamask-extension/pull/6177): Fixes #6176
-- [#6146](https://github.com/MetaMask/metamask-extension/pull/6146): * Add Copy Tx ID button to transaction-list-item-details
+- [#6146](https://github.com/MetaMask/metamask-extension/pull/6146): * Add Copy Tx ID button to transaction-list-item-details
- [#6133](https://github.com/MetaMask/metamask-extension/pull/6133): Checksum address before slicing it for the confirm screen
- [#6147](https://github.com/MetaMask/metamask-extension/pull/6147): Add button to force edit token symbol when adding custom token
- [#6124](https://github.com/MetaMask/metamask-extension/pull/6124): recent-blocks - dont listen for block when on infura providers
diff --git a/Dockerfile b/Dockerfile
deleted file mode 100644
index f9ec6293589a..000000000000
--- a/Dockerfile
+++ /dev/null
@@ -1,25 +0,0 @@
-FROM node:7
-MAINTAINER kumavis
-
-# setup app dir
-RUN mkdir -p /www/
-WORKDIR /www/
-
-# install dependencies
-COPY ./package.json /www/package.json
-# RUN npm install -g node-gyp
-RUN npm install >> npm_log 2>> npm_err || true
-
-RUN cat npm_log && cat npm_err
-
-# copy over app dir
-COPY ./ /www/
-
-# run tests
-# RUN npm test
-
-# build app
-RUN npm run dist
-
-# start server
-CMD node mascara/example/server.js
diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index 3d2cd35bd096..eab243b8a5ed 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -11,6 +11,9 @@
"exposeDescription": {
"message": "Expose accounts to the current website. Useful for legacy dapps."
},
+ "chartOnlyAvailableEth": {
+ "message": "Chart only available on Ethereum networks."
+ },
"confirmExpose": {
"message": "Are you sure you want to expose your accounts to the current website?"
},
@@ -41,6 +44,9 @@
"providerRequestInfo": {
"message": "This site is requesting access to view your current account address. Always make sure you trust the sites you interact with."
},
+ "aboutUs": {
+ "message": "About Us"
+ },
"accept": {
"message": "Accept"
},
@@ -71,6 +77,12 @@
"address": {
"message": "Address"
},
+ "advanced": {
+ "message": "Advanced"
+ },
+ "advancedSettingsDescription": {
+ "message": "Access developer features, download State Logs, Reset Account, setup testnets and custom RPC."
+ },
"advancedOptions": {
"message": "Advanced Options"
},
@@ -89,9 +101,6 @@
"addAcquiredTokens": {
"message": "Add the tokens you've acquired using MetaMask"
},
- "advanced": {
- "message": "Advanced"
- },
"agreeTermsOfService": {
"message": "I agree to the Terms of Service"
},
@@ -188,6 +197,12 @@
"buyCoinbaseExplainer": {
"message": "Coinbase is the world’s most popular way to buy and sell Bitcoin, Ethereum, and Litecoin."
},
+ "buyWithWyre": {
+ "message": "Buy ETH with Wyre"
+ },
+ "buyWithWyreDescription": {
+ "message": "Wyre lets you use aa credit card to deposit ETH right in to your MetaMask account. From the Airswap website, click \"Use Fiat\" in the top-right corner. You can also use Airswap to get started with ERC 20 tokens!"
+ },
"bytes": {
"message": "Bytes"
},
@@ -227,6 +242,9 @@
"chromeRequiredForHardwareWallets": {
"message": "You need to use MetaMask on Google Chrome in order to connect to your Hardware Wallet."
},
+ "company": {
+ "message": "Company"
+ },
"confirm": {
"message": "Confirm"
},
@@ -293,6 +311,9 @@
"continueToCoinbase": {
"message": "Continue to Coinbase"
},
+ "continueToWyre": {
+ "message": "Continue to Wyre"
+ },
"contractDeployment": {
"message": "Contract Deployment"
},
@@ -607,6 +628,12 @@
"gasPriceRequired": {
"message": "Gas Price Required"
},
+ "general": {
+ "message": "General"
+ },
+ "generalSettingsDescription": {
+ "message": "Currency conversion, primary currency, language, blockies identicon"
+ },
"generatingTransaction": {
"message": "Generating transaction"
},
@@ -778,6 +805,9 @@
"ledgerAccountRestriction": {
"message": "You need to make use your last account before you can add a new one."
},
+ "legal": {
+ "message": "Legal"
+ },
"lessThanMax": {
"message": "must be less than or equal to $1.",
"description": "helper for inputting hex as decimal input"
@@ -959,6 +989,9 @@
"noTransactions": {
"message": "You have no transactions"
},
+ "notEnoughGas": {
+ "message": "Not Enough Gas"
+ },
"notFound": {
"message": "Not Found"
},
@@ -1228,6 +1261,12 @@
"secretPhrase": {
"message": "Enter your secret twelve word phrase here to restore your vault."
},
+ "securityAndPrivacy": {
+ "message": "Security & Privacy"
+ },
+ "securitySettingsDescription": {
+ "message": "Privacy settings and wallet seed phrase"
+ },
"secondsShorthand": {
"message": "Sec"
},
@@ -1313,10 +1352,10 @@
"message": "Select this to show gas price and limit controls directly on the send and confirm screens."
},
"showFiatConversionInTestnets": {
- "message": "Show Conversion in Testnets"
+ "message": "Show Conversion on Testnets"
},
"showFiatConversionInTestnetsDescription": {
- "message": "Select this to show fiat conversion in when you are on Testnets"
+ "message": "Select this to show fiat conversion on Testnets"
},
"showPrivateKeys": {
"message": "Show Private Keys"
diff --git a/app/_locales/es/messages.json b/app/_locales/es/messages.json
index 04e0c9f0514a..8033b962cb0b 100644
--- a/app/_locales/es/messages.json
+++ b/app/_locales/es/messages.json
@@ -686,7 +686,7 @@
"message": "Restaurar desde semilla"
},
"restoreVault": {
- "message": "Restaurar Bóveda"
+ "message": "Restaurar bóveda"
},
"retryWithMoreGas": {
"message": "Vuelva a intentar con un precio de gas más alto aquí"
@@ -1168,10 +1168,10 @@
"message": "Nuevo URL de RPC"
},
"showAdvancedOptions": {
- "message": "Mostrar Opciones Avanzadas"
+ "message": "Mostrar opciones avanzadas"
},
"hideAdvancedOptions": {
- "message": "Ocultar Opciones Avanzadas"
+ "message": "Ocultar opciones avanzadas"
},
"optionalChainId": {
"message": "ChainID (opcional)"
@@ -1183,13 +1183,13 @@
"message": "Apodo (opcional)"
},
"newTotal": {
- "message": "Nuevo Total"
+ "message": "Nuevo total"
},
"newTransactionFee": {
- "message": "Nueva Comisión por Transacción"
+ "message": "Nueva Comisión por transacción"
},
"noConversionRateAvailable": {
- "message": "No Hay Tasa de Conversión"
+ "message": "No hay tasa de conversión"
},
"notFound": {
"message": "No se encontró"
@@ -1238,7 +1238,7 @@
"message": "Prev"
},
"primaryCurrencySetting": {
- "message": "Moneda Principal"
+ "message": "Moneda principal"
},
"primaryCurrencySettingDescription": {
"message": "Seleccionar nativa para prioritizar el que se muestren los valores en la moneda nativa de la cadena (p.ej. ETH). Seleccionar Fíat para prioritzar el que se muestren los valores en la moneda fíat seleccionada."
@@ -1247,10 +1247,10 @@
"message": "Cola"
},
"rejectAll": {
- "message": "Rechazar a Todos"
+ "message": "Rechazar todas"
},
"rejectTxsN": {
- "message": "Rechazar a transacciones de $1"
+ "message": "Rechazar transacciones de $1"
},
"rejectTxsDescription": {
"message": "Está al punto de rechazar transacciones de $1 en lote."
@@ -1271,7 +1271,7 @@
"message": "Restaurar"
},
"revealSeedWordsTitle": {
- "message": "Frase Semilla"
+ "message": "Frase semilla"
},
"revealSeedWordsDescription": {
"message": "Si en algún momento cambias de navegador o de ordenador, necesitarás esta frase semilla para acceder a tus cuentas. Guárdatela en un lugar seguro y secreto."
@@ -1298,7 +1298,7 @@
"message": "Lento"
},
"slower": {
- "message": "Más Lento"
+ "message": "Más lento"
},
"saveAsCsvFile": {
"message": "Guardar como archivo CSV"
@@ -1316,10 +1316,10 @@
"message": "Seg"
},
"selectLocale": {
- "message": "Seleccionar Local"
+ "message": "Seleccionar local"
},
"sendAmount": {
- "message": "Mandar Cantidad"
+ "message": "Mandar cantidad"
},
"sentEther": {
"message": "se mandó ether"
@@ -1361,7 +1361,7 @@
"message": "Petición de Firma"
},
"somethingWentWrong": {
- "message": "¡Ups! Algo se estropeó."
+ "message": "¡Ups! Algo funcionó mal."
},
"speedUp": {
"message": "Agilizar"
@@ -1382,13 +1382,13 @@
"message": "Cambiar de Red"
},
"step1HardwareWallet": {
- "message": "1. Conectar Monedero Físico."
+ "message": "1. Conectar monedero físico."
},
"step1HardwareWalletMsg": {
"message": "Conéctate el monedero físico directamente al ordenador."
},
"step2HardwareWallet": {
- "message": "2. Seleccionar una Cuenta"
+ "message": "2. Seleccionar una cuenta"
},
"step2HardwareWalletMsg": {
"message": "Seleccione la cuenta que quieres ver. Sólo se puede eligir una a la vez."
@@ -1454,10 +1454,10 @@
"message": "Traspasar"
},
"transferFrom": {
- "message": "Traspasar De"
+ "message": "Traspasar de"
},
"trezorHardwareWallet": {
- "message": "Monedero Físico TREZOR"
+ "message": "Monedero físico TREZOR"
},
"tryAgain": {
"message": "Vuelve a intentar"
diff --git a/app/_locales/ko/messages.json b/app/_locales/ko/messages.json
index 4c6541d216ef..b824cd72fa3d 100644
--- a/app/_locales/ko/messages.json
+++ b/app/_locales/ko/messages.json
@@ -30,13 +30,16 @@
"message": "승인 데이터 삭제"
},
"reject": {
- "message": "받지 않다"
+ "message": "거부"
},
"providerAPIRequest": {
"message": "Web3 API 요청"
},
"reviewProviderRequest": {
- "message": "이 Ethereum API 요청을 검토하십시오."
+ "message": "이 Ethereum API 요청을 검토하세요."
+ },
+ "providerRequest": {
+ "message": "$1이 당신의 계정에 연결하길 원합니다."
},
"providerRequestInfo": {
"message": "아래 나열된 도메인은 Web3 API에 대한 액세스를 요청하여 Ethereum 블록 체인과 상호 작용할 수 있습니다. Ethereum 액세스를 승인하기 전에 항상 올바른 사이트에 있는지 다시 확인하십시오."
@@ -44,6 +47,9 @@
"accept": {
"message": "수락"
},
+ "acceleratingATransaction": {
+ "message": "* 더 높은 가스 요금을 사용하여 트랜잭션을 가속화하면 네트워크에 의해 더 빨리 처리될 가능성이 증가하지만 항상 빠른 처리가 보장되는 것은 아닙니다."
+ },
"accessingYourCamera": {
"message": "카메라에 접근 중..."
},
@@ -68,6 +74,9 @@
"address": {
"message": "주소"
},
+ "advancedOptions": {
+ "message": "고급 옵션"
+ },
"addCustomToken": {
"message": "사용자 정의 토큰 추가"
},
@@ -83,12 +92,21 @@
"addAcquiredTokens": {
"message": "메타마스크를 통해 획득한 토큰 추가"
},
+ "advanced": {
+ "message": "고급"
+ },
+ "allDone": {
+ "message": "모두 완료"
+ },
"amount": {
"message": "수량"
},
"amountPlusGas": {
"message": "수량 + 가스"
},
+ "amountPlusTxFee": {
+ "message": "Amount + TX Fee"
+ },
"appDescription": {
"message": "이더리움 브라우저 확장 프로그램",
"description": "애플리케이션 설명"
@@ -109,12 +127,18 @@
"attemptToCancel": {
"message": "취소 하시겠습니까?"
},
+ "attemptToCancelDescription": {
+ "message": "이 취소 시도가 기존 트랜잭션의 취소를 보장하지 않습니다. 취소 시도가 성공하면 위 거래 수수료가 부과됩니다."
+ },
"attributions": {
"message": "속성"
},
"available": {
"message": "사용 가능"
},
+ "average": {
+ "message": "평균"
+ },
"back": {
"message": "돌아가기"
},
@@ -127,6 +151,9 @@
"balanceIsInsufficientGas": {
"message": "현재 가스 총합에 대해 잔액이 부족합니다"
},
+ "basic": {
+ "message": "Basic"
+ },
"beta": {
"message": "BETA"
},
@@ -170,6 +197,9 @@
"cancellationGasFee": {
"message": "취소 가스 수수료"
},
+ "cancelled": {
+ "message": "취소됨"
+ },
"cancelN": {
"message": "모든 $1 트랜잭션 취소"
},
@@ -179,6 +209,12 @@
"clickCopy": {
"message": "클릭하여 복사"
},
+ "clickToAdd": {
+ "message": "계정에 토큰을 추가하기 위해 $1를 클릭하세요."
+ },
+ "clickToRevealSeed": {
+ "message": "비밀 단어를 보기 위해 여기를 클릭하세요."
+ },
"close": {
"message": "닫기"
},
@@ -188,6 +224,9 @@
"confirm": {
"message": "승인"
},
+ "confirmationTime": {
+ "message": "처리 시간 (초)"
+ },
"confirmed": {
"message": "승인됨"
},
@@ -197,27 +236,39 @@
"confirmPassword": {
"message": "비밀번호 확인"
},
+ "confirmSecretBackupPhrase": {
+ "message": "비밀 백업 구문 확인"
+ },
"confirmTransaction": {
"message": "트랜잭션 승인"
},
+ "congratulations": {
+ "message": "축하합니다."
+ },
"connectHardwareWallet": {
"message": "하드웨어 지갑 연결"
},
"connect": {
"message": "연결"
},
+ "connectRequest": {
+ "message": "연결 요청"
+ },
"connecting": {
"message": "연결 중..."
},
+ "connectingTo": {
+ "message": "$1에 연결"
+ },
+ "connectingToKovan": {
+ "message": "Kovan 테스트넷 접속 중"
+ },
"connectingToMainnet": {
"message": "이더리움 메인넷 접속 중"
},
"connectingToRopsten": {
"message": "Ropsten 테스트넷 접속 중"
},
- "connectingToKovan": {
- "message": "Kovan 테스트넷 접속 중"
- },
"connectingToRinkeby": {
"message": "Rinkeby 테스트넷 접속 중"
},
@@ -263,6 +314,12 @@
"copyToClipboard": {
"message": "클립보드로 복사"
},
+ "copyTransactionId": {
+ "message": "트랜잭션 아이디 복사"
+ },
+ "copiedTransactionId": {
+ "message": "트랜잭션 아이디 복사됨"
+ },
"copyButton": {
"message": " 복사 "
},
@@ -275,9 +332,15 @@
"createAccount": {
"message": "계정 생성"
},
+ "createAWallet": {
+ "message": "지갑 생성하기"
+ },
"createDen": {
"message": "생성"
},
+ "createPassword": {
+ "message": "비밀번호 생성"
+ },
"crypto": {
"message": "암호화폐",
"description": "거래 유형 (암호화폐)"
@@ -285,6 +348,9 @@
"currentConversion": {
"message": "현재 통화"
},
+ "currencyConversion": {
+ "message": "통화 변환"
+ },
"currentLanguage": {
"message": "현재 언어"
},
@@ -297,6 +363,9 @@
"customGas": {
"message": "가스 설정"
},
+ "customGasSubTitle": {
+ "message": "수수료를 높히면 처리 시간이 단축될 수 있지만, 그것이 보장되진 않습니다."
+ },
"customToken": {
"message": "사용자 정의 토큰"
},
@@ -310,7 +379,7 @@
"message": "소수점은 0 이상이고 36 이하여야 합니다."
},
"decimal": {
- "message": "소수점 정확도"
+ "message": "소수 자릿수"
},
"defaultNetwork": {
"message": "이더리움 트랜잭션의 기본 네트워크는 메인넷입니다."
@@ -364,6 +433,9 @@
"downloadGoogleChrome": {
"message": "구글 크롬 다운로드"
},
+ "downloadSecretBackup": {
+ "message": "이 비밀 백업 구문을 다운로드하여 암호화된 외장 하드 드라이브나 저장 매체에 안전하게 보관하세요."
+ },
"downloadStateLogs": {
"message": "상태 로그 다운로드"
},
@@ -388,6 +460,30 @@
"encryptNewDen": {
"message": "새로운 DEN을 암호화"
},
+ "endOfFlowMessage1": {
+ "message": "인증을 통과했습니다 - 시드 구문을 안전하게 보관하세요. 그것은 당신의 의무입니다."
+ },
+ "endOfFlowMessage2": {
+ "message": "안전하게 시드 구문을 보관하는 팁"
+ },
+ "endOfFlowMessage3": {
+ "message": "여러 군데에 시드 구문을 백업하세요."
+ },
+ "endOfFlowMessage4": {
+ "message": "누구와도 시드 구문을 공유하지마세요."
+ },
+ "endOfFlowMessage5": {
+ "message": "피싱에 주의하세요! 메타마스크는 절대 갑작스럽게 당신의 시드 구문을 묻지 않습니다."
+ },
+ "endOfFlowMessage6": {
+ "message": "만약 시드 구문을 다시 백업해야한다면, 설정 -> 보안에서 확인할 수 있습니다. "
+ },
+ "endOfFlowMessage7": {
+ "message": "만약 질문이 있거나 피싱을 목격했다면 support@metamask.io으로 메일을 보내주세요."
+ },
+ "endOfFlowMessage8": {
+ "message": "메타마스크는 당신의 시드 구문을 복원해줄 수 없습니다. 더 알아보기."
+ },
"ensNameNotFound": {
"message": "ENS 이름을 찾을 수 없습니다"
},
@@ -400,9 +496,15 @@
"enterPasswordContinue": {
"message": "계속하기 위해 비밀번호 입력"
},
+ "eth": {
+ "message": "ETH"
+ },
"etherscanView": {
"message": "이더스캔에서 계정보기"
},
+ "estimatedProcessingTimes": {
+ "message": "예상 처리 시간"
+ },
"exchangeRate": {
"message": "환율"
},
@@ -418,6 +520,12 @@
"failed": {
"message": "실패"
},
+ "fast": {
+ "message": "빠름"
+ },
+ "faster": {
+ "message": "빨라짐"
+ },
"fiat": {
"message": "FIAT",
"description": "거래 형식"
@@ -457,18 +565,33 @@
"gasLimitCalculation": {
"message": "네트워크 성공률을 기반으로 적합한 가스 한도를 계산합니다."
},
+ "gasLimitInfoModalContent": {
+ "message": "가스 한도는 당신이 기꺼이 소비할 수 있는 가스의 최대 수량입니다."
+ },
"gasLimitRequired": {
"message": "가스 한도가 필요합니다."
},
"gasLimitTooLow": {
"message": "가스 한도는 최소 21000 이상이어야 합니다."
},
+ "gasUsed": {
+ "message": "사용된 가스"
+ },
"generatingSeed": {
"message": "시드 생성 중..."
},
"gasPrice": {
"message": "가스 가격 (GWEI)"
},
+ "gasPriceExtremelyLow": {
+ "message": "가스 가격 매우 낮음"
+ },
+ "gasPriceInfoModalContent": {
+ "message": "가스 가격은 가스 1당 지불할 gwei 단위의 이더 수량을 명시합니다."
+ },
+ "gasPriceNoDenom": {
+ "message": "가스 가격"
+ },
"gasPriceCalculation": {
"message": "네트워크 성공률을 기반으로 적합한 가스 가격을 계산합니다."
},
@@ -488,10 +611,16 @@
"getHelp": {
"message": "도움말"
},
+ "getStarted": {
+ "message": "시작하기"
+ },
"greaterThanMin": {
"message": "$1 이상이어야 합니다.",
"description": "10진수 입력으로 hex값 입력을 도와줍니다"
},
+ "happyToSeeYou": {
+ "message": "We’re happy to see you."
+ },
"hardware": {
"message": "하드웨어"
},
@@ -545,12 +674,21 @@
"importAccountMsg": {
"message": " 가져온 계정은 메타마스크에서 원래 생성된 계정의 시드구문과 연관성이 없습니다. 가져온 계정에 대해 더 배우기 "
},
+ "importAccountSeedPhrase": {
+ "message": "시드 구문으로 계정 가져오기"
+ },
"importAnAccount": {
"message": "계정 가져오기"
},
"importDen": {
"message": "기존의 DEN 가져오기"
},
+ "importWallet": {
+ "message": "지갑 가져오기"
+ },
+ "importYourExisting": {
+ "message": "12개 단어로 구성된 시드 구문으로 이미 만들어진 지갑을 가져오기"
+ },
"imported": {
"message": "가져온 계정",
"description": "이 상태는 해당 계정이 keyring으로 완전히 로드된 상태임을 표시합니다"
@@ -558,6 +696,9 @@
"importUsingSeed": {
"message": "계정 시드 구문으로 가져오기"
},
+ "importWithSeedPhrase": {
+ "message": "시드 구문 가져오기"
+ },
"info": {
"message": "정보"
},
@@ -567,6 +708,9 @@
"initialTransactionConfirmed": {
"message": "초기 트랜잭션이 네트워크를 통해 확정되었습니다. 확인을 누르면 이전으로 돌아갑니다."
},
+ "insufficientBalance": {
+ "message": "잔액 부족."
+ },
"insufficientFunds": {
"message": "충분하지 않은 자금."
},
@@ -623,6 +767,9 @@
"message": "$1 이하여야합니다.",
"description": "10진수 입력으로 hex값 입력을 도와줍니다"
},
+ "letsGoSetUp": {
+ "message": "네, 설정해볼게요!"
+ },
"likeToAddTokens": {
"message": "토큰을 추가하시겠습니까?"
},
@@ -632,6 +779,9 @@
"limit": {
"message": "한도"
},
+ "liveGasPricePredictions": {
+ "message": "실시간 가스 가격 예측"
+ },
"loading": {
"message": "로딩 중..."
},
@@ -656,6 +806,9 @@
"mainnet": {
"message": "이더리움 메인넷"
},
+ "memorizePhrase": {
+ "message": "이 구문을 기억하세요."
+ },
"menu": {
"message": "메뉴"
},
@@ -663,17 +816,20 @@
"message": "메시지"
},
"metamaskDescription": {
- "message": "메타마스크는 이더리움을 위한 안전한 신분 저장소입니다."
- },
- "metamaskVersion": {
- "message": "메타마스크 버전"
+ "message": "메타마스크는 이더리움을 위한 안전한 저장소입니다."
},
"metamaskSeedWords": {
"message": "메타마스크 시드 단어"
},
+ "metamaskVersion": {
+ "message": "메타마스크 버전"
+ },
"min": {
"message": "최소"
},
+ "missingYourTokens": {
+ "message": "당신의 토큰이 보이지 않나요?"
+ },
"myAccounts": {
"message": "내 계정"
},
@@ -713,11 +869,44 @@
"newPassword": {
"message": "새 비밀번호 (최소 8자 이상)"
},
+ "newPassword8Chars": {
+ "message": "새 비밀번호 (최소 8문자)"
+ },
"newRecipient": {
"message": "받는 사람"
},
- "newRPC": {
- "message": "새로운 RPC URL"
+ "newNetwork": {
+ "message": "새 네트워크"
+ },
+ "newToMetaMask": {
+ "message": "메타마스크를 처음 사용하시나요?"
+ },
+ "noAlreadyHaveSeed": {
+ "message": "아니요, 이미 시드 구문을 가지고 있습니다."
+ },
+ "rpcURL": {
+ "message": "새 RPC 주소"
+ },
+ "showAdvancedOptions": {
+ "message": "고급 옵션 보기"
+ },
+ "hideAdvancedOptions": {
+ "message": "고급 옵션 숨기기"
+ },
+ "optionalChainId": {
+ "message": "ChainID (선택)"
+ },
+ "optionalSymbol": {
+ "message": "Symbol (선택)"
+ },
+ "optionalNickname": {
+ "message": "Nickname (선택)"
+ },
+ "newTotal": {
+ "message": "새 합계"
+ },
+ "newTransactionFee": {
+ "message": "새 트랜잭션 수수료"
},
"next": {
"message": "다음"
@@ -743,12 +932,12 @@
"notStarted": {
"message": "시작 안 됨"
},
- "noWebcamFound": {
- "message": "컴퓨터의 웹캠을 찾을 수 없습니다. 다시 시도해보세요."
- },
"noWebcamFoundTitle": {
"message": "웹캠이 없습니다"
},
+ "noWebcamFound": {
+ "message": "컴퓨터의 웹캠을 찾을 수 없습니다. 다시 시도해보세요."
+ },
"oldUI": {
"message": "구버전 UI"
},
@@ -778,6 +967,12 @@
"parameters": {
"message": "매개변수"
},
+ "participateInMetaMetrics": {
+ "message": "MetaMetrics 참여"
+ },
+ "participateInMetaMetricsDescription": {
+ "message": "메타마스크를 더 좋게 만들기 위해 MetaMetrics에 참여하세요."
+ },
"password": {
"message": "비밀번호"
},
@@ -820,6 +1015,12 @@
"prev": {
"message": "이전"
},
+ "primaryCurrencySetting": {
+ "message": "주 화폐"
+ },
+ "primaryCurrencySettingDescription": {
+ "message": "체인의 고유 통화 값으로 우선 표기하시려면 네이티브를 선택하세요. (예: ETH) 설정하신 Fiat 통화 값으로 우선 표기하시려면 Fiat을 선택하세요."
+ },
"privacyMsg": {
"message": "개인정보 보호 정책"
},
@@ -887,6 +1088,9 @@
"restoreAccountWithSeed": {
"message": "시드 구문으로 계정 복구하기"
},
+ "requestsAwaitingAcknowledgement": {
+ "message": "요청이 인정되기까지 대기중"
+ },
"required": {
"message": "필요함"
},
@@ -942,11 +1146,11 @@
"save": {
"message": "저장"
},
- "speedUpTitle": {
- "message": "트랜잭션 속도 향상하기"
+ "slow": {
+ "message": "느림"
},
- "speedUpSubtitle": {
- "message": "트랜잭션 가스 가격을 올려서 해당 트랜잭션에 덮어쓰고 속도를 빠르게 합니다"
+ "slower": {
+ "message": "느려짐"
},
"saveAsCsvFile": {
"message": "CSV 파일로 저장"
@@ -970,11 +1174,23 @@
"searchResults": {
"message": "검색 결과"
},
+ "secretBackupPhrase": {
+ "message": "비밀 백업 구문"
+ },
+ "secretBackupPhraseDescription": {
+ "message": "비밀 백업 구문을 사용하면 계정을 쉽게 백업하고 복원할 수 있습니다."
+ },
+ "secretBackupPhraseWarning": {
+ "message": "경고: 백업 구문을 절대 공개하지 마세요. 이 구문을 가진 누군가 당신의 이더를 영원히 가지고 갈 수 있습니다."
+ },
"secretPhrase": {
"message": "12개 단어로 구성된 비밀 구문을 입력하여 저장소를 복구하세요."
},
- "newPassword8Chars": {
- "message": "새 비밀번호 (최소 8문자)"
+ "secondsShorthand": {
+ "message": "초"
+ },
+ "seedPhrasePlaceholder": {
+ "message": "각 단어를 스페이스로 구분해주세요"
},
"seedPhraseReq": {
"message": "시드 구문은 12개의 단어입니다"
@@ -985,6 +1201,9 @@
"selectCurrency": {
"message": "통화 선택"
},
+ "selectEachPhrase": {
+ "message": "백업 구문이 올바른지 확인하기 위해 각 단어를 순서에 맞게 선택해주세요."
+ },
"selectLocale": {
"message": "언어 선택"
},
@@ -997,6 +1216,9 @@
"send": {
"message": "전송"
},
+ "sendAmount": {
+ "message": "전송 수량"
+ },
"sendETH": {
"message": "ETH 보내기"
},
@@ -1024,6 +1246,9 @@
"selectAnAccountHelp": {
"message": "메타마스크에서 보기 위한 계정 선택"
},
+ "selectAHigherGasFee": {
+ "message": "트랜잭션 처리를 가속하기 위해 더 높은 가스 요금을 선택하세요.*"
+ },
"selectHdPath": {
"message": "HD 경로 지정"
},
@@ -1039,6 +1264,18 @@
"shapeshiftBuy": {
"message": "Shapeshift를 통해서 구매하기"
},
+ "showAdvancedGasInline": {
+ "message": "고급 가스 제어"
+ },
+ "showAdvancedGasInlineDescription": {
+ "message": "전송 및 확인 화면에서 직접 가스 가격과 한도 제어를 표시하려면 이 옵션을 선택해주세요."
+ },
+ "showFiatConversionInTestnets": {
+ "message": "테스트 넷에서 fiat 변환 보여주기"
+ },
+ "showFiatConversionInTestnetsDescription": {
+ "message": "테스트 넷에서 fiat 변환을 보기 위해 활성화하세요"
+ },
"showPrivateKeys": {
"message": "개인키 보기"
},
@@ -1054,12 +1291,12 @@
"sign": {
"message": "서명"
},
- "signed": {
- "message": "서명됨"
- },
"signatureRequest": {
"message": "서명 요청"
},
+ "signed": {
+ "message": "서명됨"
+ },
"signMessage": {
"message": "메시지 서명"
},
@@ -1072,9 +1309,30 @@
"sigRequested": {
"message": "서명이 요청됨"
},
+ "somethingWentWrong": {
+ "message": "헉! 뭔가 잘못됐어요."
+ },
"spaceBetween": {
"message": "단어 사이에는 공백만 올 수 있습니다"
},
+ "speedUp": {
+ "message": "속도 향상"
+ },
+ "speedUpTitle": {
+ "message": "트랜잭션 속도 향상하기"
+ },
+ "speedUpSubtitle": {
+ "message": "트랜잭션 가스 가격을 올려서 해당 트랜잭션에 덮어쓰고 속도를 빠르게 합니다"
+ },
+ "speedUpCancellation": {
+ "message": "취소 속도 향상"
+ },
+ "speedUpTransaction": {
+ "message": "트랜잭션 속도 향상"
+ },
+ "switchNetworks": {
+ "message": "네트워크 변경"
+ },
"status": {
"message": "상태"
},
@@ -1105,6 +1363,9 @@
"step3HardwareWalletMsg": {
"message": "다른 이더리움 계정을 사용하듯 하드웨어 계정을 사용합니다. dApps을 로그인하거나, 이더를 보내거나, ERC20 토큰 혹은 대체 가능하지 않은 토큰 (예를 들어 CryptoKitties)을 사거나 저장하거나 합니다."
},
+ "storePhrase": {
+ "message": "이 구문을 1Password같은 암호 관리자에 저장하세요."
+ },
"submit": {
"message": "제출"
},
@@ -1126,8 +1387,14 @@
"testFaucet": {
"message": "파우셋 테스트"
},
+ "thisWillCreate": {
+ "message": "새로운 지갑과 시드 구문을 생성"
+ },
+ "tips": {
+ "message": "팁"
+ },
"to": {
- "message": "받는이: "
+ "message": "받는이"
},
"toETHviaShapeShift": {
"message": "ShapeShift를 통해 $1를 ETH로 바꾸기",
@@ -1145,6 +1412,9 @@
"tokenBalance": {
"message": "현재 토큰 잔액:"
},
+ "tokenContractAddress": {
+ "message": "토큰 컨트랙트 주소"
+ },
"tokenSelection": {
"message": "토큰을 검색하거나 유명한 토큰 리스트에서 선택하시기 바랍니다."
},
@@ -1160,21 +1430,63 @@
"transaction": {
"message": "트랜잭션"
},
+ "transactionConfirmed": {
+ "message": "트랜잭션이 승인됨 $2."
+ },
+ "transactionCreated": {
+ "message": "$1에 대한 트랜잭션이 생성됨 $2."
+ },
+ "transactionWithNonce": {
+ "message": "트랜잭션 $1"
+ },
+ "transactionDropped": {
+ "message": "트랜잭션이 드롭됨 $2."
+ },
+ "transactionSubmitted": {
+ "message": "$1의 가스 요금으로 트랜잭션이 제출됨 $2."
+ },
+ "transactionResubmitted": {
+ "message": "$1으로 가스 요금을 올려 트랜잭션이 다시 제출됨 $2."
+ },
+ "transactionUpdated": {
+ "message": "트랜잭션이 수정됨 $2."
+ },
+ "transactionUpdatedGas": {
+ "message": "$1의 가스 요금으로 트랜잭션이 수정됨 $2."
+ },
+ "transactionErrored": {
+ "message": "트랜잭션 오류 발생."
+ },
+ "transactionCancelAttempted": {
+ "message": "$1의 가스 요금으로 트랜잭션 취소가 시도됨 $2."
+ },
+ "transactionCancelSuccess": {
+ "message": "트랜잭션이 성공적으로 취소됨 $2."
+ },
"transactions": {
"message": "트랜잭션"
},
"transactionError": {
"message": "트랜잭션 오류. 컨트랙트 코드에서 예외 발생(Exception thrown)."
},
+ "transactionFee": {
+ "message": "수수료"
+ },
"transactionMemo": {
"message": "트랜잭션 메모 (선택사항)"
},
"transactionNumber": {
"message": "트랜잭션 번호"
},
+ "transactionTime": {
+ "message": "트랜잭션 시간"
+ },
"transfer": {
"message": "전송"
},
+ "transferFrom": {
+ "message": "보내는 이"
+ },
"transfers": {
"message": "전송"
},
@@ -1194,6 +1506,9 @@
"typePassword": {
"message": "비밀번호를 입력하세요"
},
+ "uiMigrationAnnouncement": {
+ "message": "새로운 메타마스크 UI에 오신 것을 환영합니다. UI에 대한 피드백 또는 기능 요청이 있는 경우, 당사의 지원팀 또는 GitHub에 연락해주세요."
+ },
"uiWelcome": {
"message": "새로운 UI에 오신 것을 환영합니다. (Beta)"
},
@@ -1234,7 +1549,10 @@
"message": "잠금 해제"
},
"unlockMessage": {
- "message": "우리가 기다리던 분권형 웹입니다"
+ "message": "우리가 기다리던 탈 중앙화 웹입니다"
+ },
+ "updatedWithDate": {
+ "message": "$1에 업데이트 됨"
},
"uriErrorMsg": {
"message": "URI는 HTTP/HTTPS로 시작해야 합니다."
@@ -1279,9 +1597,15 @@
"whatsThis": {
"message": "이것은 무엇인가요?"
},
+ "writePhrase": {
+ "message": "이 구문을 종이에 써서 안전한 장소에 보관하세요. 만약 당신이 더 높은 수준의 보안을 원한다면, 그것을 여러 장의 종이에 적어서 각각 2-3개의 다른 위치에 보관하세요."
+ },
"yesLetsTry": {
"message": "네, 시도해보겠습니다."
},
+ "youNeedToAllowCameraAccess": {
+ "message": "이 기능을 사용하려면 카메라 접근을 허용해야 합니다."
+ },
"yourSigRequested": {
"message": "서명을 요청 중입니다."
},
@@ -1290,8 +1614,5 @@
},
"yourPrivateSeedPhrase": {
"message": "개인 시드 구문"
- },
- "youNeedToAllowCameraAccess": {
- "message": "이 기능을 사용하려면 카메라 접근을 허용해야 합니다."
}
}
diff --git a/app/images/caret-left-black.svg b/app/images/caret-left-black.svg
new file mode 100644
index 000000000000..872135ece42d
--- /dev/null
+++ b/app/images/caret-left-black.svg
@@ -0,0 +1,18 @@
+
+
diff --git a/app/images/wyre.svg b/app/images/wyre.svg
new file mode 100644
index 000000000000..a5209329d552
--- /dev/null
+++ b/app/images/wyre.svg
@@ -0,0 +1,9 @@
+
diff --git a/app/manifest.json b/app/manifest.json
index 6dbd8ad09376..6837adf619c2 100644
--- a/app/manifest.json
+++ b/app/manifest.json
@@ -1,7 +1,7 @@
{
"name": "__MSG_appName__",
"short_name": "__MSG_appName__",
- "version": "6.2.0",
+ "version": "6.3.0",
"manifest_version": 2,
"author": "https://metamask.io",
"description": "__MSG_appDescription__",
diff --git a/app/scripts/background.js b/app/scripts/background.js
index 189858a1d3b3..7fea3c8c67b9 100644
--- a/app/scripts/background.js
+++ b/app/scripts/background.js
@@ -102,7 +102,6 @@ setupMetamaskMeshMetrics()
* @property {boolean} isInitialized - Whether the first vault has been created.
* @property {boolean} isUnlocked - Whether the vault is currently decrypted and accounts are available for selection.
* @property {boolean} isAccountMenuOpen - Represents whether the main account selection UI is currently displayed.
- * @property {boolean} isMascara - True if the current context is the extensionless MetaMascara project.
* @property {boolean} isPopup - Returns true if the current view is an externally-triggered notification.
* @property {string} rpcTarget - DEPRECATED - The URL of the current RPC provider.
* @property {Object} identities - An object matching lower-case hex addresses to Identity objects with "address" and "name" (nickname) keys.
@@ -448,7 +447,7 @@ function setupController (initState, initLangCode) {
function triggerUi () {
extension.tabs.query({ active: true }, tabs => {
const currentlyActiveMetamaskTab = Boolean(tabs.find(tab => openMetamaskTabsIDs[tab.id]))
- if (!popupIsOpen && !currentlyActiveMetamaskTab) {
+ if (!popupIsOpen && !currentlyActiveMetamaskTab && !notificationIsOpen) {
notificationManager.showPopup()
notificationIsOpen = true
}
diff --git a/app/scripts/controllers/address-book.js b/app/scripts/controllers/address-book.js
deleted file mode 100644
index 4697e074c9bd..000000000000
--- a/app/scripts/controllers/address-book.js
+++ /dev/null
@@ -1,98 +0,0 @@
-const ObservableStore = require('obs-store')
-const extend = require('xtend')
-
-class AddressBookController {
-
-
- /**
- * Controller in charge of managing the address book functionality from the
- * recipients field on the send screen. Manages a history of all saved
- * addresses and all currently owned addresses.
- *
- * @typedef {Object} AddressBookController
- * @param {object} opts Overrides the defaults for the initial state of this.store
- * @property {array} opts.initState initializes the the state of the AddressBookController. Can contain an
- * addressBook property to initialize the addressBook array
- * @property {object} opts.preferencesStore the {@code PreferencesController} store
- * @property {object} store The the store of the current users address book
- * @property {array} store.addressBook An array of addresses and nicknames. These are set by the user when sending
- * to a new address.
- *
- */
- constructor ({initState, preferencesStore}) {
- this.store = new ObservableStore(extend({
- addressBook: [],
- }, initState))
- this._preferencesStore = preferencesStore
- }
-
- //
- // PUBLIC METHODS
- //
-
- /**
- * Sets a new address book in store by accepting a new address and nickname.
- *
- * @param {string} address A hex address of a new account that the user is sending to.
- * @param {string} name The name the user wishes to associate with the new account
- * @returns {Promise} Promise resolves with undefined
- *
- */
- setAddressBook (address, name) {
- return this._addToAddressBook(address, name)
- .then((addressBook) => {
- this.store.updateState({
- addressBook,
- })
- return Promise.resolve()
- })
- }
-
- /**
- * Performs the logic to add the address and name into the address book. The pushed object is an object of two
- * fields. Current behavior does not set an upper limit to the number of addresses.
- *
- * @private
- * @param {string} address A hex address of a new account that the user is sending to.
- * @param {string} name The name the user wishes to associate with the new account
- * @returns {Promise} Promises the updated addressBook array
- *
- */
- _addToAddressBook (address, name) {
- const addressBook = this._getAddressBook()
- const {identities} = this._preferencesStore.getState()
-
- const addressBookIndex = addressBook.findIndex((element) => { return element.address.toLowerCase() === address.toLowerCase() || element.name === name })
- const identitiesIndex = Object.keys(identities).findIndex((element) => { return element.toLowerCase() === address.toLowerCase() })
- // trigger this condition if we own this address--no need to overwrite.
- if (identitiesIndex !== -1) {
- return Promise.resolve(addressBook)
- // trigger this condition if we've seen this address before--may need to update nickname.
- } else if (addressBookIndex !== -1) {
- addressBook.splice(addressBookIndex, 1)
- } else if (addressBook.length > 15) {
- addressBook.shift()
- }
-
-
- addressBook.push({
- address: address,
- name,
- })
- return Promise.resolve(addressBook)
- }
-
- /**
- * Internal method to get the address book. Current persistence behavior should not require that this method be
- * called from the UI directly.
- *
- * @private
- * @returns {array} The addressBook array from the store.
- *
- */
- _getAddressBook () {
- return this.store.getState().addressBook
- }
-}
-
-module.exports = AddressBookController
diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js
index f923413537dd..7374118904c9 100644
--- a/app/scripts/controllers/preferences.js
+++ b/app/scripts/controllers/preferences.js
@@ -40,7 +40,9 @@ class PreferencesController {
// Feature flag toggling is available in the global namespace
// for convenient testing of pre-release features, and should never
// perform sensitive operations.
- featureFlags: {},
+ featureFlags: {
+ privacyMode: true,
+ },
knownMethodData: {},
participateInMetaMetrics: null,
firstTimeFlowType: null,
diff --git a/app/scripts/controllers/transactions/tx-gas-utils.js b/app/scripts/controllers/transactions/tx-gas-utils.js
index b296dc5eb5d5..76555116720f 100644
--- a/app/scripts/controllers/transactions/tx-gas-utils.js
+++ b/app/scripts/controllers/transactions/tx-gas-utils.js
@@ -7,7 +7,7 @@ const {
const { addHexPrefix } = require('ethereumjs-util')
const SIMPLE_GAS_COST = '0x5208' // Hex for 21000, cost of a simple send.
-import { TRANSACTION_NO_CONTRACT_ERROR_KEY } from '../../../../ui/app/constants/error-keys'
+import { TRANSACTION_NO_CONTRACT_ERROR_KEY } from '../../../../ui/app/helpers/constants/error-keys'
/**
tx-gas-utils are gas utility methods for Transaction manager
diff --git a/app/scripts/inpage.js b/app/scripts/inpage.js
index c7f0c56696dd..68394d1ae743 100644
--- a/app/scripts/inpage.js
+++ b/app/scripts/inpage.js
@@ -91,6 +91,10 @@ inpageProvider.enable = function ({ force } = {}) {
})
}
+// give the dapps control of a refresh they can toggle this off on the window.ethereum
+// this will be default true so it does not break any old apps.
+inpageProvider.autoRefreshOnNetworkChange = true
+
// add metamask-specific convenience methods
inpageProvider._metamask = new Proxy({
/**
diff --git a/app/scripts/lib/ComposableObservableStore.js b/app/scripts/lib/ComposableObservableStore.js
index d5ee708a1814..abb77967291b 100644
--- a/app/scripts/lib/ComposableObservableStore.js
+++ b/app/scripts/lib/ComposableObservableStore.js
@@ -40,7 +40,9 @@ class ComposableObservableStore extends ObservableStore {
getFlatState () {
let flatState = {}
for (const key in this.config) {
- flatState = { ...flatState, ...this.config[key].getState() }
+ const controller = this.config[key]
+ const state = controller.getState ? controller.getState() : controller.state
+ flatState = { ...flatState, ...state }
}
return flatState
}
diff --git a/app/scripts/lib/auto-reload.js b/app/scripts/lib/auto-reload.js
index 558391a06448..44fbe847c6a6 100644
--- a/app/scripts/lib/auto-reload.js
+++ b/app/scripts/lib/auto-reload.js
@@ -20,6 +20,10 @@ function setupDappAutoReload (web3, observable) {
})
observable.subscribe(function (state) {
+ // if the auto refresh on network change is false do not
+ // do anything
+ if (!window.ethereum.autoRefreshOnNetworkChange) return
+
// if reload in progress, no need to check reload logic
if (reloadInProgress) return
diff --git a/app/scripts/lib/buy-eth-url.js b/app/scripts/lib/buy-eth-url.js
index 4e2d0bc79429..040b5695bcb6 100644
--- a/app/scripts/lib/buy-eth-url.js
+++ b/app/scripts/lib/buy-eth-url.js
@@ -15,7 +15,7 @@ function getBuyEthUrl ({ network, amount, address }) {
let url
switch (network) {
case '1':
- url = `https://buy.coinbase.com/?code=9ec56d01-7e81-5017-930c-513daa27bb6a&amount=${amount}&address=${address}&crypto_currency=ETH`
+ url = `https://dash.sendwyre.com/sign-up`
break
case '3':
diff --git a/app/scripts/lib/notification-manager.js b/app/scripts/lib/notification-manager.js
index 969a9459a135..721d109a1a95 100644
--- a/app/scripts/lib/notification-manager.js
+++ b/app/scripts/lib/notification-manager.js
@@ -1,7 +1,6 @@
const extension = require('extensionizer')
-const height = 620
-const width = 360
-
+const NOTIFICATION_HEIGHT = 620
+const NOTIFICATION_WIDTH = 360
class NotificationManager {
@@ -26,13 +25,18 @@ class NotificationManager {
// bring focus to existing chrome popup
extension.windows.update(popup.id, { focused: true })
} else {
+ const {screenX, screenY, outerWidth, outerHeight} = window
+ const notificationTop = Math.round(screenY + (outerHeight / 2) - (NOTIFICATION_HEIGHT / 2))
+ const notificationLeft = Math.round(screenX + (outerWidth / 2) - (NOTIFICATION_WIDTH / 2))
const cb = (currentPopup) => { this._popupId = currentPopup.id }
// create new notification popup
const creation = extension.windows.create({
url: 'notification.html',
type: 'popup',
- width,
- height,
+ width: NOTIFICATION_WIDTH,
+ height: NOTIFICATION_HEIGHT,
+ top: Math.max(notificationTop, 0),
+ left: Math.max(notificationLeft, 0),
}, cb)
creation && creation.then && creation.then(cb)
}
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index 653868066374..540aee936eb1 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -26,7 +26,6 @@ const PreferencesController = require('./controllers/preferences')
const CurrencyController = require('./controllers/currency')
const NoticeController = require('./notice-controller')
const ShapeShiftController = require('./controllers/shapeshift')
-const AddressBookController = require('./controllers/address-book')
const InfuraController = require('./controllers/infura')
const BlacklistController = require('./controllers/blacklist')
const CachedBalancesController = require('./controllers/cached-balances')
@@ -55,6 +54,7 @@ const HW_WALLETS_KEYRINGS = [TrezorKeyring.type, LedgerBridgeKeyring.type]
const EthQuery = require('eth-query')
const ethUtil = require('ethereumjs-util')
const sigUtil = require('eth-sig-util')
+const { AddressBookController } = require('gaba')
module.exports = class MetamaskController extends EventEmitter {
@@ -175,11 +175,7 @@ module.exports = class MetamaskController extends EventEmitter {
keyringMemStore: this.keyringController.memStore,
})
- // address book controller
- this.addressBookController = new AddressBookController({
- initState: initState.AddressBookController,
- preferencesStore: this.preferencesController.store,
- })
+ this.addressBookController = new AddressBookController(undefined, initState.AddressBookController)
// tx mgmt
this.txController = new TransactionController({
@@ -245,7 +241,7 @@ module.exports = class MetamaskController extends EventEmitter {
TransactionController: this.txController.store,
KeyringController: this.keyringController.store,
PreferencesController: this.preferencesController.store,
- AddressBookController: this.addressBookController.store,
+ AddressBookController: this.addressBookController,
CurrencyController: this.currencyController.store,
NoticeController: this.noticeController.store,
ShapeShiftController: this.shapeshiftController.store,
@@ -267,7 +263,7 @@ module.exports = class MetamaskController extends EventEmitter {
KeyringController: this.keyringController.memStore,
PreferencesController: this.preferencesController.store,
RecentBlocksController: this.recentBlocksController.store,
- AddressBookController: this.addressBookController.store,
+ AddressBookController: this.addressBookController,
CurrencyController: this.currencyController.store,
NoticeController: this.noticeController.memStore,
ShapeshiftController: this.shapeshiftController.store,
@@ -376,7 +372,6 @@ module.exports = class MetamaskController extends EventEmitter {
const preferencesController = this.preferencesController
const txController = this.txController
const noticeController = this.noticeController
- const addressBookController = this.addressBookController
const networkController = this.networkController
const providerApprovalController = this.providerApprovalController
@@ -443,7 +438,7 @@ module.exports = class MetamaskController extends EventEmitter {
whitelistPhishingDomain: this.whitelistPhishingDomain.bind(this),
// AddressController
- setAddressBook: nodeify(addressBookController.setAddressBook, addressBookController),
+ setAddressBook: this.addressBookController.set.bind(this.addressBookController),
// KeyringController
setLocked: nodeify(this.setLocked, this),
diff --git a/auto-changelog.sh b/development/auto-changelog.sh
similarity index 100%
rename from auto-changelog.sh
rename to development/auto-changelog.sh
diff --git a/development/metamaskbot-build-announce.js b/development/metamaskbot-build-announce.js
index 96b5572fed42..07adbcb5d833 100755
--- a/development/metamaskbot-build-announce.js
+++ b/development/metamaskbot-build-announce.js
@@ -23,7 +23,6 @@ async function start () {
const SHORT_SHA1 = CIRCLE_SHA1.slice(0, 7)
const BUILD_LINK_BASE = `https://${CIRCLE_BUILD_NUM}-42009758-gh.circle-artifacts.com/0`
- const MASCARA = `${BUILD_LINK_BASE}/builds/mascara/home.html`
const CHROME = `${BUILD_LINK_BASE}/builds/metamask-chrome-${VERSION}.zip`
const FIREFOX = `${BUILD_LINK_BASE}/builds/metamask-firefox-${VERSION}.zip`
const EDGE = `${BUILD_LINK_BASE}/builds/metamask-edge-${VERSION}.zip`
@@ -34,7 +33,6 @@ async function start () {
Builds ready [${SHORT_SHA1}]:
- mascara,
chrome,
firefox,
edge,
diff --git a/development/mock-dev.js b/development/mock-dev.js
index 1af10a13178d..4a3217a06b38 100644
--- a/development/mock-dev.js
+++ b/development/mock-dev.js
@@ -14,9 +14,9 @@
const render = require('react-dom').render
const h = require('react-hyperscript')
-const Root = require('../ui/app/root')
-const configureStore = require('../ui/app/store')
-const actions = require('../ui/app/actions')
+const Root = require('../ui/app/pages')
+const configureStore = require('../ui/app/store/store')
+const actions = require('../ui/app/store/actions')
const states = require('./states')
const backGroundConnectionModifiers = require('./backGroundConnectionModifiers')
const Selector = require('./selector')
diff --git a/development/rollback.sh b/development/rollback.sh
new file mode 100755
index 000000000000..0a1d8ad62022
--- /dev/null
+++ b/development/rollback.sh
@@ -0,0 +1,35 @@
+#! /bin/bash
+
+[[ -z "$1" ]] && { echo "Rollback version is required!" ; exit 1; }
+echo "Rolling back to version $1"
+
+# Checkout branch to increment version
+git checkout -b version-increment-$1
+npm run version:bump patch
+
+# Store the new version name
+NEW_VERSION=$(cat app/manifest.json | jq -r .version)
+
+# Make sure origin tags are loaded
+git fetch origin
+
+# check out the rollback branch
+git checkout origin/v$1
+
+# Create the rollback branch.
+git checkout -b Version-$NEW_VERSION-Rollback-to-$1
+
+# Set the version files to the next one.
+git checkout master CHANGELOG.md
+git checkout master app/manifest.json
+git commit -m "Version $NEW_VERSION (Rollback to $1)"
+
+# Push the new branch to PR
+git push -u origin HEAD
+
+# Create tag and push that up too
+git tag v${NEW_VERSION}
+git push origin v${NEW_VERSION}
+
+# Cleanup version branch
+git branch -D version-increment-$1
diff --git a/development/sourcemap-validator.js b/development/sourcemap-validator.js
index 14388812870b..546745f1665d 100644
--- a/development/sourcemap-validator.js
+++ b/development/sourcemap-validator.js
@@ -1,6 +1,9 @@
const fs = require('fs')
const { SourceMapConsumer } = require('source-map')
const path = require('path')
+const pify = require('pify')
+const fsAsync = pify(fs)
+
//
// Utility to help check if sourcemaps are working
//
@@ -9,39 +12,85 @@ const path = require('path')
// if not working it may error or print minified garbage
//
-start()
+start().catch(console.error)
async function start () {
- const rawBuild = fs.readFileSync(path.join(__dirname, '/../dist/chrome/', 'inpage.js')
- , 'utf8')
- const rawSourceMap = fs.readFileSync(path.join(__dirname, '/../dist/sourcemaps/', 'inpage.js.map'), 'utf8')
+ const targetFiles = [`inpage.js`, `contentscript.js`, `ui.js`, `background.js`]
+ for (const buildName of targetFiles) {
+ await validateSourcemapForFile({ buildName })
+ }
+}
+
+async function validateSourcemapForFile ({ buildName }) {
+ console.log(`build "${buildName}"`)
+ const platform = `chrome`
+ // load build and sourcemaps
+ let rawBuild
+ try {
+ const filePath = path.join(__dirname, `/../dist/${platform}/`, `${buildName}`)
+ rawBuild = await fsAsync.readFile(filePath, 'utf8')
+ } catch (err) {}
+ if (!rawBuild) {
+ throw new Error(`SourcemapValidator - failed to load source file for "${buildName}"`)
+ }
+ // attempt to load in dist mode
+ let rawSourceMap
+ try {
+ const filePath = path.join(__dirname, `/../dist/sourcemaps/`, `${buildName}.map`)
+ rawSourceMap = await fsAsync.readFile(filePath, 'utf8')
+ } catch (err) {}
+ // attempt to load in dev mode
+ try {
+ const filePath = path.join(__dirname, `/../dist/${platform}/`, `${buildName}.map`)
+ rawSourceMap = await fsAsync.readFile(filePath, 'utf8')
+ } catch (err) {}
+ if (!rawSourceMap) {
+ throw new Error(`SourcemapValidator - failed to load sourcemaps for "${buildName}"`)
+ }
+
const consumer = await new SourceMapConsumer(rawSourceMap)
- console.log('hasContentsOfAllSources:', consumer.hasContentsOfAllSources(), '\n')
- console.log('sources:')
- consumer.sources.map((sourcePath) => console.log(sourcePath))
-
- console.log('\nexamining "new Error" statements:\n')
- const sourceLines = rawBuild.split('\n')
- sourceLines.map(line => indicesOf('new Error', line))
- .forEach((errorIndices, lineIndex) => {
- // if (errorIndex === null) return console.log('line does not contain "new Error"')
- errorIndices.forEach((errorIndex) => {
- const position = { line: lineIndex + 1, column: errorIndex }
+ const hasContentsOfAllSources = consumer.hasContentsOfAllSources()
+ if (!hasContentsOfAllSources) console.warn('SourcemapValidator - missing content of some sources...')
+
+ console.log(` sampling from ${consumer.sources.length} files`)
+ let sampleCount = 0
+
+ const buildLines = rawBuild.split('\n')
+ const targetString = 'new Error'
+ // const targetString = 'null'
+ const matchesPerLine = buildLines.map(line => indicesOf(targetString, line))
+ matchesPerLine.forEach((matchIndices, lineIndex) => {
+ matchIndices.forEach((matchColumn) => {
+ sampleCount++
+ const position = { line: lineIndex + 1, column: matchColumn }
const result = consumer.originalPositionFor(position)
- if (!result.source) return console.warn(`!! missing source for position: ${position}`)
- // filter out deps distributed minified without sourcemaps
- if (result.source === 'node_modules/browserify/node_modules/browser-pack/_prelude.js') return // minified mess
- if (result.source === 'node_modules/web3/dist/web3.min.js') return // minified mess
+ // warn if source content is missing
+ if (!result.source) {
+ console.warn(`!! missing source for position: ${JSON.stringify(position)}`)
+ // const buildLine = buildLines[position.line - 1]
+ console.warn(` origin in build:`)
+ console.warn(` ${buildLines[position.line - 2]}`)
+ console.warn(`-> ${buildLines[position.line - 1]}`)
+ console.warn(` ${buildLines[position.line - 0]}`)
+ return
+ }
const sourceContent = consumer.sourceContentFor(result.source)
const sourceLines = sourceContent.split('\n')
const line = sourceLines[result.line - 1]
- console.log(`\n========================== ${result.source} ====================================\n`)
- console.log(line)
- console.log(`\n==============================================================================\n`)
+ // this sometimes includes the whole line though we tried to match somewhere in the middle
+ const portion = line.slice(result.column)
+ const isMaybeValid = portion.includes(targetString)
+ if (!isMaybeValid) {
+ console.error('Sourcemap seems invalid:')
+ console.log(`\n========================== ${result.source} ====================================\n`)
+ console.log(line)
+ console.log(`\n==============================================================================\n`)
+ }
})
})
+ console.log(` checked ${sampleCount} samples`)
}
function indicesOf (substring, string) {
diff --git a/development/states/navigate-txs.json b/development/states/navigate-txs.json
index 813b02172379..23255abed60b 100644
--- a/development/states/navigate-txs.json
+++ b/development/states/navigate-txs.json
@@ -3,7 +3,6 @@
"isInitialized": true,
"isUnlocked": true,
"isAccountMenuOpen": false,
- "isMascara": false,
"isPopup": false,
"rpcTarget": "https://rawtestrpc.metamask.io/",
"identities": {
diff --git a/development/states/pending-tx.json b/development/states/pending-tx.json
index bfa93f7ae3c9..b4a6565fa414 100644
--- a/development/states/pending-tx.json
+++ b/development/states/pending-tx.json
@@ -2,7 +2,6 @@
"metamask": {
"isInitialized": true,
"isUnlocked": true,
- "isMascara": false,
"rpcTarget": "https://rawtestrpc.metamask.io/",
"identities": {
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
diff --git a/development/ui-dev.js b/development/ui-dev.js
index bae0ce50eb53..70f513972c38 100644
--- a/development/ui-dev.js
+++ b/development/ui-dev.js
@@ -17,7 +17,7 @@
const render = require('react-dom').render
const h = require('react-hyperscript')
-const Root = require('../ui/app/root')
+const Root = require('../ui/app/pages')
const configureStore = require('./uiStore')
const states = require('./states')
const Selector = require('./selector')
diff --git a/development/uiStore.js b/development/uiStore.js
index c71d66d3b670..bfec8f5e472e 100644
--- a/development/uiStore.js
+++ b/development/uiStore.js
@@ -2,7 +2,7 @@ const createStore = require('redux').createStore
const applyMiddleware = require('redux').applyMiddleware
const thunkMiddleware = require('redux-thunk').default
const createLogger = require('redux-logger').createLogger
-const rootReducer = require('../ui/app/reducers')
+const rootReducer = require('../ui/app/ducks')
module.exports = configureStore
diff --git a/docker-compose.yml b/docker-compose.yml
deleted file mode 100644
index 9a57617dd84a..000000000000
--- a/docker-compose.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-metamascara:
- build: ./
- restart: always
- ports:
- - "9001"
- environment:
- MASCARA_ORIGIN: "https://wallet.metamask.io"
- VIRTUAL_PORT: "9001"
- VIRTUAL_HOST: "wallet.metamask.io"
- LETSENCRYPT_HOST: "wallet.metamask.io"
- LETSENCRYPT_EMAIL: "admin@metamask.io"
\ No newline at end of file
diff --git a/docs/adding-new-networks.md b/docs/adding-new-networks.md
index b74233fa6863..40925f135054 100644
--- a/docs/adding-new-networks.md
+++ b/docs/adding-new-networks.md
@@ -5,7 +5,7 @@ To add another network to our dropdown menu, make sure the following files are a
```
app/scripts/config.js
app/scripts/lib/buy-eth-url.js
-ui/app/app.js
+ui/app/index.js
ui/app/components/buy-button-subview.js
ui/app/components/drop-menu-item.js
ui/app/components/network.js
diff --git a/docs/secret-preferences.md b/docs/secret-preferences.md
index f9d01a5038c2..58a4554c41ed 100644
--- a/docs/secret-preferences.md
+++ b/docs/secret-preferences.md
@@ -6,5 +6,5 @@ One example is our "sync with mobile" feature, which didn't make sense to roll o
To enable features like this, first open the background console, and then you can use the global method `global.setPreference(key, value)`.
-For example, if the feature flag was a booelan was called `mobileSync`, you might type `setPreference('mobileSync', true)`.
+For example, if the feature flag was a boolean was called `useNativeCurrencyAsPrimaryCurrency`, you might type `setPreference('useNativeCurrencyAsPrimaryCurrency', true)`.
diff --git a/docs/sensitive-release.md b/docs/sensitive-release.md
index e2c4e2f3d611..288df57eb9a3 100644
--- a/docs/sensitive-release.md
+++ b/docs/sensitive-release.md
@@ -18,10 +18,10 @@ Simply follow the steps in [the publishing guide](./publishing.md).
### Prepare Rollback Release
-1. Check out the tagged commit for the release before this new release.
-2. Increment the version over the new release by one patch.
-3. Merge the changelog of the new release into this branch, and make its own changelog refer to rolling back those changes.
-4. Push to the main repository and pull request against `master` prominently noting this is a `DO NOT MERGE` rollback pull request.
+1. Be on the new risky version branch.
+2. Run `npm run rollback $ROLLBACK_TARGET_VERSION`.
+
+The rollback version will now be a branch and tag on the origin.
### Roll the normal release out
diff --git a/gulpfile.js b/gulpfile.js
index c672a9eff581..c1012d5aa61b 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -51,8 +51,6 @@ const browserPlatforms = [
'opera',
]
const commonPlatforms = [
- // browser webapp
- 'mascara',
// browser extensions
...browserPlatforms,
]
@@ -110,14 +108,6 @@ createCopyTasks('manifest', {
destinations: browserPlatforms.map(platform => `./dist/${platform}`),
})
-// copy mascara
-
-createCopyTasks('html:mascara', {
- source: './mascara/',
- pattern: 'proxy/index.html',
- destinations: [`./dist/mascara/`],
-})
-
function createCopyTasks (label, opts) {
if (!opts.devOnly) {
const copyTaskName = `copy:${label}`
@@ -298,8 +288,6 @@ const buildJsFiles = [
createTasksForBuildJsUIDeps({ dependenciesToBundle: uiDependenciesToBundle, filename: 'libs' })
createTasksForBuildJsExtension({ buildJsFiles, taskPrefix: 'dev:extension:js', devMode: true })
createTasksForBuildJsExtension({ buildJsFiles, taskPrefix: 'build:extension:js' })
-createTasksForBuildJsMascara({ taskPrefix: 'build:mascara:js' })
-createTasksForBuildJsMascara({ taskPrefix: 'dev:mascara:js', devMode: true })
function createTasksForBuildJsUIDeps ({ dependenciesToBundle, filename }) {
const destinations = browserPlatforms.map(platform => `./dist/${platform}`)
@@ -340,22 +328,6 @@ function createTasksForBuildJsExtension ({ buildJsFiles, taskPrefix, devMode, bu
createTasksForBuildJs({ rootDir, taskPrefix, bundleTaskOpts, destinations, buildPhase1, buildPhase2 })
}
-function createTasksForBuildJsMascara ({ taskPrefix, devMode, bundleTaskOpts = {} }) {
- // inpage must be built before all other scripts:
- const rootDir = './mascara/src/'
- const buildPhase1 = ['ui', 'proxy', 'background', 'metamascara']
- const destinations = ['./dist/mascara']
- bundleTaskOpts = Object.assign({
- buildSourceMaps: true,
- sourceMapDir: './',
- minifyBuild: !devMode,
- buildWithFullPaths: devMode,
- watch: devMode,
- devMode,
- }, bundleTaskOpts)
- createTasksForBuildJs({ rootDir, taskPrefix, bundleTaskOpts, destinations, buildPhase1 })
-}
-
function createTasksForBuildJs ({ rootDir, taskPrefix, bundleTaskOpts, destinations, buildPhase1 = [], buildPhase2 = [] }) {
// bundle task for each file
const jsFiles = [].concat(buildPhase1, buildPhase2)
@@ -405,7 +377,6 @@ gulp.task('dev',
'dev:scss',
gulp.parallel(
'dev:extension:js',
- 'dev:mascara:js',
'dev:copy',
'dev:reload'
)
@@ -424,18 +395,6 @@ gulp.task('dev:extension',
)
)
-gulp.task('dev:mascara',
- gulp.series(
- 'clean',
- 'dev:scss',
- gulp.parallel(
- 'dev:mascara:js',
- 'dev:copy',
- 'dev:reload'
- )
- )
-)
-
gulp.task('build',
gulp.series(
'clean',
@@ -443,7 +402,6 @@ gulp.task('build',
gulpParallel(
'build:extension:js:uideps',
'build:extension:js',
- 'build:mascara:js',
'copy'
)
)
@@ -460,17 +418,6 @@ gulp.task('build:extension',
)
)
-gulp.task('build:mascara',
- gulp.series(
- 'clean',
- 'build:scss',
- gulp.parallel(
- 'build:mascara:js',
- 'copy'
- )
- )
-)
-
gulp.task('dist',
gulp.series(
'build',
diff --git a/mascara/README.md b/mascara/README.md
deleted file mode 100644
index 6e3bfe96beed..000000000000
--- a/mascara/README.md
+++ /dev/null
@@ -1,33 +0,0 @@
-start the dual servers (dapp + mascara)
-```
-npm run mascara
-```
-
-### First time use:
-
-- navigate to: http://localhost:9001
-- Create an Account
-- go back to http://localhost:9002
-- open devTools
-- click Sync Tx
-
-### Tests:
-
-```
-npm run testMascara
-```
-
-Test will run in browser, you will have to have these browsers installed:
-
-- Chrome
-- Firefox
-- Opera
-
-
-### Deploy:
-
-Will build and deploy mascara via docker
-
-```
-docker-compose build && docker-compose stop && docker-compose up -d && docker-compose logs --tail 200 -f
-```
\ No newline at end of file
diff --git a/mascara/example/app.js b/mascara/example/app.js
deleted file mode 100644
index 7b6421fdca1a..000000000000
--- a/mascara/example/app.js
+++ /dev/null
@@ -1,38 +0,0 @@
-const EthQuery = require('ethjs-query')
-
-window.addEventListener('load', loadProvider)
-window.addEventListener('message', console.warn)
-
-async function loadProvider () {
- const ethereumProvider = window.metamask.createDefaultProvider({ host: 'http://localhost:9001' })
- const ethQuery = new EthQuery(ethereumProvider)
- const accounts = await ethQuery.accounts()
- window.METAMASK_ACCOUNT = accounts[0] || 'locked'
- logToDom(accounts.length ? accounts[0] : 'LOCKED or undefined', 'account')
- setupButtons(ethQuery)
-}
-
-
-function logToDom (message, context) {
- document.getElementById(context).innerText = message
- console.log(message)
-}
-
-function setupButtons (ethQuery) {
- const accountButton = document.getElementById('action-button-1')
- accountButton.addEventListener('click', async () => {
- const accounts = await ethQuery.accounts()
- window.METAMASK_ACCOUNT = accounts[0] || 'locked'
- logToDom(accounts.length ? accounts[0] : 'LOCKED or undefined', 'account')
- })
- const txButton = document.getElementById('action-button-2')
- txButton.addEventListener('click', async () => {
- if (!window.METAMASK_ACCOUNT || window.METAMASK_ACCOUNT === 'locked') return
- const txHash = await ethQuery.sendTransaction({
- from: window.METAMASK_ACCOUNT,
- to: window.METAMASK_ACCOUNT,
- data: '',
- })
- logToDom(txHash, 'cb-value')
- })
-}
diff --git a/mascara/example/app/index.html b/mascara/example/app/index.html
deleted file mode 100644
index 8afb6f3f2ae3..000000000000
--- a/mascara/example/app/index.html
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
- MetaMask ZeroClient Example
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/mascara/example/server.js b/mascara/example/server.js
deleted file mode 100644
index bdb1efa16b80..000000000000
--- a/mascara/example/server.js
+++ /dev/null
@@ -1,31 +0,0 @@
-const express = require('express')
-const path = require('path')
-const createMetamascaraServer = require('../server/')
-const createBundle = require('../server/util').createBundle
-const serveBundle = require('../server/util').serveBundle
-//
-// Iframe Server
-//
-
-const mascaraServer = createMetamascaraServer()
-
-// start the server
-const mascaraPort = 9001
-mascaraServer.listen(mascaraPort)
-console.log(`Mascara service listening on port ${mascaraPort}`)
-
-
-//
-// Dapp Server
-//
-
-const dappServer = express()
-
-// serve dapp bundle
-serveBundle(dappServer, '/app.js', createBundle(require.resolve('./app.js')))
-dappServer.use(express.static(path.join(__dirname, '/app/')))
-
-// start the server
-const dappPort = '9002'
-dappServer.listen(dappPort)
-console.log(`Dapp listening on port ${dappPort}`)
diff --git a/mascara/proxy/index.html b/mascara/proxy/index.html
deleted file mode 100644
index 4e167db72753..000000000000
--- a/mascara/proxy/index.html
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
- MetaMask ZeroClient Iframe
-
-
-
-
-
-
-
- Hello! I am the MetaMask iframe.
-
-
-
diff --git a/mascara/server/index.js b/mascara/server/index.js
deleted file mode 100644
index a30120438212..000000000000
--- a/mascara/server/index.js
+++ /dev/null
@@ -1,21 +0,0 @@
-const path = require('path')
-const express = require('express')
-const compression = require('compression')
-
-module.exports = createMetamascaraServer
-
-
-function createMetamascaraServer () {
-
- // setup server
- const server = express()
- server.use(compression())
-
- // serve assets
- server.use(express.static(path.join(__dirname, '/../ui/'), { setHeaders: (res) => res.set('X-Frame-Options', 'DENY') }))
- server.use(express.static(path.join(__dirname, '/../../dist/mascara')))
- server.use(express.static(path.join(__dirname, '/../proxy')))
-
- return server
-
-}
diff --git a/mascara/server/util.js b/mascara/server/util.js
deleted file mode 100644
index f9692afb6065..000000000000
--- a/mascara/server/util.js
+++ /dev/null
@@ -1,47 +0,0 @@
-const browserify = require('browserify')
-const watchify = require('watchify')
-
-module.exports = {
- serveBundle,
- createBundle,
-}
-
-
-function serveBundle (server, path, bundle) {
- server.get(path, function (req, res) {
- res.setHeader('Content-Type', 'application/javascript; charset=UTF-8')
- res.send(bundle.latest)
- })
-}
-
-function createBundle (entryPoint) {
-
- var bundleContainer = {}
-
- var bundler = browserify({
- entries: [entryPoint],
- cache: {},
- packageCache: {},
- plugin: [watchify],
- })
- .transform('babelify')
- .transform('uglifyify', { global: true })
-
- bundler.on('update', bundle)
- bundle()
-
- return bundleContainer
-
- function bundle () {
- bundler.bundle(function (err, result) {
- if (err) {
- console.log(`Bundle failed! (${entryPoint})`)
- console.error(err)
- return
- }
- console.log(`Bundle updated! (${entryPoint})`)
- bundleContainer.latest = result.toString()
- })
- }
-
-}
diff --git a/mascara/src/app/buy-ether-widget/index.js b/mascara/src/app/buy-ether-widget/index.js
index c8530ba4c71b..d0d6ff343699 100644
--- a/mascara/src/app/buy-ether-widget/index.js
+++ b/mascara/src/app/buy-ether-widget/index.js
@@ -5,7 +5,7 @@ import {connect} from 'react-redux'
import {qrcode} from 'qrcode-generator'
import copyToClipboard from 'copy-to-clipboard'
import ShapeShiftForm from '../shapeshift-form'
-import {buyEth, showAccountDetail} from '../../../../ui/app/actions'
+import {buyEth, showAccountDetail} from '../../../../ui/app/store/actions'
const OPTION_VALUES = {
COINBASE: 'coinbase',
diff --git a/mascara/src/app/shapeshift-form/index.js b/mascara/src/app/shapeshift-form/index.js
index fe7f7ffcb0c3..c044f9ecc6e7 100644
--- a/mascara/src/app/shapeshift-form/index.js
+++ b/mascara/src/app/shapeshift-form/index.js
@@ -3,8 +3,8 @@ import PropTypes from 'prop-types'
import classnames from 'classnames'
import qrcode from 'qrcode-generator'
import {connect} from 'react-redux'
-import {shapeShiftSubview, pairUpdate, buyWithShapeShift} from '../../../../ui/app/actions'
-import {isValidAddress} from '../../../../ui/app/util'
+import {shapeShiftSubview, pairUpdate, buyWithShapeShift} from '../../../../ui/app/store/actions'
+import {isValidAddress} from '../../../../ui/app/helpers/utils/util'
export class ShapeShiftForm extends Component {
static propTypes = {
diff --git a/mascara/src/background.js b/mascara/src/background.js
deleted file mode 100644
index d8d1c8c54770..000000000000
--- a/mascara/src/background.js
+++ /dev/null
@@ -1,134 +0,0 @@
-global.window = global
-
-const SwGlobalListener = require('sw-stream/lib/sw-global-listener.js')
-const connectionListener = new SwGlobalListener(global)
-const setupMultiplex = require('../../app/scripts/lib/stream-utils.js').setupMultiplex
-
-const DbController = require('idb-global')
-
-const SwPlatform = require('../../app/scripts/platforms/sw')
-const MetamaskController = require('../../app/scripts/metamask-controller')
-
-const Migrator = require('../../app/scripts/lib/migrator/')
-const migrations = require('../../app/scripts/migrations/')
-const firstTimeState = require('../../app/scripts/first-time-state')
-
-const STORAGE_KEY = 'metamask-config'
-const METAMASK_DEBUG = process.env.METAMASK_DEBUG
-global.metamaskPopupIsOpen = false
-
-const log = require('loglevel')
-global.log = log
-log.setDefaultLevel(METAMASK_DEBUG ? 'debug' : 'warn')
-
-global.addEventListener('install', function (event) {
- event.waitUntil(global.skipWaiting())
-})
-global.addEventListener('activate', function (event) {
- event.waitUntil(global.clients.claim())
-})
-
-log.debug('inside:open')
-
-// state persistence
-const dbController = new DbController({
- key: STORAGE_KEY,
-})
-
-start().catch(log.error)
-
-async function start () {
- log.debug('MetaMask initializing...')
- const initState = await loadStateFromPersistence()
- await setupController(initState)
- log.debug('MetaMask initialization complete.')
-}
-
-//
-// State and Persistence
-//
-async function loadStateFromPersistence () {
- // migrations
- const migrator = new Migrator({ migrations })
- const initialState = migrator.generateInitialState(firstTimeState)
- dbController.initialState = initialState
- const versionedData = await dbController.open()
- const migratedData = await migrator.migrateData(versionedData)
- await dbController.put(migratedData)
- return migratedData.data
-}
-
-async function setupController (initState, client) {
-
- //
- // MetaMask Controller
- //
-
- const platform = new SwPlatform()
-
- const controller = new MetamaskController({
- // platform specific implementation
- platform,
- // User confirmation callbacks:
- showUnconfirmedMessage: noop,
- unlockAccountMessage: noop,
- showUnapprovedTx: noop,
- // initial state
- initState,
- })
- global.metamaskController = controller
-
- controller.store.subscribe(async (state) => {
- try {
- const versionedData = await versionifyData(state)
- await dbController.put(versionedData)
- } catch (e) { console.error('METAMASK Error:', e) }
- })
-
- async function versionifyData (state) {
- const rawData = await dbController.get()
- return {
- data: state,
- meta: rawData.meta,
- }
- }
-
- //
- // connect to other contexts
- //
-
- connectionListener.on('remote', (portStream, messageEvent) => {
- log.debug('REMOTE CONECTION FOUND***********')
- connectRemote(portStream, messageEvent.data.context)
- })
-
- function connectRemote (connectionStream, context) {
- var isMetaMaskInternalProcess = (context === 'popup')
- if (isMetaMaskInternalProcess) {
- // communication with popup
- controller.setupTrustedCommunication(connectionStream, 'MetaMask')
- global.metamaskPopupIsOpen = true
- } else {
- // communication with page
- setupUntrustedCommunication(connectionStream, context)
- }
- }
-
- function setupUntrustedCommunication (connectionStream, originDomain) {
- // setup multiplexing
- var mx = setupMultiplex(connectionStream)
- // connect features
- controller.setupProviderConnection(mx.createStream('provider'), originDomain)
- controller.setupPublicConfig(mx.createStream('publicConfig'))
- }
-}
-// // this will be useful later but commented out for linting for now (liiiinting)
-// function sendMessageToAllClients (message) {
-// global.clients.matchAll().then(function (clients) {
-// clients.forEach(function (client) {
-// client.postMessage(message)
-// })
-// })
-// }
-
-function noop () {}
diff --git a/mascara/src/metamascara.js b/mascara/src/metamascara.js
deleted file mode 100644
index 0af6f532fb27..000000000000
--- a/mascara/src/metamascara.js
+++ /dev/null
@@ -1 +0,0 @@
-global.metamask = require('metamascara')
diff --git a/mascara/src/proxy.js b/mascara/src/proxy.js
deleted file mode 100644
index 80b4dc516c1b..000000000000
--- a/mascara/src/proxy.js
+++ /dev/null
@@ -1,25 +0,0 @@
-const createParentStream = require('iframe-stream').ParentStream
-const SwController = require('sw-controller')
-const SwStream = require('sw-stream/lib/sw-stream.js')
-
-const keepAliveDelay = Math.floor(Math.random() * (30000 - 1000)) + 1000
-const background = new SwController({
- fileName: './scripts/background.js',
- keepAlive: true,
- keepAliveInterval: 30000,
- keepAliveDelay,
-})
-
-const pageStream = createParentStream()
-background.on('ready', () => {
- const swStream = SwStream({
- serviceWorker: background.controller,
- context: 'dapp',
- })
- pageStream.pipe(swStream).pipe(pageStream)
-
-})
-background.on('updatefound', () => window.location.reload())
-
-background.on('error', console.error)
-background.startWorker()
diff --git a/mascara/src/ui.js b/mascara/src/ui.js
deleted file mode 100644
index f9e7670eafd1..000000000000
--- a/mascara/src/ui.js
+++ /dev/null
@@ -1,73 +0,0 @@
-const injectCss = require('inject-css')
-const SwController = require('sw-controller')
-const SwStream = require('sw-stream')
-const MetaMaskUiCss = require('../../ui/css')
-const MetamascaraPlatform = require('../../app/scripts/platforms/window')
-const startPopup = require('../../app/scripts/popup-core')
-
-// create platform global
-global.platform = new MetamascaraPlatform()
-
-var css = MetaMaskUiCss()
-injectCss(css)
-const container = document.getElementById('app-content')
-
-const name = 'popup'
-window.METAMASK_UI_TYPE = name
-window.METAMASK_PLATFORM_TYPE = 'mascara'
-
-const keepAliveDelay = Math.floor(Math.random() * (30000 - 1000)) + 1000
-
-const swController = new SwController({
- fileName: './background.js',
- keepAlive: true,
- keepAliveDelay,
- keepAliveInterval: 20000,
-})
-
-swController.once('updatefound', windowReload)
-swController.once('ready', async () => {
- try {
- swController.removeListener('updatefound', windowReload)
- console.log('swController ready')
- await timeout(1000)
- console.log('connecting to app')
- await connectApp()
- console.log('app connected')
- } catch (err) {
- console.error(err)
- }
-})
-
-console.log('starting service worker')
-swController.startWorker()
-
-// Setup listener for when the service worker is read
-function connectApp () {
- const connectionStream = SwStream({
- serviceWorker: swController.getWorker(),
- context: name,
- })
- return new Promise((resolve, reject) => {
- startPopup({ container, connectionStream }, (err, store) => {
- console.log('hello from MetaMascara ui!')
- if (err) reject(err)
- store.subscribe(() => {
- const state = store.getState()
- if (state.appState.shouldClose) window.close()
- })
- resolve()
- })
- })
-}
-
-function windowReload () {
- if (window.METAMASK_SKIP_RELOAD) return
- window.location.reload()
-}
-
-function timeout (time) {
- return new Promise((resolve) => {
- setTimeout(resolve, time || 1500)
- })
-}
diff --git a/mascara/test/helpers.js b/mascara/test/helpers.js
deleted file mode 100644
index 8fc5c816bda9..000000000000
--- a/mascara/test/helpers.js
+++ /dev/null
@@ -1,7 +0,0 @@
-export default function wait (time) {
- return new Promise(function (resolve, reject) {
- setTimeout(function () {
- resolve()
- }, time * 3 || 1500)
- })
-}
diff --git a/mascara/test/index.js b/mascara/test/index.js
deleted file mode 100644
index d62e43705d06..000000000000
--- a/mascara/test/index.js
+++ /dev/null
@@ -1,22 +0,0 @@
-var fs = require('fs')
-var path = require('path')
-var browserify = require('browserify')
-var tests = fs.readdirSync(path.join(__dirname, 'lib'))
-var bundlePath = path.join(__dirname, 'test-bundle.js')
-var b = browserify()
-
-// Remove old bundle
-try {
- fs.unlinkSync(bundlePath)
-} catch (e) {
- console.error(e)
-}
-
-var writeStream = fs.createWriteStream(bundlePath)
-
-tests.forEach(function (fileName) {
- b.add(path.join(__dirname, 'lib', fileName))
-})
-
-b.bundle().pipe(writeStream)
-
diff --git a/mascara/test/jquery-3.1.0.min.js b/mascara/test/jquery-3.1.0.min.js
deleted file mode 100644
index f6a6a99e60ee..000000000000
--- a/mascara/test/jquery-3.1.0.min.js
+++ /dev/null
@@ -1,4 +0,0 @@
-/*! jQuery v3.1.0 | (c) jQuery Foundation | jquery.org/license */
-!function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.1.0",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null!=a?a<0?this[a+this.length]:this[a]:f.call(this)},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(a<0?b:0);return this.pushStack(c>=0&&c0&&b-1 in a)}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g,ca=function(a,b){return b?"\0"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"label"in b&&b.disabled===a||"form"in b&&b.disabled===a||"form"in b&&b.disabled===!1&&(b.isDisabled===a||b.isDisabled!==!a&&("label"in b||!ea(b))!==a)}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}},d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){if("undefined"!=typeof b.getElementsByClassName&&p)return b.getElementsByClassName(a)},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML="";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:!b||(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b&&(e===c||e.slice(0,c.length+1)===c+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[c<0?c+b:c]}),even:pa(function(a,b){for(var c=0;c=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=c<0?c+b:c;++d1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;d-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];i1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,i0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(_,aa),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=V.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(_,aa),$.test(j[0].type)&&qa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&sa(j),!a)return G.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,!b||$.test(a)&&qa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext,B=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,C=/^.[^:#\[\.,]*$/;function D(a,b,c){if(r.isFunction(b))return r.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return r.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(C.test(b))return r.filter(b,a,c);b=r.filter(b,a)}return r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType})}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;b1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(D(this,a||[],!1))},not:function(a){return this.pushStack(D(this,a||[],!0))},is:function(a){return!!D(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var E,F=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,G=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||E,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:F.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),B.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};G.prototype=r.fn,E=r(d);var H=/^(?:parents|prev(?:Until|All))/,I={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;a-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function J(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return J(a,"nextSibling")},prev:function(a){return J(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return a.contentDocument||r.merge([],a.childNodes)}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(I[a]||r.uniqueSort(e),H.test(a)&&e.reverse()),this.pushStack(e)}});var K=/\S+/g;function L(a){var b={};return r.each(a.match(K)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?L(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),c<=h&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function M(a){return a}function N(a){throw a}function O(a,b,c){var d;try{a&&r.isFunction(d=a.promise)?d.call(a).done(b).fail(c):a&&r.isFunction(d=a.then)?d.call(a,b,c):b.call(void 0,a)}catch(a){c.call(void 0,a)}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(b=f&&(d!==N&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:M,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:M)),c[2][3].add(g(0,a,r.isFunction(d)?d:N))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(b<=1&&(O(a,g.done(h(c)).resolve,g.reject),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)O(e[c],h(c),g.reject);return g.promise()}});var P=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&P.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)},r.readyException=function(b){a.setTimeout(function(){throw b})};var Q=r.Deferred();r.fn.ready=function(a){return Q.then(a)["catch"](function(a){r.readyException(a)}),this},r.extend({isReady:!1,readyWait:1,holdReady:function(a){a?r.readyWait++:r.ready(!0)},ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||Q.resolveWith(d,[r]))}}),r.ready.then=Q.then;function R(){d.removeEventListener("DOMContentLoaded",R),a.removeEventListener("load",R),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",R),a.addEventListener("load",R));var S=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)S(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,
-r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h1,null,!0)},removeData:function(a){return this.each(function(){W.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=V.get(a,b),c&&(!d||r.isArray(c)?d=V.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return V.get(a,c)||V.access(a,c,{empty:r.Callbacks("once memory").add(function(){V.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length\x20\t\r\n\f]+)/i,ja=/^$|\/(?:java|ecma)script/i,ka={option:[1,""],thead:[1,""],col:[2,""],tr:[2,""],td:[3,""],_default:[0,"",""]};ka.optgroup=ka.option,ka.tbody=ka.tfoot=ka.colgroup=ka.caption=ka.thead,ka.th=ka.td;function la(a,b){var c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&r.nodeName(a,b)?r.merge([a],c):c}function ma(a,b){for(var c=0,d=a.length;c-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=la(l.appendChild(f),"script"),j&&ma(g),c){k=0;while(f=g[k++])ja.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var pa=d.documentElement,qa=/^key/,ra=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,sa=/^([^.]*)(?:\.(.+)|)/;function ta(){return!0}function ua(){return!1}function va(){try{return d.activeElement}catch(a){}}function wa(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)wa(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=ua;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(pa,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(K)||[""],j=b.length;while(j--)h=sa.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.hasData(a)&&V.get(a);if(q&&(i=q.events)){b=(b||"").match(K)||[""],j=b.length;while(j--)if(h=sa.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&V.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(V.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;c-1:r.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h\x20\t\r\n\f]*)[^>]*)\/>/gi,ya=/
-