From e8411971419473e24defc6b69596d33a0a90ccfb Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 12 Apr 2018 13:40:54 -0700 Subject: [PATCH 01/18] Add team member documentation To help exeternal contributors make sense of who is active on the project, and who they should go to to ask certain questions. --- README.md | 1 + docs/team.md | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 docs/team.md diff --git a/README.md b/README.md index 2e68991b5..a25a8be98 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,7 @@ To write tests that will be run in the browser using QUnit, add your test files - [How to develop a live-reloading UI](./docs/ui-dev-mode.md) - [How to add a new translation to MetaMask](./docs/translating-guide.md) - [Publishing Guide](./docs/publishing.md) +- [The MetaMask Team](./docs/team.md) - [How to develop an in-browser mocked UI](./docs/ui-mock-mode.md) - [How to live reload on local dependency changes](./docs/developing-on-deps.md) - [How to add new networks to the Provider Menu](./docs/adding-new-networks.md) diff --git a/docs/team.md b/docs/team.md new file mode 100644 index 000000000..10a0729b8 --- /dev/null +++ b/docs/team.md @@ -0,0 +1,62 @@ +# The Team + +Here is an overview of the current MetaMask team, and their primary roles and responsibilities, in the order they joined the team. + +This section will describe + +## Core Team Members + +The core team maintains aspects of the main product (the extension) and the core libraries (the MetaMask Controller, provider-engine, etc). + +### Aaron Davis + +@kumavis +Founder / Technical Lead + +Especially in charge of connection to the blockchain. Wrote [provider-engine](https://github.com/MetaMask/provider-engine), and is currently working with @hermanjunge on our JavaScript light-client. + +### Dan Finlay + +@danfinlay +Software Engineer / Product Lead + +Focused on the deliverable, user-valuable aspects of MetaMask, including usability and documentation. Coordinates efforts between different branches of the team, and integrations with other projects. + +### Frankie Pangilinan + +@frankiebee +Software Engineer / Transaction Manager Lead + +Frankie contributes code throughout MetaMask, but has become especially specialized in the way MetaMask manages transactions. She is also the original lead of the [Mascara](https://github.com/MetaMask/mascara) project. + +### Kevin Serrano + +@Zanibas +Software Engineer / Project Management Lead + +Kevin is a software engineer, but also spends a lot of his time keeping the team's administrative operations running smoothly. + +### Thomas Huang + +@tmashuang +QA Engineer + +Thomas is the head of MetaMask Quality Assurance. He both takes the final pass of branches of code before we ship to production, and is also in charge of continuously improving our automated quality assurance process. + +### Christian Jeria + +@cjeria +User Experience Designer + +Christian is the lead of MetaMask's user experience. He is continuously designing prototypes, testing them with users, and refining them with our developers for production. + +## Kyokan Team Members + +[Kyokan](http://kyokan.io/) is a consulting firm that has been working closely with the MetaMask team on the latest version of our user interface. Their team members are not members of ConsenSys LLC, but they contribute a lot to the project. + +- Daniel Tsui (@sdsui) +- Chi Kei Chan (@chikeichan) +- Dan Miller (@danjm) +- David Yoo (@yookd) +- Whymarrh Whitby (@whymarrh) + From 044ac112254a86c92aec7eb51dbb1a7e60927fca Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 12 Apr 2018 13:42:59 -0700 Subject: [PATCH 02/18] Add Paul to team docs --- docs/team.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/team.md b/docs/team.md index 10a0729b8..dbc7e5bad 100644 --- a/docs/team.md +++ b/docs/team.md @@ -50,6 +50,13 @@ User Experience Designer Christian is the lead of MetaMask's user experience. He is continuously designing prototypes, testing them with users, and refining them with our developers for production. +### Paul Bouchon + +@bitpshr +Software Engineer + +The newest member of the team! Paul is currently being onboarded, and finding his niche within the team. + ## Kyokan Team Members [Kyokan](http://kyokan.io/) is a consulting firm that has been working closely with the MetaMask team on the latest version of our user interface. Their team members are not members of ConsenSys LLC, but they contribute a lot to the project. From 174fb1691dc1b4642b394dd92312a0ae2e96987f Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 12 Apr 2018 13:43:58 -0700 Subject: [PATCH 03/18] Linted --- docs/team.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/team.md b/docs/team.md index dbc7e5bad..f4b970a41 100644 --- a/docs/team.md +++ b/docs/team.md @@ -2,8 +2,6 @@ Here is an overview of the current MetaMask team, and their primary roles and responsibilities, in the order they joined the team. -This section will describe - ## Core Team Members The core team maintains aspects of the main product (the extension) and the core libraries (the MetaMask Controller, provider-engine, etc). From 153a26752f746fe82a101db8eaa67dee4a4a45e1 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 12 Apr 2018 13:47:25 -0700 Subject: [PATCH 04/18] Add Herman team documentation --- docs/team.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/team.md b/docs/team.md index f4b970a41..b6562c3cf 100644 --- a/docs/team.md +++ b/docs/team.md @@ -55,6 +55,17 @@ Software Engineer The newest member of the team! Paul is currently being onboarded, and finding his niche within the team. +## Laboratry Team Members + +These team members are working on projects that will benefit MetaMask, but are not directly working on the product itself. + +### Herman Junge + +@hermanjunge +Software Engineer + +Herman is currently leading the Mustekala project, a JavaScript, IPFS-based Ethereum light client. + ## Kyokan Team Members [Kyokan](http://kyokan.io/) is a consulting firm that has been working closely with the MetaMask team on the latest version of our user interface. Their team members are not members of ConsenSys LLC, but they contribute a lot to the project. From 136caa2034e151763347d20488f2516277631bc9 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 12 Apr 2018 14:39:28 -0700 Subject: [PATCH 05/18] Add JSDocs Page and Script Includes new script `npm run doc` for generating docs page. --- README.md | 1 + development/tools/.jsdoc.json | 25 + development/tools/README.md | 15 + development/tools/appveyor.txt | 21 + docs/jsdocs/controllers_transactions.js.html | 471 +++++++ docs/jsdocs/global.html | 486 +++++++ docs/jsdocs/index.html | 275 ++++ docs/jsdocs/metamask-controller.js.html | 1144 +++++++++++++++++ .../jsdocs/module.exports_module.exports.html | 229 ++++ docs/jsdocs/platforms_extension.js.html | 117 ++ docs/jsdocs/scripts/linenumber.js | 25 + .../scripts/prettify/Apache-License-2.0.txt | 202 +++ docs/jsdocs/scripts/prettify/lang-css.js | 2 + docs/jsdocs/scripts/prettify/prettify.js | 28 + docs/jsdocs/scripts/semantic.min.js | 19 + docs/jsdocs/styles/jsdoc.css | 626 +++++++++ docs/jsdocs/styles/override.css | 3 + docs/jsdocs/styles/prettify.css | 79 ++ docs/jsdocs/styles/semantic.min.css | 364 ++++++ package-lock.json | 607 ++++++++- package.json | 3 + 21 files changed, 4681 insertions(+), 61 deletions(-) create mode 100644 development/tools/.jsdoc.json create mode 100644 development/tools/README.md create mode 100644 development/tools/appveyor.txt create mode 100644 docs/jsdocs/controllers_transactions.js.html create mode 100644 docs/jsdocs/global.html create mode 100644 docs/jsdocs/index.html create mode 100644 docs/jsdocs/metamask-controller.js.html create mode 100644 docs/jsdocs/module.exports_module.exports.html create mode 100644 docs/jsdocs/platforms_extension.js.html create mode 100644 docs/jsdocs/scripts/linenumber.js create mode 100644 docs/jsdocs/scripts/prettify/Apache-License-2.0.txt create mode 100644 docs/jsdocs/scripts/prettify/lang-css.js create mode 100644 docs/jsdocs/scripts/prettify/prettify.js create mode 100644 docs/jsdocs/scripts/semantic.min.js create mode 100644 docs/jsdocs/styles/jsdoc.css create mode 100644 docs/jsdocs/styles/override.css create mode 100644 docs/jsdocs/styles/prettify.css create mode 100644 docs/jsdocs/styles/semantic.min.css diff --git a/README.md b/README.md index 2e68991b5..fc7de00e2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # MetaMask Browser Extension [![Build Status](https://circleci.com/gh/MetaMask/metamask-extension.svg?style=shield&circle-token=a1ddcf3cd38e29267f254c9c59d556d513e3a1fd)](https://circleci.com/gh/MetaMask/metamask-extension) [![Coverage Status](https://coveralls.io/repos/github/MetaMask/metamask-extension/badge.svg?branch=master)](https://coveralls.io/github/MetaMask/metamask-extension?branch=master) [![Greenkeeper badge](https://badges.greenkeeper.io/MetaMask/metamask-extension.svg)](https://greenkeeper.io/) [![Stories in Ready](https://badge.waffle.io/MetaMask/metamask-extension.png?label=in%20progress&title=waffle.io)](https://waffle.io/MetaMask/metamask-extension) +[Internal documentation](./docs/jsdocs) ## Support diff --git a/development/tools/.jsdoc.json b/development/tools/.jsdoc.json new file mode 100644 index 000000000..fd90bf89f --- /dev/null +++ b/development/tools/.jsdoc.json @@ -0,0 +1,25 @@ +{ + "tags": { + "allowUnknownTags": false + }, + "source": { + "include": "app/scripts/", + "includePattern": ".js$", + "excludePattern": "(node_modules/|docs)" + }, + "plugins": [ + "plugins/markdown" + ], + "opts": { + "template": "node_modules/radgrad-jsdoc-template/", + "encoding": "utf8", + "destination": "docs/jsdocs", + "recurse": true, + "verbose": true + }, + "templates": { + "cleverLinks": false, + "monospaceLinks": false + } +} + diff --git a/development/tools/README.md b/development/tools/README.md new file mode 100644 index 000000000..caef51fe6 --- /dev/null +++ b/development/tools/README.md @@ -0,0 +1,15 @@ +# Development Tools & Configurations + +This folder contains configuration files which are used by the the different +development-tools, like e.g. JsDoc. + + +## Appveyor + + +https://www.appveyor.com/docs/build-configuration/#alternative-yaml-file-location + +Withtin the configuration, point to a weblocation of a txt config file: + +https://ci.appveyor.com/project/lazaridiscom/mm-vault/settings +https://raw.githubusercontent.com/lazaridiscom/mm-vault/master/dev/tools/appveyor.txt diff --git a/development/tools/appveyor.txt b/development/tools/appveyor.txt new file mode 100644 index 000000000..4ed974079 --- /dev/null +++ b/development/tools/appveyor.txt @@ -0,0 +1,21 @@ +# Test against the latest version of this Node.js version +environment: + nodejs_version: "8" + +# Install scripts. (runs after repo cloning) +install: + # Get the latest stable version of Node.js or io.js + - ps: Install-Product node $env:nodejs_version + # install modules + - npm install + +# Post-install test scripts. +test_script: + # Output useful info for debugging. + - node --version + - npm --version + # run tests + - npm test + +# Don't actually build. +build: off diff --git a/docs/jsdocs/controllers_transactions.js.html b/docs/jsdocs/controllers_transactions.js.html new file mode 100644 index 000000000..f86d3aa48 --- /dev/null +++ b/docs/jsdocs/controllers_transactions.js.html @@ -0,0 +1,471 @@ + + + + + + + controllers/transactions.js - Documentation + + + + + + + + + + + + + + + + + + + + + +
+ +

controllers/transactions.js

+ + + + + + + +
+
+
const EventEmitter = require('events')
+const ObservableStore = require('obs-store')
+const ethUtil = require('ethereumjs-util')
+/**
+ * @file      The transaction controller. Receives incoming transactions, and emits events for various states of their processing.
+ * @copyright Copyright (c) 2018 MetaMask
+ * @license   MIT
+ */
+
+
+const Transaction = require('ethereumjs-tx')
+const EthQuery = require('ethjs-query')
+const TransactionStateManager = require('../lib/tx-state-manager')
+const TxGasUtil = require('../lib/tx-gas-utils')
+const PendingTransactionTracker = require('../lib/pending-tx-tracker')
+const NonceTracker = require('../lib/nonce-tracker')
+
+/*
+  Transaction Controller is an aggregate of sub-controllers and trackers
+  composing them in a way to be exposed to the metamask controller
+    - txStateManager
+      responsible for the state of a transaction and
+      storing the transaction
+    - pendingTxTracker
+      watching blocks for transactions to be include
+      and emitting confirmed events
+    - txGasUtil
+      gas calculations and safety buffering
+    - nonceTracker
+      calculating nonces
+*/
+
+module.exports = class TransactionController extends EventEmitter {
+  constructor (opts) {
+    super()
+    this.networkStore = opts.networkStore || new ObservableStore({})
+    this.preferencesStore = opts.preferencesStore || new ObservableStore({})
+    this.provider = opts.provider
+    this.blockTracker = opts.blockTracker
+    this.signEthTx = opts.signTransaction
+    this.getGasPrice = opts.getGasPrice
+
+    this.memStore = new ObservableStore({})
+    this.query = new EthQuery(this.provider)
+    this.txGasUtil = new TxGasUtil(this.provider)
+
+    this.txStateManager = new TransactionStateManager({
+      initState: opts.initState,
+      txHistoryLimit: opts.txHistoryLimit,
+      getNetwork: this.getNetwork.bind(this),
+    })
+
+    this.txStateManager.getFilteredTxList({
+      status: 'unapproved',
+      loadingDefaults: true,
+    }).forEach((tx) => {
+      this.addTxDefaults(tx)
+      .then((txMeta) => {
+        txMeta.loadingDefaults = false
+        this.txStateManager.updateTx(txMeta, 'transactions: gas estimation for tx on boot')
+      }).catch((error) => {
+        this.txStateManager.setTxStatusFailed(tx.id, error)
+      })
+    })
+
+    this.txStateManager.getFilteredTxList({
+      status: 'approved',
+    }).forEach((txMeta) => {
+      const txSignError = new Error('Transaction found as "approved" during boot - possibly stuck during signing')
+      this.txStateManager.setTxStatusFailed(txMeta.id, txSignError)
+    })
+
+
+    this.store = this.txStateManager.store
+    this.txStateManager.on('tx:status-update', this.emit.bind(this, 'tx:status-update'))
+    this.nonceTracker = new NonceTracker({
+      provider: this.provider,
+      getPendingTransactions: this.txStateManager.getPendingTransactions.bind(this.txStateManager),
+      getConfirmedTransactions: (address) => {
+        return this.txStateManager.getFilteredTxList({
+          from: address,
+          status: 'confirmed',
+          err: undefined,
+        })
+      },
+    })
+
+    this.pendingTxTracker = new PendingTransactionTracker({
+      provider: this.provider,
+      nonceTracker: this.nonceTracker,
+      publishTransaction: (rawTx) => this.query.sendRawTransaction(rawTx),
+      getPendingTransactions: this.txStateManager.getPendingTransactions.bind(this.txStateManager),
+      getCompletedTransactions: this.txStateManager.getConfirmedTransactions.bind(this.txStateManager),
+    })
+
+    this.txStateManager.store.subscribe(() => this.emit('update:badge'))
+
+    this.pendingTxTracker.on('tx:warning', (txMeta) => {
+      this.txStateManager.updateTx(txMeta, 'transactions/pending-tx-tracker#event: tx:warning')
+    })
+    this.pendingTxTracker.on('tx:confirmed', (txId) => this._markNonceDuplicatesDropped(txId))
+    this.pendingTxTracker.on('tx:failed', this.txStateManager.setTxStatusFailed.bind(this.txStateManager))
+    this.pendingTxTracker.on('tx:block-update', (txMeta, latestBlockNumber) => {
+      if (!txMeta.firstRetryBlockNumber) {
+        txMeta.firstRetryBlockNumber = latestBlockNumber
+        this.txStateManager.updateTx(txMeta, 'transactions/pending-tx-tracker#event: tx:block-update')
+      }
+    })
+    this.pendingTxTracker.on('tx:retry', (txMeta) => {
+      if (!('retryCount' in txMeta)) txMeta.retryCount = 0
+      txMeta.retryCount++
+      this.txStateManager.updateTx(txMeta, 'transactions/pending-tx-tracker#event: tx:retry')
+    })
+
+    this.blockTracker.on('block', this.pendingTxTracker.checkForTxInBlock.bind(this.pendingTxTracker))
+    // this is a little messy but until ethstore has been either
+    // removed or redone this is to guard against the race condition
+    this.blockTracker.on('latest', this.pendingTxTracker.resubmitPendingTxs.bind(this.pendingTxTracker))
+    this.blockTracker.on('sync', this.pendingTxTracker.queryPendingTxs.bind(this.pendingTxTracker))
+    // memstore is computed from a few different stores
+    this._updateMemstore()
+    this.txStateManager.store.subscribe(() => this._updateMemstore())
+    this.networkStore.subscribe(() => this._updateMemstore())
+    this.preferencesStore.subscribe(() => this._updateMemstore())
+  }
+
+  getState () {
+    return this.memStore.getState()
+  }
+
+  getNetwork () {
+    return this.networkStore.getState()
+  }
+
+  getSelectedAddress () {
+    return this.preferencesStore.getState().selectedAddress
+  }
+
+  getUnapprovedTxCount () {
+    return Object.keys(this.txStateManager.getUnapprovedTxList()).length
+  }
+
+  getPendingTxCount (account) {
+    return this.txStateManager.getPendingTransactions(account).length
+  }
+
+  getFilteredTxList (opts) {
+    return this.txStateManager.getFilteredTxList(opts)
+  }
+
+  getChainId () {
+    const networkState = this.networkStore.getState()
+    const getChainId = parseInt(networkState)
+    if (Number.isNaN(getChainId)) {
+      return 0
+    } else {
+      return getChainId
+    }
+  }
+
+  wipeTransactions (address) {
+    this.txStateManager.wipeTransactions(address)
+  }
+
+  // Adds a tx to the txlist
+  addTx (txMeta) {
+    this.txStateManager.addTx(txMeta)
+    this.emit(`${txMeta.id}:unapproved`, txMeta)
+  }
+
+  async newUnapprovedTransaction (txParams, opts = {}) {
+    log.debug(`MetaMaskController newUnapprovedTransaction ${JSON.stringify(txParams)}`)
+    const initialTxMeta = await this.addUnapprovedTransaction(txParams)
+    initialTxMeta.origin = opts.origin
+    this.txStateManager.updateTx(initialTxMeta, '#newUnapprovedTransaction - adding the origin')
+    // listen for tx completion (success, fail)
+    return new Promise((resolve, reject) => {
+      this.txStateManager.once(`${initialTxMeta.id}:finished`, (finishedTxMeta) => {
+        switch (finishedTxMeta.status) {
+          case 'submitted':
+            return resolve(finishedTxMeta.hash)
+          case 'rejected':
+            return reject(new Error('MetaMask Tx Signature: User denied transaction signature.'))
+          case 'failed':
+            return reject(new Error(finishedTxMeta.err.message))
+          default:
+            return reject(new Error(`MetaMask Tx Signature: Unknown problem: ${JSON.stringify(finishedTxMeta.txParams)}`))
+        }
+      })
+    })
+  }
+
+  async addUnapprovedTransaction (txParams) {
+    // validate
+    const normalizedTxParams = this._normalizeTxParams(txParams)
+    this._validateTxParams(normalizedTxParams)
+    // construct txMeta
+    let txMeta = this.txStateManager.generateTxMeta({ txParams: normalizedTxParams })
+    this.addTx(txMeta)
+    this.emit('newUnapprovedTx', txMeta)
+    // add default tx params
+    try {
+      txMeta = await this.addTxDefaults(txMeta)
+    } catch (error) {
+      console.log(error)
+      this.txStateManager.setTxStatusFailed(txMeta.id, error)
+      throw error
+    }
+    txMeta.loadingDefaults = false
+    // save txMeta
+    this.txStateManager.updateTx(txMeta)
+
+    return txMeta
+  }
+
+  async addTxDefaults (txMeta) {
+    const txParams = txMeta.txParams
+    // ensure value
+    txMeta.gasPriceSpecified = Boolean(txParams.gasPrice)
+    let gasPrice = txParams.gasPrice
+    if (!gasPrice) {
+      gasPrice = this.getGasPrice ? this.getGasPrice() : await this.query.gasPrice()
+    }
+    txParams.gasPrice = ethUtil.addHexPrefix(gasPrice.toString(16))
+    txParams.value = txParams.value || '0x0'
+    // set gasLimit
+    return await this.txGasUtil.analyzeGasUsage(txMeta)
+  }
+
+  async retryTransaction (originalTxId) {
+    const originalTxMeta = this.txStateManager.getTx(originalTxId)
+    const lastGasPrice = originalTxMeta.txParams.gasPrice
+    const txMeta = this.txStateManager.generateTxMeta({
+      txParams: originalTxMeta.txParams,
+      lastGasPrice,
+      loadingDefaults: false,
+    })
+    this.addTx(txMeta)
+    this.emit('newUnapprovedTx', txMeta)
+    return txMeta
+  }
+
+  async updateTransaction (txMeta) {
+    this.txStateManager.updateTx(txMeta, 'confTx: user updated transaction')
+  }
+
+  async updateAndApproveTransaction (txMeta) {
+    this.txStateManager.updateTx(txMeta, 'confTx: user approved transaction')
+    await this.approveTransaction(txMeta.id)
+  }
+
+  async approveTransaction (txId) {
+    let nonceLock
+    try {
+      // approve
+      this.txStateManager.setTxStatusApproved(txId)
+      // get next nonce
+      const txMeta = this.txStateManager.getTx(txId)
+      const fromAddress = txMeta.txParams.from
+      // wait for a nonce
+      nonceLock = await this.nonceTracker.getNonceLock(fromAddress)
+      // add nonce to txParams
+      // if txMeta has lastGasPrice then it is a retry at same nonce with higher
+      // gas price transaction and their for the nonce should not be calculated
+      const nonce = txMeta.lastGasPrice ? txMeta.txParams.nonce : nonceLock.nextNonce
+      txMeta.txParams.nonce = ethUtil.addHexPrefix(nonce.toString(16))
+      // add nonce debugging information to txMeta
+      txMeta.nonceDetails = nonceLock.nonceDetails
+      this.txStateManager.updateTx(txMeta, 'transactions#approveTransaction')
+      // sign transaction
+      const rawTx = await this.signTransaction(txId)
+      await this.publishTransaction(txId, rawTx)
+      // must set transaction to submitted/failed before releasing lock
+      nonceLock.releaseLock()
+    } catch (err) {
+      this.txStateManager.setTxStatusFailed(txId, err)
+      // must set transaction to submitted/failed before releasing lock
+      if (nonceLock) nonceLock.releaseLock()
+      // continue with error chain
+      throw err
+    }
+  }
+
+  async signTransaction (txId) {
+    const txMeta = this.txStateManager.getTx(txId)
+    // add network/chain id
+    const chainId = this.getChainId()
+    const txParams = Object.assign({}, txMeta.txParams, { chainId })
+    // sign tx
+    const fromAddress = txParams.from
+    const ethTx = new Transaction(txParams)
+    await this.signEthTx(ethTx, fromAddress)
+    // set state to signed
+    this.txStateManager.setTxStatusSigned(txMeta.id)
+    const rawTx = ethUtil.bufferToHex(ethTx.serialize())
+    return rawTx
+  }
+
+  async publishTransaction (txId, rawTx) {
+    const txMeta = this.txStateManager.getTx(txId)
+    txMeta.rawTx = rawTx
+    this.txStateManager.updateTx(txMeta, 'transactions#publishTransaction')
+    const txHash = await this.query.sendRawTransaction(rawTx)
+    this.setTxHash(txId, txHash)
+    this.txStateManager.setTxStatusSubmitted(txId)
+  }
+
+  async cancelTransaction (txId) {
+    this.txStateManager.setTxStatusRejected(txId)
+  }
+
+  // receives a txHash records the tx as signed
+  setTxHash (txId, txHash) {
+    // Add the tx hash to the persisted meta-tx object
+    const txMeta = this.txStateManager.getTx(txId)
+    txMeta.hash = txHash
+    this.txStateManager.updateTx(txMeta, 'transactions#setTxHash')
+  }
+
+//
+//           PRIVATE METHODS
+//
+
+  _normalizeTxParams (txParams) {
+    // functions that handle normalizing of that key in txParams
+    const whiteList = {
+      from: from => ethUtil.addHexPrefix(from).toLowerCase(),
+      to: to => ethUtil.addHexPrefix(txParams.to).toLowerCase(),
+      nonce: nonce => ethUtil.addHexPrefix(nonce),
+      value: value => ethUtil.addHexPrefix(value),
+      data: data => ethUtil.addHexPrefix(data),
+      gas: gas => ethUtil.addHexPrefix(gas),
+      gasPrice: gasPrice => ethUtil.addHexPrefix(gasPrice),
+    }
+
+    // apply only keys in the whiteList
+    const normalizedTxParams = {}
+    Object.keys(whiteList).forEach((key) => {
+      if (txParams[key]) normalizedTxParams[key] = whiteList[key](txParams[key])
+    })
+
+    return normalizedTxParams
+  }
+
+  _validateTxParams (txParams) {
+    this._validateFrom(txParams)
+    this._validateRecipient(txParams)
+    if ('value' in txParams) {
+      const value = txParams.value.toString()
+      if (value.includes('-')) {
+        throw new Error(`Invalid transaction value of ${txParams.value} not a positive number.`)
+      }
+
+      if (value.includes('.')) {
+        throw new Error(`Invalid transaction value of ${txParams.value} number must be in wei`)
+      }
+    }
+  }
+
+  _validateFrom (txParams) {
+    if ( !(typeof txParams.from === 'string') ) throw new Error(`Invalid from address ${txParams.from} not a string`)
+    if (!ethUtil.isValidAddress(txParams.from)) throw new Error('Invalid from address')
+  }
+
+  _validateRecipient (txParams) {
+    if (txParams.to === '0x' || txParams.to === null ) {
+      if (txParams.data) {
+        delete txParams.to
+      } else {
+        throw new Error('Invalid recipient address')
+      }
+    } else if ( txParams.to !== undefined && !ethUtil.isValidAddress(txParams.to) ) {
+      throw new Error('Invalid recipient address')
+    }
+    return txParams
+  }
+
+  _markNonceDuplicatesDropped (txId) {
+    this.txStateManager.setTxStatusConfirmed(txId)
+    // get the confirmed transactions nonce and from address
+    const txMeta = this.txStateManager.getTx(txId)
+    const { nonce, from } = txMeta.txParams
+    const sameNonceTxs = this.txStateManager.getFilteredTxList({nonce, from})
+    if (!sameNonceTxs.length) return
+    // mark all same nonce transactions as dropped and give i a replacedBy hash
+    sameNonceTxs.forEach((otherTxMeta) => {
+      if (otherTxMeta.id === txId) return
+      otherTxMeta.replacedBy = txMeta.hash
+      this.txStateManager.updateTx(txMeta, 'transactions/pending-tx-tracker#event: tx:confirmed reference to confirmed txHash with same nonce')
+      this.txStateManager.setTxStatusDropped(otherTxMeta.id)
+    })
+  }
+
+  _updateMemstore () {
+    const unapprovedTxs = this.txStateManager.getUnapprovedTxList()
+    const selectedAddressTxList = this.txStateManager.getFilteredTxList({
+      from: this.getSelectedAddress(),
+      metamaskNetworkId: this.getNetwork(),
+    })
+    this.memStore.updateState({ unapprovedTxs, selectedAddressTxList })
+  }
+}
+
+
+
+ + + + +
+ +
+ +
+ Documentation generated by JSDoc 3.5.5 on Thu Apr 12 2018 14:37:39 GMT-0700 (PDT) using the radgrad jsdoc theme. Derived from docdash. +
+ + + + + + diff --git a/docs/jsdocs/global.html b/docs/jsdocs/global.html new file mode 100644 index 000000000..c06006e58 --- /dev/null +++ b/docs/jsdocs/global.html @@ -0,0 +1,486 @@ + + + + + + + Global - Documentation + + + + + + + + + + + + + + + + + + + + + +
+ +

Global

+ + + + + + + +
+ +
+ +

+ +

+ + +
+ +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + + + + + + + +

Type Definitions

+ + + +

MetaMaskOptions

+ + + + + +
+ + +
Source:
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
platform + + +Platform + + + +

An object including platform-specific functions.

+ + + + + + + + +
Type:
+
    +
  • + +object + + +
  • +
+ + + + + + + + +

Platform

+ + + + + +
+ + +
Source:
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
reload + + +function + + + +

A function to reload the application.

openWindow + + +function + + + +

Opens a URL in the web browser.

getVersion + + +function + + + +

Gets the current version of MetaMask.

openExtensionInBrowser + + +function + + + +

Opens the MetaMask UI in a full window.

getPlatformInfo + + +function + + + +

Callback function that returns info about the current platform.

+ + + + + + +
+

An object that provides a variety of platform-specific functions.

+
+ + + +
Type:
+
    +
  • + +object + + +
  • +
+ + + + + + + + + + +
+ +
+ + + + +
+ +
+ +
+ Documentation generated by JSDoc 3.5.5 on Thu Apr 12 2018 14:37:39 GMT-0700 (PDT) using the radgrad jsdoc theme. Derived from docdash. +
+ + + + + + \ No newline at end of file diff --git a/docs/jsdocs/index.html b/docs/jsdocs/index.html new file mode 100644 index 000000000..7ac43e45d --- /dev/null +++ b/docs/jsdocs/index.html @@ -0,0 +1,275 @@ + + + + + + + Home - Documentation + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +

+ controllers/transactions.js +

+ + +
+ +
+
+ + + +
+ + +
Source:
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
License:
+
  • MIT
+ + + + + + + + + +
+ + + + + +

The transaction controller. Receives incoming transactions, and emits events for various states of their processing.

+ + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+ +
+ +

+ metamask-controller.js +

+ + +
+ +
+
+ + + +
+ + +
Source:
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
License:
+
  • MIT
+ + + + + + + + + +
+ + + + + +

The central metamask controller. Aggregates other controllers and exports an api.

+ + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ +
+ Documentation generated by JSDoc 3.5.5 on Thu Apr 12 2018 14:37:39 GMT-0700 (PDT) using the radgrad jsdoc theme. Derived from docdash. +
+ + + + + + \ No newline at end of file diff --git a/docs/jsdocs/metamask-controller.js.html b/docs/jsdocs/metamask-controller.js.html new file mode 100644 index 000000000..2162138d1 --- /dev/null +++ b/docs/jsdocs/metamask-controller.js.html @@ -0,0 +1,1144 @@ + + + + + + + metamask-controller.js - Documentation + + + + + + + + + + + + + + + + + + + + + +
+ +

metamask-controller.js

+ + + + + + + +
+
+
/**
+ * @file      The central metamask controller. Aggregates other controllers and exports an api.
+ * @copyright Copyright (c) 2018 MetaMask
+ * @license   MIT
+ */
+
+const EventEmitter = require('events')
+const extend = require('xtend')
+const pump = require('pump')
+const Dnode = require('dnode')
+const ObservableStore = require('obs-store')
+const asStream = require('obs-store/lib/asStream')
+const AccountTracker = require('./lib/account-tracker')
+const RpcEngine = require('json-rpc-engine')
+const debounce = require('debounce')
+const createEngineStream = require('json-rpc-middleware-stream/engineStream')
+const createFilterMiddleware = require('eth-json-rpc-filters')
+const createOriginMiddleware = require('./lib/createOriginMiddleware')
+const createLoggerMiddleware = require('./lib/createLoggerMiddleware')
+const createProviderMiddleware = require('./lib/createProviderMiddleware')
+const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex
+const KeyringController = require('eth-keyring-controller')
+const NetworkController = require('./controllers/network')
+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 RecentBlocksController = require('./controllers/recent-blocks')
+const MessageManager = require('./lib/message-manager')
+const PersonalMessageManager = require('./lib/personal-message-manager')
+const TypedMessageManager = require('./lib/typed-message-manager')
+const TransactionController = require('./controllers/transactions')
+const BalancesController = require('./controllers/computed-balances')
+const ConfigManager = require('./lib/config-manager')
+const nodeify = require('./lib/nodeify')
+const accountImporter = require('./account-import-strategies')
+const getBuyEthUrl = require('./lib/buy-eth-url')
+const Mutex = require('await-semaphore').Mutex
+const version = require('../manifest.json').version
+const BN = require('ethereumjs-util').BN
+const GWEI_BN = new BN('1000000000')
+const percentile = require('percentile')
+const seedPhraseVerifier = require('./lib/seed-phrase-verifier')
+
+/**
+ * @typedef {object} MetaMaskOptions
+ * @property {Platform} platform - An object including platform-specific functions.
+ */
+
+module.exports = class MetamaskController extends EventEmitter {
+
+  /**
+   * @constructor
+   * @param {Object} opts
+   */
+   constructor (opts) {
+    super()
+
+    // Avoids warnings when we use lots of emitters.
+    this.defaultMaxListeners = 20
+
+    this.sendUpdate = debounce(this.privateSendUpdate.bind(this), 200)
+    this.opts = opts
+    const initState = opts.initState || {}
+    this.recordFirstTimeInfo(initState)
+
+    // platform-specific api
+    this.platform = opts.platform
+
+    // observable state store
+    this.store = new ObservableStore(initState)
+
+    // lock to ensure only one vault created at once
+    this.createVaultMutex = new Mutex()
+
+    // network store
+    this.networkController = new NetworkController(initState.NetworkController)
+
+    // config manager
+    this.configManager = new ConfigManager({
+      store: this.store,
+    })
+
+    // preferences controller
+    this.preferencesController = new PreferencesController({
+      initState: initState.PreferencesController,
+      initLangCode: opts.initLangCode,
+    })
+
+    // currency controller
+    this.currencyController = new CurrencyController({
+      initState: initState.CurrencyController,
+    })
+    this.currencyController.updateConversionRate()
+    this.currencyController.scheduleConversionInterval()
+
+    // infura controller
+    this.infuraController = new InfuraController({
+      initState: initState.InfuraController,
+    })
+    this.infuraController.scheduleInfuraNetworkCheck()
+
+    this.blacklistController = new BlacklistController()
+    this.blacklistController.scheduleUpdates()
+
+    // rpc provider
+    this.provider = this.initializeProvider()
+    this.blockTracker = this.provider._blockTracker
+
+    this.recentBlocksController = new RecentBlocksController({
+      blockTracker: this.blockTracker,
+      provider: this.provider,
+    })
+
+    // account tracker watches balances, nonces, and any code at their address.
+    this.accountTracker = new AccountTracker({
+      provider: this.provider,
+      blockTracker: this.blockTracker,
+    })
+
+    // key mgmt
+    this.keyringController = new KeyringController({
+      initState: initState.KeyringController,
+      getNetwork: this.networkController.getNetworkState.bind(this.networkController),
+      encryptor: opts.encryptor || undefined,
+    })
+
+    // If only one account exists, make sure it is selected.
+    this.keyringController.memStore.subscribe((state) => {
+      const addresses = state.keyrings.reduce((res, keyring) => {
+        return res.concat(keyring.accounts)
+      }, [])
+      if (addresses.length === 1) {
+        const address = addresses[0]
+        this.preferencesController.setSelectedAddress(address)
+      }
+      this.accountTracker.syncWithAddresses(addresses)
+    })
+
+    // address book controller
+    this.addressBookController = new AddressBookController({
+      initState: initState.AddressBookController,
+    }, this.keyringController)
+
+    // tx mgmt
+    this.txController = new TransactionController({
+      initState: initState.TransactionController || initState.TransactionManager,
+      networkStore: this.networkController.networkStore,
+      preferencesStore: this.preferencesController.store,
+      txHistoryLimit: 40,
+      getNetwork: this.networkController.getNetworkState.bind(this),
+      signTransaction: this.keyringController.signTransaction.bind(this.keyringController),
+      provider: this.provider,
+      blockTracker: this.blockTracker,
+      getGasPrice: this.getGasPrice.bind(this),
+    })
+    this.txController.on('newUnapprovedTx', opts.showUnapprovedTx.bind(opts))
+
+    // computed balances (accounting for pending transactions)
+    this.balancesController = new BalancesController({
+      accountTracker: this.accountTracker,
+      txController: this.txController,
+      blockTracker: this.blockTracker,
+    })
+    this.networkController.on('networkDidChange', () => {
+      this.balancesController.updateAllBalances()
+    })
+    this.balancesController.updateAllBalances()
+
+    // notices
+    this.noticeController = new NoticeController({
+      initState: initState.NoticeController,
+      version,
+      firstVersion: initState.firstTimeInfo.version,
+    })
+    this.noticeController.updateNoticesList()
+    // to be uncommented when retrieving notices from a remote server.
+    // this.noticeController.startPolling()
+
+    this.shapeshiftController = new ShapeShiftController({
+      initState: initState.ShapeShiftController,
+    })
+
+    this.networkController.lookupNetwork()
+    this.messageManager = new MessageManager()
+    this.personalMessageManager = new PersonalMessageManager()
+    this.typedMessageManager = new TypedMessageManager()
+    this.publicConfigStore = this.initPublicConfigStore()
+
+    // manual disk state subscriptions
+    this.txController.store.subscribe((state) => {
+      this.store.updateState({ TransactionController: state })
+    })
+    this.keyringController.store.subscribe((state) => {
+      this.store.updateState({ KeyringController: state })
+    })
+    this.preferencesController.store.subscribe((state) => {
+      this.store.updateState({ PreferencesController: state })
+    })
+    this.addressBookController.store.subscribe((state) => {
+      this.store.updateState({ AddressBookController: state })
+    })
+    this.currencyController.store.subscribe((state) => {
+      this.store.updateState({ CurrencyController: state })
+    })
+    this.noticeController.store.subscribe((state) => {
+      this.store.updateState({ NoticeController: state })
+    })
+    this.shapeshiftController.store.subscribe((state) => {
+      this.store.updateState({ ShapeShiftController: state })
+    })
+    this.networkController.store.subscribe((state) => {
+      this.store.updateState({ NetworkController: state })
+    })
+
+    this.infuraController.store.subscribe((state) => {
+      this.store.updateState({ InfuraController: state })
+    })
+
+    // manual mem state subscriptions
+    const sendUpdate = this.sendUpdate.bind(this)
+    this.networkController.store.subscribe(sendUpdate)
+    this.accountTracker.store.subscribe(sendUpdate)
+    this.txController.memStore.subscribe(sendUpdate)
+    this.balancesController.store.subscribe(sendUpdate)
+    this.messageManager.memStore.subscribe(sendUpdate)
+    this.personalMessageManager.memStore.subscribe(sendUpdate)
+    this.typedMessageManager.memStore.subscribe(sendUpdate)
+    this.keyringController.memStore.subscribe(sendUpdate)
+    this.preferencesController.store.subscribe(sendUpdate)
+    this.recentBlocksController.store.subscribe(sendUpdate)
+    this.addressBookController.store.subscribe(sendUpdate)
+    this.currencyController.store.subscribe(sendUpdate)
+    this.noticeController.memStore.subscribe(sendUpdate)
+    this.shapeshiftController.store.subscribe(sendUpdate)
+    this.infuraController.store.subscribe(sendUpdate)
+  }
+
+  /**
+   * Constructor helper: initialize a provider.
+   */
+  initializeProvider () {
+    const providerOpts = {
+      static: {
+        eth_syncing: false,
+        web3_clientVersion: `MetaMask/v${version}`,
+        eth_sendTransaction: (payload, next, end) => {
+          const origin = payload.origin
+          const txParams = payload.params[0]
+          nodeify(this.txController.newUnapprovedTransaction, this.txController)(txParams, { origin }, end)
+        },
+      },
+      // account mgmt
+      getAccounts: (cb) => {
+        const isUnlocked = this.keyringController.memStore.getState().isUnlocked
+        const result = []
+        const selectedAddress = this.preferencesController.getSelectedAddress()
+
+        // only show address if account is unlocked
+        if (isUnlocked && selectedAddress) {
+          result.push(selectedAddress)
+        }
+        cb(null, result)
+      },
+      // tx signing
+      // old style msg signing
+      processMessage: this.newUnsignedMessage.bind(this),
+      // personal_sign msg signing
+      processPersonalMessage: this.newUnsignedPersonalMessage.bind(this),
+      processTypedMessage: this.newUnsignedTypedMessage.bind(this),
+    }
+    const providerProxy = this.networkController.initializeProvider(providerOpts)
+    return providerProxy
+  }
+
+  /**
+   * Constructor helper: initialize a public config store.
+   */
+  initPublicConfigStore () {
+    // get init state
+    const publicConfigStore = new ObservableStore()
+
+    // memStore -> transform -> publicConfigStore
+    this.on('update', (memState) => {
+      const publicState = selectPublicState(memState)
+      publicConfigStore.putState(publicState)
+    })
+
+    function selectPublicState (memState) {
+      const result = {
+        selectedAddress: memState.isUnlocked ? memState.selectedAddress : undefined,
+        networkVersion: memState.network,
+      }
+      return result
+    }
+
+    return publicConfigStore
+  }
+
+//=============================================================================
+// EXPOSED TO THE UI SUBSYSTEM
+//=============================================================================
+
+  /**
+   * The metamask-state of the various controllers, made available to the UI
+   *
+   * @returns {Object} status
+   */
+  getState () {
+    const wallet = this.configManager.getWallet()
+    const vault = this.keyringController.store.getState().vault
+    const isInitialized = (!!wallet || !!vault)
+
+    return extend(
+      {
+        isInitialized,
+      },
+      this.networkController.store.getState(),
+      this.accountTracker.store.getState(),
+      this.txController.memStore.getState(),
+      this.messageManager.memStore.getState(),
+      this.personalMessageManager.memStore.getState(),
+      this.typedMessageManager.memStore.getState(),
+      this.keyringController.memStore.getState(),
+      this.balancesController.store.getState(),
+      this.preferencesController.store.getState(),
+      this.addressBookController.store.getState(),
+      this.currencyController.store.getState(),
+      this.noticeController.memStore.getState(),
+      this.infuraController.store.getState(),
+      this.recentBlocksController.store.getState(),
+      // config manager
+      this.configManager.getConfig(),
+      this.shapeshiftController.store.getState(),
+      {
+        lostAccounts: this.configManager.getLostAccounts(),
+        seedWords: this.configManager.getSeedWords(),
+        forgottenPassword: this.configManager.getPasswordForgotten(),
+      }
+    )
+  }
+
+  /**
+   * Returns an api-object which is consumed by the UI
+   *
+   * @returns {Object}
+   */
+  getApi () {
+    const keyringController = this.keyringController
+    const preferencesController = this.preferencesController
+    const txController = this.txController
+    const noticeController = this.noticeController
+    const addressBookController = this.addressBookController
+    const networkController = this.networkController
+
+    return {
+      // etc
+      getState: (cb) => cb(null, this.getState()),
+      setCurrentCurrency: this.setCurrentCurrency.bind(this),
+      setUseBlockie: this.setUseBlockie.bind(this),
+      setCurrentLocale: this.setCurrentLocale.bind(this),
+      markAccountsFound: this.markAccountsFound.bind(this),
+      markPasswordForgotten: this.markPasswordForgotten.bind(this),
+      unMarkPasswordForgotten: this.unMarkPasswordForgotten.bind(this),
+
+      // coinbase
+      buyEth: this.buyEth.bind(this),
+      // shapeshift
+      createShapeShiftTx: this.createShapeShiftTx.bind(this),
+
+      // primary HD keyring management
+      addNewAccount: nodeify(this.addNewAccount, this),
+      placeSeedWords: this.placeSeedWords.bind(this),
+      verifySeedPhrase: nodeify(this.verifySeedPhrase, this),
+      clearSeedWordCache: this.clearSeedWordCache.bind(this),
+      resetAccount: nodeify(this.resetAccount, this),
+      importAccountWithStrategy: this.importAccountWithStrategy.bind(this),
+
+      // vault management
+      submitPassword: nodeify(keyringController.submitPassword, keyringController),
+
+      // network management
+      setNetworkEndpoints: nodeify(networkController.setNetworkEndpoints, networkController),
+      setProviderType: nodeify(networkController.setProviderType, networkController),
+      setCustomRpc: nodeify(this.setCustomRpc, this),
+
+      // PreferencesController
+      setSelectedAddress: nodeify(preferencesController.setSelectedAddress, preferencesController),
+      addToken: nodeify(preferencesController.addToken, preferencesController),
+      removeToken: nodeify(preferencesController.removeToken, preferencesController),
+      setCurrentAccountTab: nodeify(preferencesController.setCurrentAccountTab, preferencesController),
+      setFeatureFlag: nodeify(preferencesController.setFeatureFlag, preferencesController),
+
+      // AddressController
+      setAddressBook: nodeify(addressBookController.setAddressBook, addressBookController),
+
+      // KeyringController
+      setLocked: nodeify(keyringController.setLocked, keyringController),
+      createNewVaultAndKeychain: nodeify(this.createNewVaultAndKeychain, this),
+      createNewVaultAndRestore: nodeify(this.createNewVaultAndRestore, this),
+      addNewKeyring: nodeify(keyringController.addNewKeyring, keyringController),
+      saveAccountLabel: nodeify(keyringController.saveAccountLabel, keyringController),
+      exportAccount: nodeify(keyringController.exportAccount, keyringController),
+
+      // txController
+      cancelTransaction: nodeify(txController.cancelTransaction, txController),
+      updateTransaction: nodeify(txController.updateTransaction, txController),
+      updateAndApproveTransaction: nodeify(txController.updateAndApproveTransaction, txController),
+      retryTransaction: nodeify(this.retryTransaction, this),
+
+      // messageManager
+      signMessage: nodeify(this.signMessage, this),
+      cancelMessage: this.cancelMessage.bind(this),
+
+      // personalMessageManager
+      signPersonalMessage: nodeify(this.signPersonalMessage, this),
+      cancelPersonalMessage: this.cancelPersonalMessage.bind(this),
+
+      // personalMessageManager
+      signTypedMessage: nodeify(this.signTypedMessage, this),
+      cancelTypedMessage: this.cancelTypedMessage.bind(this),
+
+      // notices
+      checkNotices: noticeController.updateNoticesList.bind(noticeController),
+      markNoticeRead: noticeController.markNoticeRead.bind(noticeController),
+    }
+  }
+
+
+
+//=============================================================================
+// VAULT / KEYRING RELATED METHODS
+//=============================================================================
+
+  /**
+   * Creates a new Vault(?) and create a new keychain(?)
+   *
+   * A vault is ...
+   *
+   * A keychain is ...
+   *
+   *
+   * @param  {} password
+   *
+   * @returns {} vault
+   */
+  async createNewVaultAndKeychain (password) {
+    const release = await this.createVaultMutex.acquire()
+    let vault
+
+    try {
+      const accounts = await this.keyringController.getAccounts()
+
+      if (accounts.length > 0) {
+        vault = await this.keyringController.fullUpdate()
+
+      } else {
+        vault = await this.keyringController.createNewVaultAndKeychain(password)
+        this.selectFirstIdentity(vault)
+      }
+      release()
+    } catch (err) {
+      release()
+      throw err
+    }
+
+    return vault
+  }
+
+  /**
+   * Create a new Vault and restore an existent keychain
+   * @param  {} password
+   * @param  {} seed
+   */
+  async createNewVaultAndRestore (password, seed) {
+    const release = await this.createVaultMutex.acquire()
+    try {
+      const vault = await this.keyringController.createNewVaultAndRestore(password, seed)
+      this.selectFirstIdentity(vault)
+      release()
+      return vault
+    } catch (err) {
+      release()
+      throw err
+    }
+  }
+
+  /**
+   * Retrieves the first Identiy from the passed Vault and selects the related address
+   *
+   * An Identity is ...
+   *
+   * @param  {} vault
+   */
+  selectFirstIdentity (vault) {
+    const { identities } = vault
+    const address = Object.keys(identities)[0]
+    this.preferencesController.setSelectedAddress(address)
+  }
+
+  // ?
+  // Opinionated Keyring Management
+  //
+
+  /**
+   * Adds a new account to ...
+   *
+   * @returns {} keyState
+   */
+  async addNewAccount () {
+    const primaryKeyring = this.keyringController.getKeyringsByType('HD Key Tree')[0]
+    if (!primaryKeyring) {
+      throw new Error('MetamaskController - No HD Key Tree found')
+    }
+    const keyringController = this.keyringController
+    const oldAccounts = await keyringController.getAccounts()
+    const keyState = await keyringController.addNewAccount(primaryKeyring)
+    const newAccounts = await keyringController.getAccounts()
+
+    await this.verifySeedPhrase()
+
+    newAccounts.forEach((address) => {
+      if (!oldAccounts.includes(address)) {
+        this.preferencesController.setSelectedAddress(address)
+      }
+    })
+
+    return keyState
+  }
+
+  /**
+   * Adds the current vault's seed words to the UI's state tree.
+   *
+   * Used when creating a first vault, to allow confirmation.
+   * Also used when revealing the seed words in the confirmation view.
+   */
+  placeSeedWords (cb) {
+
+    this.verifySeedPhrase()
+      .then((seedWords) => {
+        this.configManager.setSeedWords(seedWords)
+        return cb(null, seedWords)
+      })
+      .catch((err) => {
+        return cb(err)
+      })
+  }
+
+  /**
+   * Verifies the validity of the current vault's seed phrase.
+   *
+   * Validity: seed phrase restores the accounts belonging to the current vault.
+   *
+   * Called when the first account is created and on unlocking the vault.
+   */
+  async verifySeedPhrase () {
+
+    const primaryKeyring = this.keyringController.getKeyringsByType('HD Key Tree')[0]
+    if (!primaryKeyring) {
+      throw new Error('MetamaskController - No HD Key Tree found')
+    }
+
+    const serialized = await primaryKeyring.serialize()
+    const seedWords = serialized.mnemonic
+
+    const accounts = await primaryKeyring.getAccounts()
+    if (accounts.length < 1) {
+      throw new Error('MetamaskController - No accounts found')
+    }
+
+    try {
+      await seedPhraseVerifier.verifyAccounts(accounts, seedWords)
+      return seedWords
+    } catch (err) {
+      log.error(err.message)
+      throw err
+    }
+  }
+
+  /**
+   * Remove the primary account seed phrase from the UI's state tree.
+   *
+   * The seed phrase remains available in the background process.
+   *
+   */
+  clearSeedWordCache (cb) {
+    this.configManager.setSeedWords(null)
+    cb(null, this.preferencesController.getSelectedAddress())
+  }
+
+  /**
+   * ?
+   */
+  async resetAccount (cb) {
+    const selectedAddress = this.preferencesController.getSelectedAddress()
+    this.txController.wipeTransactions(selectedAddress)
+
+    const networkController = this.networkController
+    const oldType = networkController.getProviderConfig().type
+    await networkController.setProviderType(oldType, true)
+
+    return selectedAddress
+  }
+
+  /**
+   * Imports an account ... ?
+   *
+   * @param  {} strategy
+   * @param  {} args
+   * @param  {} cb
+   */
+  importAccountWithStrategy (strategy, args, cb) {
+    accountImporter.importAccount(strategy, args)
+    .then((privateKey) => {
+      return this.keyringController.addNewKeyring('Simple Key Pair', [ privateKey ])
+    })
+    .then(keyring => keyring.getAccounts())
+    .then((accounts) => this.preferencesController.setSelectedAddress(accounts[0]))
+    .then(() => { cb(null, this.keyringController.fullUpdate()) })
+    .catch((reason) => { cb(reason) })
+  }
+
+  // ---------------------------------------------------------------------------
+  // Identity Management (sign)
+
+  /**
+   * @param  {} msgParams
+   * @param  {} cb
+   */
+  signMessage (msgParams, cb) {
+    log.info('MetaMaskController - signMessage')
+    const msgId = msgParams.metamaskId
+
+    // sets the status op the message to 'approved'
+    // and removes the metamaskId for signing
+    return this.messageManager.approveMessage(msgParams)
+    .then((cleanMsgParams) => {
+      // signs the message
+      return this.keyringController.signMessage(cleanMsgParams)
+    })
+    .then((rawSig) => {
+      // tells the listener that the message has been signed
+      // and can be returned to the dapp
+      this.messageManager.setMsgStatusSigned(msgId, rawSig)
+      return this.getState()
+    })
+  }
+
+  // Prefixed Style Message Signing Methods:
+
+  /**
+   *
+   * @param  {} msgParams
+   * @param  {} cb
+   */
+  approvePersonalMessage (msgParams, cb) {
+    const msgId = this.personalMessageManager.addUnapprovedMessage(msgParams)
+    this.sendUpdate()
+    this.opts.showUnconfirmedMessage()
+    this.personalMessageManager.once(`${msgId}:finished`, (data) => {
+      switch (data.status) {
+        case 'signed':
+          return cb(null, data.rawSig)
+        case 'rejected':
+          return cb(new Error('MetaMask Message Signature: User denied transaction signature.'))
+        default:
+          return cb(new Error(`MetaMask Message Signature: Unknown problem: ${JSON.stringify(msgParams)}`))
+      }
+    })
+  }
+
+  /**
+   * @param  {} msgParams
+   */
+  signPersonalMessage (msgParams) {
+    log.info('MetaMaskController - signPersonalMessage')
+    const msgId = msgParams.metamaskId
+    // sets the status op the message to 'approved'
+    // and removes the metamaskId for signing
+    return this.personalMessageManager.approveMessage(msgParams)
+    .then((cleanMsgParams) => {
+      // signs the message
+      return this.keyringController.signPersonalMessage(cleanMsgParams)
+    })
+    .then((rawSig) => {
+      // tells the listener that the message has been signed
+      // and can be returned to the dapp
+      this.personalMessageManager.setMsgStatusSigned(msgId, rawSig)
+      return this.getState()
+    })
+  }
+
+  /**
+   * @param  {} msgParams
+   */
+  signTypedMessage (msgParams) {
+    log.info('MetaMaskController - signTypedMessage')
+    const msgId = msgParams.metamaskId
+    // sets the status op the message to 'approved'
+    // and removes the metamaskId for signing
+    return this.typedMessageManager.approveMessage(msgParams)
+      .then((cleanMsgParams) => {
+        // signs the message
+        return this.keyringController.signTypedMessage(cleanMsgParams)
+      })
+      .then((rawSig) => {
+        // tells the listener that the message has been signed
+        // and can be returned to the dapp
+        this.typedMessageManager.setMsgStatusSigned(msgId, rawSig)
+        return this.getState()
+      })
+  }
+
+  // ---------------------------------------------------------------------------
+  // Account Restauration
+
+  /**
+   * ?
+   *
+   * @param  {} migratorOutput
+   */
+  restoreOldVaultAccounts (migratorOutput) {
+    const { serialized } = migratorOutput
+    return this.keyringController.restoreKeyring(serialized)
+    .then(() => migratorOutput)
+  }
+
+  /**
+   * ?
+   *
+   * @param  {} migratorOutput
+   */
+  restoreOldLostAccounts (migratorOutput) {
+    const { lostAccounts } = migratorOutput
+    if (lostAccounts) {
+      this.configManager.setLostAccounts(lostAccounts.map(acct => acct.address))
+      return this.importLostAccounts(migratorOutput)
+    }
+    return Promise.resolve(migratorOutput)
+  }
+
+  /**
+   * Import (lost) Accounts
+   *
+   * @param  {Object} {lostAccounts} @Array accounts <{ address, privateKey }>
+   *
+   * Uses the array's private keys to create a new Simple Key Pair keychain
+   * and add it to the keyring controller.
+   */
+  importLostAccounts ({ lostAccounts }) {
+    const privKeys = lostAccounts.map(acct => acct.privateKey)
+    return this.keyringController.restoreKeyring({
+      type: 'Simple Key Pair',
+      data: privKeys,
+    })
+  }
+
+//=============================================================================
+// END (VAULT / KEYRING RELATED METHODS)
+//=============================================================================
+
+//
+
+//=============================================================================
+// MESSAGES
+//=============================================================================
+
+  async retryTransaction (txId, cb) {
+    await this.txController.retryTransaction(txId)
+    const state = await this.getState()
+    return state
+  }
+
+
+  newUnsignedMessage (msgParams, cb) {
+    const msgId = this.messageManager.addUnapprovedMessage(msgParams)
+    this.sendUpdate()
+    this.opts.showUnconfirmedMessage()
+    this.messageManager.once(`${msgId}:finished`, (data) => {
+      switch (data.status) {
+        case 'signed':
+          return cb(null, data.rawSig)
+        case 'rejected':
+          return cb(new Error('MetaMask Message Signature: User denied message signature.'))
+        default:
+          return cb(new Error(`MetaMask Message Signature: Unknown problem: ${JSON.stringify(msgParams)}`))
+      }
+    })
+  }
+
+  newUnsignedPersonalMessage (msgParams, cb) {
+    if (!msgParams.from) {
+      return cb(new Error('MetaMask Message Signature: from field is required.'))
+    }
+
+    const msgId = this.personalMessageManager.addUnapprovedMessage(msgParams)
+    this.sendUpdate()
+    this.opts.showUnconfirmedMessage()
+    this.personalMessageManager.once(`${msgId}:finished`, (data) => {
+      switch (data.status) {
+        case 'signed':
+          return cb(null, data.rawSig)
+        case 'rejected':
+          return cb(new Error('MetaMask Message Signature: User denied message signature.'))
+        default:
+          return cb(new Error(`MetaMask Message Signature: Unknown problem: ${JSON.stringify(msgParams)}`))
+      }
+    })
+  }
+
+  newUnsignedTypedMessage (msgParams, cb) {
+    let msgId
+    try {
+      msgId = this.typedMessageManager.addUnapprovedMessage(msgParams)
+      this.sendUpdate()
+      this.opts.showUnconfirmedMessage()
+    } catch (e) {
+      return cb(e)
+    }
+
+    this.typedMessageManager.once(`${msgId}:finished`, (data) => {
+      switch (data.status) {
+        case 'signed':
+          return cb(null, data.rawSig)
+        case 'rejected':
+          return cb(new Error('MetaMask Message Signature: User denied message signature.'))
+        default:
+          return cb(new Error(`MetaMask Message Signature: Unknown problem: ${JSON.stringify(msgParams)}`))
+      }
+    })
+  }
+
+  cancelMessage (msgId, cb) {
+    const messageManager = this.messageManager
+    messageManager.rejectMsg(msgId)
+    if (cb && typeof cb === 'function') {
+      cb(null, this.getState())
+    }
+  }
+
+  cancelPersonalMessage (msgId, cb) {
+    const messageManager = this.personalMessageManager
+    messageManager.rejectMsg(msgId)
+    if (cb && typeof cb === 'function') {
+      cb(null, this.getState())
+    }
+  }
+
+  cancelTypedMessage (msgId, cb) {
+    const messageManager = this.typedMessageManager
+    messageManager.rejectMsg(msgId)
+    if (cb && typeof cb === 'function') {
+      cb(null, this.getState())
+    }
+  }
+
+  markAccountsFound (cb) {
+    this.configManager.setLostAccounts([])
+    this.sendUpdate()
+    cb(null, this.getState())
+  }
+
+  markPasswordForgotten(cb) {
+    this.configManager.setPasswordForgotten(true)
+    this.sendUpdate()
+    cb()
+  }
+
+  unMarkPasswordForgotten(cb) {
+    this.configManager.setPasswordForgotten(false)
+    this.sendUpdate()
+    cb()
+  }
+
+//=============================================================================
+// SETUP
+//=============================================================================
+
+  setupUntrustedCommunication (connectionStream, originDomain) {
+    // Check if new connection is blacklisted
+    if (this.blacklistController.checkForPhishing(originDomain)) {
+      log.debug('MetaMask - sending phishing warning for', originDomain)
+      this.sendPhishingWarning(connectionStream, originDomain)
+      return
+    }
+
+    // setup multiplexing
+    const mux = setupMultiplex(connectionStream)
+    // connect features
+    this.setupProviderConnection(mux.createStream('provider'), originDomain)
+    this.setupPublicConfig(mux.createStream('publicConfig'))
+  }
+
+  setupTrustedCommunication (connectionStream, originDomain) {
+    // setup multiplexing
+    const mux = setupMultiplex(connectionStream)
+    // connect features
+    this.setupControllerConnection(mux.createStream('controller'))
+    this.setupProviderConnection(mux.createStream('provider'), originDomain)
+  }
+
+  sendPhishingWarning (connectionStream, hostname) {
+    const mux = setupMultiplex(connectionStream)
+    const phishingStream = mux.createStream('phishing')
+    phishingStream.write({ hostname })
+  }
+
+  setupControllerConnection (outStream) {
+    const api = this.getApi()
+    const dnode = Dnode(api)
+    pump(
+      outStream,
+      dnode,
+      outStream,
+      (err) => {
+        if (err) log.error(err)
+      }
+    )
+    dnode.on('remote', (remote) => {
+      // push updates to popup
+      const sendUpdate = remote.sendUpdate.bind(remote)
+      this.on('update', sendUpdate)
+    })
+  }
+
+  setupProviderConnection (outStream, origin) {
+    // setup json rpc engine stack
+    const engine = new RpcEngine()
+
+    // create filter polyfill middleware
+    const filterMiddleware = createFilterMiddleware({
+      provider: this.provider,
+      blockTracker: this.provider._blockTracker,
+    })
+
+    engine.push(createOriginMiddleware({ origin }))
+    engine.push(createLoggerMiddleware({ origin }))
+    engine.push(filterMiddleware)
+    engine.push(createProviderMiddleware({ provider: this.provider }))
+
+    // setup connection
+    const providerStream = createEngineStream({ engine })
+    pump(
+      outStream,
+      providerStream,
+      outStream,
+      (err) => {
+        // cleanup filter polyfill middleware
+        filterMiddleware.destroy()
+        if (err) log.error(err)
+      }
+    )
+  }
+
+  setupPublicConfig (outStream) {
+    pump(
+      asStream(this.publicConfigStore),
+      outStream,
+      (err) => {
+        if (err) log.error(err)
+      }
+    )
+  }
+
+  privateSendUpdate () {
+    this.emit('update', this.getState())
+  }
+
+  getGasPrice () {
+    const { recentBlocksController } = this
+    const { recentBlocks } = recentBlocksController.store.getState()
+
+    // Return 1 gwei if no blocks have been observed:
+    if (recentBlocks.length === 0) {
+      return '0x' + GWEI_BN.toString(16)
+    }
+
+    const lowestPrices = recentBlocks.map((block) => {
+      if (!block.gasPrices || block.gasPrices.length < 1) {
+        return GWEI_BN
+      }
+      return block.gasPrices
+      .map(hexPrefix => hexPrefix.substr(2))
+      .map(hex => new BN(hex, 16))
+      .sort((a, b) => {
+        return a.gt(b) ? 1 : -1
+      })[0]
+    })
+    .map(number => number.div(GWEI_BN).toNumber())
+
+    const percentileNum = percentile(50, lowestPrices)
+    const percentileNumBn = new BN(percentileNum)
+    return '0x' + percentileNumBn.mul(GWEI_BN).toString(16)
+  }
+
+//=============================================================================
+// CONFIG
+//=============================================================================
+
+  // Log blocks
+
+  setCurrentCurrency (currencyCode, cb) {
+    try {
+      this.currencyController.setCurrentCurrency(currencyCode)
+      this.currencyController.updateConversionRate()
+      const data = {
+        conversionRate: this.currencyController.getConversionRate(),
+        currentCurrency: this.currencyController.getCurrentCurrency(),
+        conversionDate: this.currencyController.getConversionDate(),
+      }
+      cb(null, data)
+    } catch (err) {
+      cb(err)
+    }
+  }
+
+  buyEth (address, amount) {
+    if (!amount) amount = '5'
+    const network = this.networkController.getNetworkState()
+    const url = getBuyEthUrl({ network, address, amount })
+    if (url) this.platform.openWindow({ url })
+  }
+
+  createShapeShiftTx (depositAddress, depositType) {
+    this.shapeshiftController.createShapeShiftTx(depositAddress, depositType)
+  }
+
+  // network
+
+  async setCustomRpc (rpcTarget, rpcList) {
+    this.networkController.setRpcTarget(rpcTarget)
+    await this.preferencesController.updateFrequentRpcList(rpcTarget)
+    return rpcTarget
+  }
+
+  setUseBlockie (val, cb) {
+    try {
+      this.preferencesController.setUseBlockie(val)
+      cb(null)
+    } catch (err) {
+      cb(err)
+    }
+  }
+
+  setCurrentLocale (key, cb) {
+    try {
+      this.preferencesController.setCurrentLocale(key)
+      cb(null)
+    } catch (err) {
+      cb(err)
+    }
+  }
+
+  /**
+   * Records the MetaMask version and time of first installation,
+   * mutating the initState param.
+   *
+   * @private
+   *
+   * @param {object} initState The initial state passed to the controller,
+   * which may be new.
+   */
+  recordFirstTimeInfo (initState) {
+    if (!('firstTimeInfo' in initState)) {
+      initState.firstTimeInfo = {
+        version,
+        date: Date.now(),
+      }
+    }
+  }
+
+}
+
+
+
+ + + + +
+ +
+ +
+ Documentation generated by JSDoc 3.5.5 on Thu Apr 12 2018 14:37:39 GMT-0700 (PDT) using the radgrad jsdoc theme. Derived from docdash. +
+ + + + + + diff --git a/docs/jsdocs/module.exports_module.exports.html b/docs/jsdocs/module.exports_module.exports.html new file mode 100644 index 000000000..5558cea07 --- /dev/null +++ b/docs/jsdocs/module.exports_module.exports.html @@ -0,0 +1,229 @@ + + + + + + + exports - Documentation + + + + + + + + + + + + + + + + + + + + + +
+ +

exports

+ + + + + + + +
+ +
+ +

+ exports +

+ + +
+ +
+
+ + + + + +

new exports(opts)

+ + + + + + +
+ + +
Source:
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
opts + + +Object + + + +
+ + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ +
+ Documentation generated by JSDoc 3.5.5 on Thu Apr 12 2018 14:37:39 GMT-0700 (PDT) using the radgrad jsdoc theme. Derived from docdash. +
+ + + + + + \ No newline at end of file diff --git a/docs/jsdocs/platforms_extension.js.html b/docs/jsdocs/platforms_extension.js.html new file mode 100644 index 000000000..c75f6c108 --- /dev/null +++ b/docs/jsdocs/platforms_extension.js.html @@ -0,0 +1,117 @@ + + + + + + + platforms/extension.js - Documentation + + + + + + + + + + + + + + + + + + + + + +
+ +

platforms/extension.js

+ + + + + + + +
+
+
const extension = require('extensionizer')
+
+/**
+ * An object that provides a variety of platform-specific functions.
+ *
+ * @typedef {object} Platform
+ *
+ * @property {Function} reload - A function to reload the application.
+ * @property {Function} openWindow - Opens a URL in the web browser.
+ * @property {Function} getVersion - Gets the current version of MetaMask.
+ * @property {Function} openExtensionInBrowser - Opens the MetaMask UI in a full window.
+ * @property {Function} getPlatformInfo - Callback function that returns info about the current platform.
+ */
+
+class ExtensionPlatform {
+
+  //
+  // Public
+  //
+  reload () {
+    extension.runtime.reload()
+  }
+
+  openWindow ({ url }) {
+    extension.tabs.create({ url })
+  }
+
+  getVersion () {
+    return extension.runtime.getManifest().version
+  }
+
+  openExtensionInBrowser () {
+    const extensionURL = extension.runtime.getURL('home.html')
+    this.openWindow({ url: extensionURL })
+  }
+
+  getPlatformInfo (cb) {
+    try {
+      extension.runtime.getPlatformInfo((platform) => {
+        cb(null, platform)
+      })
+    } catch (e) {
+      cb(e)
+    }
+  }
+}
+
+module.exports = ExtensionPlatform
+
+
+
+ + + + +
+ +
+ +
+ Documentation generated by JSDoc 3.5.5 on Thu Apr 12 2018 14:37:39 GMT-0700 (PDT) using the radgrad jsdoc theme. Derived from docdash. +
+ + + + + + diff --git a/docs/jsdocs/scripts/linenumber.js b/docs/jsdocs/scripts/linenumber.js new file mode 100644 index 000000000..9cb891484 --- /dev/null +++ b/docs/jsdocs/scripts/linenumber.js @@ -0,0 +1,25 @@ +/*global document */ +(function() { + var source = document.getElementsByClassName('prettyprint source linenums'); + var i = 0; + var lineNumber = 0; + var lineId; + var lines; + var totalLines; + var anchorHash; + + if (source && source[0]) { + anchorHash = document.location.hash.substring(1); + lines = source[0].getElementsByTagName('li'); + totalLines = lines.length; + + for (; i < totalLines; i++) { + lineNumber++; + lineId = 'line' + lineNumber; + lines[i].id = lineId; + if (lineId === anchorHash) { + lines[i].className += ' selected'; + } + } + } +})(); diff --git a/docs/jsdocs/scripts/prettify/Apache-License-2.0.txt b/docs/jsdocs/scripts/prettify/Apache-License-2.0.txt new file mode 100644 index 000000000..75b52484e --- /dev/null +++ b/docs/jsdocs/scripts/prettify/Apache-License-2.0.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/docs/jsdocs/scripts/prettify/lang-css.js b/docs/jsdocs/scripts/prettify/lang-css.js new file mode 100644 index 000000000..bb6dbea43 --- /dev/null +++ b/docs/jsdocs/scripts/prettify/lang-css.js @@ -0,0 +1,2 @@ +PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n "]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com", +/^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]); diff --git a/docs/jsdocs/scripts/prettify/prettify.js b/docs/jsdocs/scripts/prettify/prettify.js new file mode 100644 index 000000000..ec2a488bd --- /dev/null +++ b/docs/jsdocs/scripts/prettify/prettify.js @@ -0,0 +1,28 @@ +var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; +(function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= +[],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), +l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, +q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, +q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, +"");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), +a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} +for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], +"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], +H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], +J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ +I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), +["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", +/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), +["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", +hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= +!k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p0&&(o.verbose("Modifying existing settings",s),s[r]("setting",t,n))))})},settings:function(t,n,i){n="string"==typeof n?[n]:n||d.modules,i=void 0===i||i,e.each(n,function(n,a){var r;o.moduleExists(a)&&(o.verbose("Changing default setting",t,a),e.extend(!0,e.fn[a].settings,t),i&&f&&(r=e(":data(module-"+f+")"),r.length>0&&(o.verbose("Modifying existing settings",r),r[a]("setting",t))))})}},enable:{console:function(){o.console(!0)},debug:function(e,t){e=e||d.modules,o.debug("Enabling debug for modules",e),o.change.setting("debug",!0,e,t)},verbose:function(e,t){e=e||d.modules,o.debug("Enabling verbose debug for modules",e),o.change.setting("verbose",!0,e,t)}},disable:{console:function(){o.console(!1)},debug:function(e,t){e=e||d.modules,o.debug("Disabling debug for modules",e),o.change.setting("debug",!1,e,t)},verbose:function(e,t){e=e||d.modules,o.debug("Disabling verbose debug for modules",e),o.change.setting("verbose",!1,e,t)}},console:function(e){if(e){if(void 0===b.cache.console)return void o.error(m.console);o.debug("Restoring console function"),t.console=b.cache.console}else o.debug("Disabling console function"),b.cache.console=t.console,t.console={clear:function(){},error:function(){},group:function(){},groupCollapsed:function(){},groupEnd:function(){},info:function(){},log:function(){},markTimeline:function(){},warn:function(){}}},destroy:function(){o.verbose("Destroying previous site for",p),p.removeData(g)},cache:{},setting:function(t,n){if(e.isPlainObject(t))e.extend(!0,d,t);else{if(void 0===n)return d[t];d[t]=n}},internal:function(t,n){if(e.isPlainObject(t))e.extend(!0,o,t);else{if(void 0===n)return o[t];o[t]=n}},debug:function(){d.debug&&(d.performance?o.performance.log(arguments):(o.debug=Function.prototype.bind.call(console.info,console,d.name+":"),o.debug.apply(console,arguments)))},verbose:function(){d.verbose&&d.debug&&(d.performance?o.performance.log(arguments):(o.verbose=Function.prototype.bind.call(console.info,console,d.name+":"),o.verbose.apply(console,arguments)))},error:function(){o.error=Function.prototype.bind.call(console.error,console,d.name+":"),o.error.apply(console,arguments)},performance:{log:function(e){var t,n,i;d.performance&&(t=(new Date).getTime(),i=r||t,n=t-i,r=t,s.push({Element:h,Name:e[0],Arguments:[].slice.call(e,1)||"","Execution Time":n})),clearTimeout(o.performance.timer),o.performance.timer=setTimeout(o.performance.display,500)},display:function(){var t=d.name+":",n=0;r=!1,clearTimeout(o.performance.timer),e.each(s,function(e,t){n+=t["Execution Time"]}),t+=" "+n+"ms",(void 0!==console.group||void 0!==console.table)&&s.length>0&&(console.groupCollapsed(t),console.table?console.table(s):e.each(s,function(e,t){console.log(t.Name+": "+t["Execution Time"]+"ms")}),console.groupEnd()),s=[]}},invoke:function(t,n,i){var r,s,l,c=b;return n=n||u,i=h||i,"string"==typeof t&&void 0!==c&&(t=t.split(/[\. ]/),r=t.length-1,e.each(t,function(n,i){var a=n!=r?i+t[n+1].charAt(0).toUpperCase()+t[n+1].slice(1):t;if(e.isPlainObject(c[a])&&n!=r)c=c[a];else{if(void 0!==c[a])return s=c[a],!1;if(!e.isPlainObject(c[i])||n==r)return void 0!==c[i]?(s=c[i],!1):(o.error(m.method,t),!1);c=c[i]}})),e.isFunction(s)?l=s.apply(i,n):void 0!==s&&(l=s),e.isArray(a)?a.push(l):void 0!==a?a=[a,l]:void 0!==l&&(a=l),s}},c?(void 0===b&&o.initialize(),o.invoke(l)):(void 0!==b&&o.destroy(),o.initialize()),void 0!==a?a:this},e.site.settings={name:"Site",namespace:"site",error:{console:"Console cannot be restored, most likely it was overwritten outside of module",method:"The method you called is not defined."},debug:!1,verbose:!1,performance:!0,modules:["accordion","api","checkbox","dimmer","dropdown","embed","form","modal","nag","popup","rating","shape","sidebar","state","sticky","tab","transition","visit","visibility"],siteNamespace:"site",namespaceStub:{cache:{},config:{},sections:{},section:{},utilities:{}}},e.extend(e.expr[":"],{data:e.expr.createPseudo?e.expr.createPseudo(function(t){return function(n){return!!e.data(n,t)}}):function(t,n,i){return!!e.data(t,i[3])}})}(jQuery,window,document),function(e,t,n,i){"use strict";t=void 0!==t&&t.Math==Math?t:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")(),e.fn.form=function(t){var i,o=e(this),a=o.selector||"",r=(new Date).getTime(),s=[],l=arguments[0],c=arguments[1],u="string"==typeof l,d=[].slice.call(arguments,1);return o.each(function(){var f,m,g,v,p,h,b,y,x,C,w,k,S,T,A,R,P,E,F,O=e(this),D=this,q=[],j=!1;F={initialize:function(){F.get.settings(),u?(void 0===E&&F.instantiate(),F.invoke(l)):(void 0!==E&&E.invoke("destroy"),F.verbose("Initializing form validation",O,y),F.bindEvents(),F.set.defaults(),F.instantiate())},instantiate:function(){F.verbose("Storing instance of module",F),E=F,O.data(R,F)},destroy:function(){F.verbose("Destroying previous module",E),F.removeEvents(),O.removeData(R)},refresh:function(){F.verbose("Refreshing selector cache"),f=O.find(w.field),m=O.find(w.group),g=O.find(w.message),v=O.find(w.prompt),p=O.find(w.submit),h=O.find(w.clear),b=O.find(w.reset)},submit:function(){F.verbose("Submitting form",O),O.submit()},attachEvents:function(t,n){n=n||"submit",e(t).on("click"+P,function(e){F[n](),e.preventDefault()})},bindEvents:function(){F.verbose("Attaching form events"),O.on("submit"+P,F.validate.form).on("blur"+P,w.field,F.event.field.blur).on("click"+P,w.submit,F.submit).on("click"+P,w.reset,F.reset).on("click"+P,w.clear,F.clear),y.keyboardShortcuts&&O.on("keydown"+P,w.field,F.event.field.keydown),f.each(function(){var t=e(this),n=t.prop("type"),i=F.get.changeEvent(n,t);e(this).on(i+P,F.event.field.change)})},clear:function(){f.each(function(){var t=e(this),n=t.parent(),i=t.closest(m),o=i.find(w.prompt),a=t.data(C.defaultValue)||"",r=n.is(w.uiCheckbox),s=n.is(w.uiDropdown);i.hasClass(k.error)&&(F.verbose("Resetting error on field",i),i.removeClass(k.error),o.remove()),s?(F.verbose("Resetting dropdown value",n,a),n.dropdown("clear")):r?t.prop("checked",!1):(F.verbose("Resetting field value",t,a),t.val(""))})},reset:function(){f.each(function(){var t=e(this),n=t.parent(),i=t.closest(m),o=i.find(w.prompt),a=t.data(C.defaultValue),r=n.is(w.uiCheckbox),s=n.is(w.uiDropdown),l=i.hasClass(k.error);void 0!==a&&(l&&(F.verbose("Resetting error on field",i),i.removeClass(k.error),o.remove()),s?(F.verbose("Resetting dropdown value",n,a),n.dropdown("restore defaults")):r?(F.verbose("Resetting checkbox value",n,a),t.prop("checked",a)):(F.verbose("Resetting field value",t,a),t.val(a)))})},determine:{isValid:function(){var t=!0;return e.each(x,function(e,n){F.validate.field(n,e,!0)||(t=!1)}),t}},is:{bracketedRule:function(e){return e.type&&e.type.match(y.regExp.bracket)},shorthandFields:function(e){var t=Object.keys(e),n=e[t[0]];return F.is.shorthandRules(n)},shorthandRules:function(t){return"string"==typeof t||e.isArray(t)},empty:function(e){return!e||0===e.length||(e.is('input[type="checkbox"]')?!e.is(":checked"):F.is.blank(e))},blank:function(t){return""===e.trim(t.val())},valid:function(t){var n=!0;return t?(F.verbose("Checking if field is valid",t),F.validate.field(x[t],t,!1)):(F.verbose("Checking if form is valid"),e.each(x,function(e,t){F.is.valid(e)||(n=!1)}),n)}},removeEvents:function(){O.off(P),f.off(P),p.off(P),f.off(P)},event:{field:{keydown:function(t){var n=e(this),i=t.which,o=n.is(w.input),a=n.is(w.checkbox),r=n.closest(w.uiDropdown).length>0,s={enter:13,escape:27};i==s.escape&&(F.verbose("Escape key pressed blurring field"),n.blur()),t.ctrlKey||i!=s.enter||!o||r||a||(j||(n.one("keyup"+P,F.event.field.keyup),F.submit(),F.debug("Enter pressed on input submitting form")),j=!0)},keyup:function(){j=!1},blur:function(t){var n=e(this),i=n.closest(m),o=F.get.validation(n);i.hasClass(k.error)?(F.debug("Revalidating field",n,o),o&&F.validate.field(o)):"blur"!=y.on&&"change"!=y.on||o&&F.validate.field(o)},change:function(t){var n=e(this),i=n.closest(m),o=F.get.validation(n);o&&("change"==y.on||i.hasClass(k.error)&&y.revalidate)&&(clearTimeout(F.timer),F.timer=setTimeout(function(){F.debug("Revalidating field",n,F.get.validation(n)),F.validate.field(o)},y.delay))}}},get:{ancillaryValue:function(e){return!(!e.type||!e.value&&!F.is.bracketedRule(e))&&(void 0!==e.value?e.value:e.type.match(y.regExp.bracket)[1]+"")},ruleName:function(e){return F.is.bracketedRule(e)?e.type.replace(e.type.match(y.regExp.bracket)[0],""):e.type},changeEvent:function(e,t){return"checkbox"==e||"radio"==e||"hidden"==e||t.is("select")?"change":F.get.inputEvent()},inputEvent:function(){return void 0!==n.createElement("input").oninput?"input":void 0!==n.createElement("input").onpropertychange?"propertychange":"keyup"},fieldsFromShorthand:function(t){var n={};return e.each(t,function(t,i){"string"==typeof i&&(i=[i]),n[t]={rules:[]},e.each(i,function(e,i){n[t].rules.push({type:i})})}),n},prompt:function(e,t){var n,i,o,a=F.get.ruleName(e),r=F.get.ancillaryValue(e),s=e.prompt||y.prompt[a]||y.text.unspecifiedRule,l=-1!==s.search("{value}"),c=-1!==s.search("{name}");return(c||l)&&(i=F.get.field(t.identifier)),l&&(s=s.replace("{value}",i.val())),c&&(n=i.closest(w.group).find("label").eq(0),o=1==n.length?n.text():i.prop("placeholder")||y.text.unspecifiedField,s=s.replace("{name}",o)),s=s.replace("{identifier}",t.identifier),s=s.replace("{ruleValue}",r),e.prompt||F.verbose("Using default validation prompt for type",s,a),s},settings:function(){if(e.isPlainObject(t)){var n=Object.keys(t),i=n.length>0&&(void 0!==t[n[0]].identifier&&void 0!==t[n[0]].rules);i?(y=e.extend(!0,{},e.fn.form.settings,c),x=e.extend({},e.fn.form.settings.defaults,t),F.error(y.error.oldSyntax,D),F.verbose("Extending settings from legacy parameters",x,y)):(t.fields&&F.is.shorthandFields(t.fields)&&(t.fields=F.get.fieldsFromShorthand(t.fields)),y=e.extend(!0,{},e.fn.form.settings,t),x=e.extend({},e.fn.form.settings.defaults,y.fields),F.verbose("Extending settings",x,y))}else y=e.fn.form.settings,x=e.fn.form.settings.defaults,F.verbose("Using default form validation",x,y);A=y.namespace,C=y.metadata,w=y.selector,k=y.className,S=y.regExp,T=y.error,R="module-"+A,P="."+A,E=O.data(R),F.refresh()},field:function(t){return F.verbose("Finding field with identifier",t),t=F.escape.string(t),f.filter("#"+t).length>0?f.filter("#"+t):f.filter('[name="'+t+'"]').length>0?f.filter('[name="'+t+'"]'):f.filter('[name="'+t+'[]"]').length>0?f.filter('[name="'+t+'[]"]'):f.filter("[data-"+C.validate+'="'+t+'"]').length>0?f.filter("[data-"+C.validate+'="'+t+'"]'):e("")},fields:function(t){var n=e();return e.each(t,function(e,t){n=n.add(F.get.field(t))}),n},validation:function(t){var n,i;return!!x&&(e.each(x,function(e,o){i=o.identifier||e,F.get.field(i)[0]==t[0]&&(o.identifier=i,n=o)}),n||!1)},value:function(e){var t,n=[];return n.push(e),t=F.get.values.call(D,n),t[e]},values:function(t){var n=e.isArray(t)?F.get.fields(t):f,i={};return n.each(function(t,n){var o=e(n),a=(o.prop("type"),o.prop("name")),r=o.val(),s=o.is(w.checkbox),l=o.is(w.radio),c=-1!==a.indexOf("[]"),u=!!s&&o.is(":checked");a&&(c?(a=a.replace("[]",""),i[a]||(i[a]=[]),s?u?i[a].push(r||!0):i[a].push(!1):i[a].push(r)):l?void 0===i[a]&&(i[a]=!!u):i[a]=s?!!u&&(r||!0):r)}),i}},has:{field:function(e){return F.verbose("Checking for existence of a field with identifier",e),e=F.escape.string(e),"string"!=typeof e&&F.error(T.identifier,e),f.filter("#"+e).length>0||(f.filter('[name="'+e+'"]').length>0||f.filter("[data-"+C.validate+'="'+e+'"]').length>0)}},escape:{string:function(e){return e=String(e),e.replace(S.escape,"\\$&")}},add:{rule:function(e,t){F.add.field(e,t)},field:function(t,n){var i={};F.is.shorthandRules(n)?(n=e.isArray(n)?n:[n],i[t]={rules:[]},e.each(n,function(e,n){i[t].rules.push({type:n})})):i[t]=n,x=e.extend({},x,i),F.debug("Adding rules",i,x)},fields:function(t){var n;n=t&&F.is.shorthandFields(t)?F.get.fieldsFromShorthand(t):t,x=e.extend({},x,n)},prompt:function(t,n){var i=F.get.field(t),o=i.closest(m),a=o.children(w.prompt),r=0!==a.length;n="string"==typeof n?[n]:n,F.verbose("Adding field error state",t),o.addClass(k.error),y.inline&&(r||(a=y.templates.prompt(n),a.appendTo(o)),a.html(n[0]),r?F.verbose("Inline errors are disabled, no inline error added",t):y.transition&&void 0!==e.fn.transition&&O.transition("is supported")?(F.verbose("Displaying error with css transition",y.transition),a.transition(y.transition+" in",y.duration)):(F.verbose("Displaying error with fallback javascript animation"),a.fadeIn(y.duration)))},errors:function(e){F.debug("Adding form error messages",e),F.set.error(),g.html(y.templates.error(e))}},remove:{rule:function(t,n){var i=e.isArray(n)?n:[n];if(void 0==n)return F.debug("Removed all rules"),void(x[t].rules=[]);void 0!=x[t]&&e.isArray(x[t].rules)&&e.each(x[t].rules,function(e,n){-1!==i.indexOf(n.type)&&(F.debug("Removed rule",n.type),x[t].rules.splice(e,1))})},field:function(t){var n=e.isArray(t)?t:[t];e.each(n,function(e,t){F.remove.rule(t)})},rules:function(t,n){e.isArray(t)?e.each(fields,function(e,t){F.remove.rule(t,n)}):F.remove.rule(t,n)},fields:function(e){F.remove.field(e)},prompt:function(t){var n=F.get.field(t),i=n.closest(m),o=i.children(w.prompt);i.removeClass(k.error),y.inline&&o.is(":visible")&&(F.verbose("Removing prompt for field",t),y.transition&&void 0!==e.fn.transition&&O.transition("is supported")?o.transition(y.transition+" out",y.duration,function(){o.remove()}):o.fadeOut(y.duration,function(){o.remove()}))}},set:{success:function(){O.removeClass(k.error).addClass(k.success)},defaults:function(){f.each(function(){var t=e(this),n=t.filter(w.checkbox).length>0,i=n?t.is(":checked"):t.val();t.data(C.defaultValue,i)})},error:function(){O.removeClass(k.success).addClass(k.error)},value:function(e,t){var n={};return n[e]=t,F.set.values.call(D,n)},values:function(t){e.isEmptyObject(t)||e.each(t,function(t,n){var i,o=F.get.field(t),a=o.parent(),r=e.isArray(n),s=a.is(w.uiCheckbox),l=a.is(w.uiDropdown),c=o.is(w.radio)&&s,u=o.length>0;u&&(r&&s?(F.verbose("Selecting multiple",n,o),a.checkbox("uncheck"),e.each(n,function(e,t){i=o.filter('[value="'+t+'"]'),a=i.parent(),i.length>0&&a.checkbox("check")})):c?(F.verbose("Selecting radio value",n,o),o.filter('[value="'+n+'"]').parent(w.uiCheckbox).checkbox("check")):s?(F.verbose("Setting checkbox value",n,a),!0===n?a.checkbox("check"):a.checkbox("uncheck")):l?(F.verbose("Setting dropdown value",n,a),a.dropdown("set selected",n)):(F.verbose("Setting field value",n,o),o.val(n)))})}},validate:{form:function(e,t){var n=F.get.values();if(j)return!1;if(q=[],F.determine.isValid()){if(F.debug("Form has no validation errors, submitting"),F.set.success(),!0!==t)return y.onSuccess.call(D,e,n)}else if(F.debug("Form has errors"),F.set.error(),y.inline||F.add.errors(q),void 0!==O.data("moduleApi")&&e.stopImmediatePropagation(),!0!==t)return y.onFailure.call(D,q,n)},field:function(t,n,i){i=void 0===i||i,"string"==typeof t&&(F.verbose("Validating field",t),n=t,t=x[t]);var o=t.identifier||n,a=F.get.field(o),r=!!t.depends&&F.get.field(t.depends),s=!0,l=[];return t.identifier||(F.debug("Using field name as identifier",o),t.identifier=o),a.prop("disabled")?(F.debug("Field is disabled. Skipping",o),s=!0):t.optional&&F.is.blank(a)?(F.debug("Field is optional and blank. Skipping",o),s=!0):t.depends&&F.is.empty(r)?(F.debug("Field depends on another value that is not present or empty. Skipping",r),s=!0):void 0!==t.rules&&e.each(t.rules,function(e,n){F.has.field(o)&&!F.validate.rule(t,n)&&(F.debug("Field is invalid",o,n.type),l.push(F.get.prompt(n,t)),s=!1)}),s?(i&&(F.remove.prompt(o,l),y.onValid.call(a)),!0):(i&&(q=q.concat(l),F.add.prompt(o,l),y.onInvalid.call(a,l)),!1)},rule:function(t,n){var i=F.get.field(t.identifier),o=(n.type,i.val()),a=F.get.ancillaryValue(n),r=F.get.ruleName(n),s=y.rules[r];return e.isFunction(s)?(o=void 0===o||""===o||null===o?"":e.trim(o+""),s.call(i,o,a)):void F.error(T.noRule,r)}},setting:function(t,n){if(e.isPlainObject(t))e.extend(!0,y,t);else{if(void 0===n)return y[t];y[t]=n}},internal:function(t,n){if(e.isPlainObject(t))e.extend(!0,F,t);else{if(void 0===n)return F[t];F[t]=n}},debug:function(){!y.silent&&y.debug&&(y.performance?F.performance.log(arguments):(F.debug=Function.prototype.bind.call(console.info,console,y.name+":"),F.debug.apply(console,arguments)))},verbose:function(){!y.silent&&y.verbose&&y.debug&&(y.performance?F.performance.log(arguments):(F.verbose=Function.prototype.bind.call(console.info,console,y.name+":"),F.verbose.apply(console,arguments)))},error:function(){y.silent||(F.error=Function.prototype.bind.call(console.error,console,y.name+":"),F.error.apply(console,arguments))},performance:{log:function(e){var t,n,i;y.performance&&(t=(new Date).getTime(),i=r||t,n=t-i,r=t,s.push({Name:e[0],Arguments:[].slice.call(e,1)||"",Element:D,"Execution Time":n})),clearTimeout(F.performance.timer),F.performance.timer=setTimeout(F.performance.display,500)},display:function(){var t=y.name+":",n=0;r=!1,clearTimeout(F.performance.timer),e.each(s,function(e,t){n+=t["Execution Time"]}),t+=" "+n+"ms",a&&(t+=" '"+a+"'"),o.length>1&&(t+=" ("+o.length+")"),(void 0!==console.group||void 0!==console.table)&&s.length>0&&(console.groupCollapsed(t),console.table?console.table(s):e.each(s,function(e,t){console.log(t.Name+": "+t["Execution Time"]+"ms")}),console.groupEnd()),s=[]}},invoke:function(t,n,o){var a,r,s,l=E;return n=n||d,o=D||o,"string"==typeof t&&void 0!==l&&(t=t.split(/[\. ]/),a=t.length-1,e.each(t,function(n,i){var o=n!=a?i+t[n+1].charAt(0).toUpperCase()+t[n+1].slice(1):t;if(e.isPlainObject(l[o])&&n!=a)l=l[o];else{if(void 0!==l[o])return r=l[o],!1;if(!e.isPlainObject(l[i])||n==a)return void 0!==l[i]&&(r=l[i],!1);l=l[i]}})),e.isFunction(r)?s=r.apply(o,n):void 0!==r&&(s=r),e.isArray(i)?i.push(s):void 0!==i?i=[i,s]:void 0!==s&&(i=s),r}},F.initialize()}),void 0!==i?i:this},e.fn.form.settings={name:"Form",namespace:"form",debug:!1,verbose:!1,performance:!0,fields:!1,keyboardShortcuts:!0,on:"submit",inline:!1,delay:200,revalidate:!0,transition:"scale",duration:200,onValid:function(){},onInvalid:function(){},onSuccess:function(){return!0},onFailure:function(){return!1},metadata:{defaultValue:"default",validate:"validate"},regExp:{htmlID:/^[a-zA-Z][\w:.-]*$/g,bracket:/\[(.*)\]/i,decimal:/^\d+\.?\d*$/,email:/^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i,escape:/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,flags:/^\/(.*)\/(.*)?/,integer:/^\-?\d+$/,number:/^\-?\d*(\.\d+)?$/,url:/(https?:\/\/(?:www\.|(?!www))[^\s\.]+\.[^\s]{2,}|www\.[^\s]+\.[^\s]{2,})/i},text:{unspecifiedRule:"Please enter a valid value",unspecifiedField:"This field"},prompt:{empty:"{name} must have a value",checked:"{name} must be checked",email:"{name} must be a valid e-mail",url:"{name} must be a valid url",regExp:"{name} is not formatted correctly",integer:"{name} must be an integer",decimal:"{name} must be a decimal number",number:"{name} must be set to a number",is:'{name} must be "{ruleValue}"',isExactly:'{name} must be exactly "{ruleValue}"',not:'{name} cannot be set to "{ruleValue}"',notExactly:'{name} cannot be set to exactly "{ruleValue}"',contain:'{name} cannot contain "{ruleValue}"',containExactly:'{name} cannot contain exactly "{ruleValue}"',doesntContain:'{name} must contain "{ruleValue}"',doesntContainExactly:'{name} must contain exactly "{ruleValue}"',minLength:"{name} must be at least {ruleValue} characters",length:"{name} must be at least {ruleValue} characters",exactLength:"{name} must be exactly {ruleValue} characters",maxLength:"{name} cannot be longer than {ruleValue} characters",match:"{name} must match {ruleValue} field",different:"{name} must have a different value than {ruleValue} field",creditCard:"{name} must be a valid credit card number",minCount:"{name} must have at least {ruleValue} choices",exactCount:"{name} must have exactly {ruleValue} choices",maxCount:"{name} must have {ruleValue} or less choices"},selector:{checkbox:'input[type="checkbox"], input[type="radio"]',clear:".clear",field:"input, textarea, select",group:".field",input:"input",message:".error.message",prompt:".prompt.label",radio:'input[type="radio"]',reset:'.reset:not([type="reset"])',submit:'.submit:not([type="submit"])',uiCheckbox:".ui.checkbox",uiDropdown:".ui.dropdown"},className:{error:"error",label:"ui prompt label",pressed:"down",success:"success"},error:{identifier:"You must specify a string identifier for each field",method:"The method you called is not defined.",noRule:"There is no rule matching the one you specified",oldSyntax:"Starting in 2.0 forms now only take a single settings object. Validation settings converted to new syntax automatically."},templates:{error:function(t){var n='
    ';return e.each(t,function(e,t){n+="
  • "+t+"
  • "}),n+="
",e(n)},prompt:function(t){return e("
").addClass("ui basic red pointing prompt label").html(t[0])}},rules:{empty:function(t){return!(void 0===t||""===t||e.isArray(t)&&0===t.length)},checked:function(){return e(this).filter(":checked").length>0},email:function(t){return e.fn.form.settings.regExp.email.test(t)},url:function(t){return e.fn.form.settings.regExp.url.test(t)},regExp:function(t,n){if(n instanceof RegExp)return t.match(n);var i,o=n.match(e.fn.form.settings.regExp.flags);return o&&(n=o.length>=2?o[1]:n,i=o.length>=3?o[2]:""),t.match(new RegExp(n,i))},integer:function(t,n){var i,o,a,r=e.fn.form.settings.regExp.integer;return n&&-1===["",".."].indexOf(n)&&(-1==n.indexOf("..")?r.test(n)&&(i=o=n-0):(a=n.split("..",2),r.test(a[0])&&(i=a[0]-0),r.test(a[1])&&(o=a[1]-0))),r.test(t)&&(void 0===i||t>=i)&&(void 0===o||t<=o)},decimal:function(t){return e.fn.form.settings.regExp.decimal.test(t)},number:function(t){return e.fn.form.settings.regExp.number.test(t)},is:function(e,t){return t="string"==typeof t?t.toLowerCase():t,(e="string"==typeof e?e.toLowerCase():e)==t},isExactly:function(e,t){return e==t},not:function(e,t){return e="string"==typeof e?e.toLowerCase():e,t="string"==typeof t?t.toLowerCase():t,e!=t},notExactly:function(e,t){return e!=t},contains:function(t,n){return n=n.replace(e.fn.form.settings.regExp.escape,"\\$&"),-1!==t.search(new RegExp(n,"i"))},containsExactly:function(t,n){return n=n.replace(e.fn.form.settings.regExp.escape,"\\$&"),-1!==t.search(new RegExp(n))},doesntContain:function(t,n){return n=n.replace(e.fn.form.settings.regExp.escape,"\\$&"),-1===t.search(new RegExp(n,"i"))},doesntContainExactly:function(t,n){return n=n.replace(e.fn.form.settings.regExp.escape,"\\$&"),-1===t.search(new RegExp(n))},minLength:function(e,t){return void 0!==e&&e.length>=t},length:function(e,t){return void 0!==e&&e.length>=t},exactLength:function(e,t){return void 0!==e&&e.length==t},maxLength:function(e,t){return void 0!==e&&e.length<=t},match:function(t,n){var i;e(this);return e('[data-validate="'+n+'"]').length>0?i=e('[data-validate="'+n+'"]').val():e("#"+n).length>0?i=e("#"+n).val():e('[name="'+n+'"]').length>0?i=e('[name="'+n+'"]').val():e('[name="'+n+'[]"]').length>0&&(i=e('[name="'+n+'[]"]')),void 0!==i&&t.toString()==i.toString()},different:function(t,n){var i;e(this);return e('[data-validate="'+n+'"]').length>0?i=e('[data-validate="'+n+'"]').val():e("#"+n).length>0?i=e("#"+n).val():e('[name="'+n+'"]').length>0?i=e('[name="'+n+'"]').val():e('[name="'+n+'[]"]').length>0&&(i=e('[name="'+n+'[]"]')),void 0!==i&&t.toString()!==i.toString()},creditCard:function(t,n){var i,o,a={visa:{pattern:/^4/,length:[16]},amex:{pattern:/^3[47]/,length:[15]},mastercard:{pattern:/^5[1-5]/,length:[16]},discover:{pattern:/^(6011|622(12[6-9]|1[3-9][0-9]|[2-8][0-9]{2}|9[0-1][0-9]|92[0-5]|64[4-9])|65)/,length:[16]},unionPay:{pattern:/^(62|88)/,length:[16,17,18,19]},jcb:{pattern:/^35(2[89]|[3-8][0-9])/,length:[16]},maestro:{pattern:/^(5018|5020|5038|6304|6759|676[1-3])/,length:[12,13,14,15,16,17,18,19]},dinersClub:{pattern:/^(30[0-5]|^36)/,length:[14]},laser:{pattern:/^(6304|670[69]|6771)/,length:[16,17,18,19]},visaElectron:{pattern:/^(4026|417500|4508|4844|491(3|7))/,length:[16]}},r={},s=!1,l="string"==typeof n&&n.split(",");if("string"==typeof t&&0!==t.length){if(t=t.replace(/[\-]/g,""),l&&(e.each(l,function(n,i){(o=a[i])&&(r={length:-1!==e.inArray(t.length,o.length),pattern:-1!==t.search(o.pattern)},r.length&&r.pattern&&(s=!0))}),!s))return!1;if(i={number:-1!==e.inArray(t.length,a.unionPay.length),pattern:-1!==t.search(a.unionPay.pattern)},i.number&&i.pattern)return!0;for(var c=t.length,u=0,d=[[0,1,2,3,4,5,6,7,8,9],[0,2,4,6,8,1,3,5,7,9]],f=0;c--;)f+=d[u][parseInt(t.charAt(c),10)],u^=1;return f%10==0&&f>0}},minCount:function(e,t){return 0==t||(1==t?""!==e:e.split(",").length>=t)},exactCount:function(e,t){return 0==t?""===e:1==t?""!==e&&-1===e.search(","):e.split(",").length==t},maxCount:function(e,t){return 0!=t&&(1==t?-1===e.search(","):e.split(",").length<=t)}}}}(jQuery,window,document),function(e,t,n,i){"use strict";t=void 0!==t&&t.Math==Math?t:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")(),e.fn.accordion=function(n){var i,o=e(this),a=(new Date).getTime(),r=[],s=arguments[0],l="string"==typeof s,c=[].slice.call(arguments,1);t.requestAnimationFrame||t.mozRequestAnimationFrame||t.webkitRequestAnimationFrame||t.msRequestAnimationFrame;return o.each(function(){var u,d,f=e.isPlainObject(n)?e.extend(!0,{},e.fn.accordion.settings,n):e.extend({},e.fn.accordion.settings),m=f.className,g=f.namespace,v=f.selector,p=f.error,h="."+g,b="module-"+g,y=o.selector||"",x=e(this),C=x.find(v.title),w=x.find(v.content),k=this,S=x.data(b);d={initialize:function(){d.debug("Initializing",x),d.bind.events(),f.observeChanges&&d.observeChanges(),d.instantiate()},instantiate:function(){S=d,x.data(b,d)},destroy:function(){d.debug("Destroying previous instance",x),x.off(h).removeData(b)},refresh:function(){C=x.find(v.title),w=x.find(v.content)},observeChanges:function(){"MutationObserver"in t&&(u=new MutationObserver(function(e){d.debug("DOM tree modified, updating selector cache"),d.refresh()}),u.observe(k,{childList:!0,subtree:!0}),d.debug("Setting up mutation observer",u))},bind:{events:function(){d.debug("Binding delegated events"),x.on(f.on+h,v.trigger,d.event.click)}},event:{click:function(){d.toggle.call(this)}},toggle:function(t){var n=void 0!==t?"number"==typeof t?C.eq(t):e(t).closest(v.title):e(this).closest(v.title),i=n.next(w),o=i.hasClass(m.animating),a=i.hasClass(m.active),r=a&&!o,s=!a&&o;d.debug("Toggling visibility of content",n),r||s?f.collapsible?d.close.call(n):d.debug("Cannot close accordion content collapsing is disabled"):d.open.call(n)},open:function(t){var n=void 0!==t?"number"==typeof t?C.eq(t):e(t).closest(v.title):e(this).closest(v.title),i=n.next(w),o=i.hasClass(m.animating);if(i.hasClass(m.active)||o)return void d.debug("Accordion already open, skipping",i);d.debug("Opening accordion content",n),f.onOpening.call(i),f.exclusive&&d.closeOthers.call(n),n.addClass(m.active),i.stop(!0,!0).addClass(m.animating),f.animateChildren&&(void 0!==e.fn.transition&&x.transition("is supported")?i.children().transition({animation:"fade in",queue:!1,useFailSafe:!0,debug:f.debug,verbose:f.verbose,duration:f.duration}):i.children().stop(!0,!0).animate({opacity:1},f.duration,d.resetOpacity)),i.slideDown(f.duration,f.easing,function(){i.removeClass(m.animating).addClass(m.active),d.reset.display.call(this),f.onOpen.call(this),f.onChange.call(this)})},close:function(t){var n=void 0!==t?"number"==typeof t?C.eq(t):e(t).closest(v.title):e(this).closest(v.title),i=n.next(w),o=i.hasClass(m.animating),a=i.hasClass(m.active),r=!a&&o,s=a&&o;!a&&!r||s||(d.debug("Closing accordion content",i),f.onClosing.call(i),n.removeClass(m.active),i.stop(!0,!0).addClass(m.animating),f.animateChildren&&(void 0!==e.fn.transition&&x.transition("is supported")?i.children().transition({animation:"fade out",queue:!1,useFailSafe:!0,debug:f.debug,verbose:f.verbose,duration:f.duration}):i.children().stop(!0,!0).animate({opacity:0},f.duration,d.resetOpacity)),i.slideUp(f.duration,f.easing,function(){i.removeClass(m.animating).removeClass(m.active),d.reset.display.call(this),f.onClose.call(this),f.onChange.call(this)}))},closeOthers:function(t){var n,i,o,a=void 0!==t?C.eq(t):e(this).closest(v.title),r=a.parents(v.content).prev(v.title),s=a.closest(v.accordion),l=v.title+"."+m.active+":visible",c=v.content+"."+m.active+":visible";f.closeNested?(n=s.find(l).not(r),o=n.next(w)):(n=s.find(l).not(r),i=s.find(c).find(l).not(r),n=n.not(i),o=n.next(w)),n.length>0&&(d.debug("Exclusive enabled, closing other content",n),n.removeClass(m.active),o.removeClass(m.animating).stop(!0,!0),f.animateChildren&&(void 0!==e.fn.transition&&x.transition("is supported")?o.children().transition({animation:"fade out",useFailSafe:!0,debug:f.debug,verbose:f.verbose,duration:f.duration}):o.children().stop(!0,!0).animate({opacity:0},f.duration,d.resetOpacity)),o.slideUp(f.duration,f.easing,function(){e(this).removeClass(m.active),d.reset.display.call(this)}))},reset:{display:function(){d.verbose("Removing inline display from element",this),e(this).css("display",""),""===e(this).attr("style")&&e(this).attr("style","").removeAttr("style")},opacity:function(){d.verbose("Removing inline opacity from element",this),e(this).css("opacity",""),""===e(this).attr("style")&&e(this).attr("style","").removeAttr("style")}},setting:function(t,n){if(d.debug("Changing setting",t,n),e.isPlainObject(t))e.extend(!0,f,t);else{if(void 0===n)return f[t];e.isPlainObject(f[t])?e.extend(!0,f[t],n):f[t]=n}},internal:function(t,n){if(d.debug("Changing internal",t,n),void 0===n)return d[t];e.isPlainObject(t)?e.extend(!0,d,t):d[t]=n},debug:function(){!f.silent&&f.debug&&(f.performance?d.performance.log(arguments):(d.debug=Function.prototype.bind.call(console.info,console,f.name+":"),d.debug.apply(console,arguments)))},verbose:function(){ +!f.silent&&f.verbose&&f.debug&&(f.performance?d.performance.log(arguments):(d.verbose=Function.prototype.bind.call(console.info,console,f.name+":"),d.verbose.apply(console,arguments)))},error:function(){f.silent||(d.error=Function.prototype.bind.call(console.error,console,f.name+":"),d.error.apply(console,arguments))},performance:{log:function(e){var t,n,i;f.performance&&(t=(new Date).getTime(),i=a||t,n=t-i,a=t,r.push({Name:e[0],Arguments:[].slice.call(e,1)||"",Element:k,"Execution Time":n})),clearTimeout(d.performance.timer),d.performance.timer=setTimeout(d.performance.display,500)},display:function(){var t=f.name+":",n=0;a=!1,clearTimeout(d.performance.timer),e.each(r,function(e,t){n+=t["Execution Time"]}),t+=" "+n+"ms",y&&(t+=" '"+y+"'"),(void 0!==console.group||void 0!==console.table)&&r.length>0&&(console.groupCollapsed(t),console.table?console.table(r):e.each(r,function(e,t){console.log(t.Name+": "+t["Execution Time"]+"ms")}),console.groupEnd()),r=[]}},invoke:function(t,n,o){var a,r,s,l=S;return n=n||c,o=k||o,"string"==typeof t&&void 0!==l&&(t=t.split(/[\. ]/),a=t.length-1,e.each(t,function(n,i){var o=n!=a?i+t[n+1].charAt(0).toUpperCase()+t[n+1].slice(1):t;if(e.isPlainObject(l[o])&&n!=a)l=l[o];else{if(void 0!==l[o])return r=l[o],!1;if(!e.isPlainObject(l[i])||n==a)return void 0!==l[i]?(r=l[i],!1):(d.error(p.method,t),!1);l=l[i]}})),e.isFunction(r)?s=r.apply(o,n):void 0!==r&&(s=r),e.isArray(i)?i.push(s):void 0!==i?i=[i,s]:void 0!==s&&(i=s),r}},l?(void 0===S&&d.initialize(),d.invoke(s)):(void 0!==S&&S.invoke("destroy"),d.initialize())}),void 0!==i?i:this},e.fn.accordion.settings={name:"Accordion",namespace:"accordion",silent:!1,debug:!1,verbose:!1,performance:!0,on:"click",observeChanges:!0,exclusive:!0,collapsible:!0,closeNested:!1,animateChildren:!0,duration:350,easing:"easeOutQuad",onOpening:function(){},onOpen:function(){},onClosing:function(){},onClose:function(){},onChange:function(){},error:{method:"The method you called is not defined"},className:{active:"active",animating:"animating"},selector:{accordion:".accordion",title:".title",trigger:".title",content:".content"}},e.extend(e.easing,{easeOutQuad:function(e,t,n,i,o){return-i*(t/=o)*(t-2)+n}})}(jQuery,window,document),function(e,t,n,i){"use strict";t=void 0!==t&&t.Math==Math?t:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")(),e.fn.checkbox=function(i){var o,a=e(this),r=a.selector||"",s=(new Date).getTime(),l=[],c=arguments[0],u="string"==typeof c,d=[].slice.call(arguments,1);return a.each(function(){var a,f,m=e.extend(!0,{},e.fn.checkbox.settings,i),g=m.className,v=m.namespace,p=m.selector,h=m.error,b="."+v,y="module-"+v,x=e(this),C=e(this).children(p.label),w=e(this).children(p.input),k=w[0],S=!1,T=!1,A=x.data(y),R=this;f={initialize:function(){f.verbose("Initializing checkbox",m),f.create.label(),f.bind.events(),f.set.tabbable(),f.hide.input(),f.observeChanges(),f.instantiate(),f.setup()},instantiate:function(){f.verbose("Storing instance of module",f),A=f,x.data(y,f)},destroy:function(){f.verbose("Destroying module"),f.unbind.events(),f.show.input(),x.removeData(y)},fix:{reference:function(){x.is(p.input)&&(f.debug("Behavior called on adjusting invoked element"),x=x.closest(p.checkbox),f.refresh())}},setup:function(){f.set.initialLoad(),f.is.indeterminate()?(f.debug("Initial value is indeterminate"),f.indeterminate()):f.is.checked()?(f.debug("Initial value is checked"),f.check()):(f.debug("Initial value is unchecked"),f.uncheck()),f.remove.initialLoad()},refresh:function(){C=x.children(p.label),w=x.children(p.input),k=w[0]},hide:{input:function(){f.verbose("Modifying z-index to be unselectable"),w.addClass(g.hidden)}},show:{input:function(){f.verbose("Modifying z-index to be selectable"),w.removeClass(g.hidden)}},observeChanges:function(){"MutationObserver"in t&&(a=new MutationObserver(function(e){f.debug("DOM tree modified, updating selector cache"),f.refresh()}),a.observe(R,{childList:!0,subtree:!0}),f.debug("Setting up mutation observer",a))},attachEvents:function(t,n){var i=e(t);n=e.isFunction(f[n])?f[n]:f.toggle,i.length>0?(f.debug("Attaching checkbox events to element",t,n),i.on("click"+b,n)):f.error(h.notFound)},event:{click:function(t){var n=e(t.target);return n.is(p.input)?void f.verbose("Using default check action on initialized checkbox"):n.is(p.link)?void f.debug("Clicking link inside checkbox, skipping toggle"):(f.toggle(),w.focus(),void t.preventDefault())},keydown:function(e){var t=e.which,n={enter:13,space:32,escape:27};t==n.escape?(f.verbose("Escape key pressed blurring field"),w.blur(),T=!0):e.ctrlKey||t!=n.space&&t!=n.enter?T=!1:(f.verbose("Enter/space key pressed, toggling checkbox"),f.toggle(),T=!0)},keyup:function(e){T&&e.preventDefault()}},check:function(){f.should.allowCheck()&&(f.debug("Checking checkbox",w),f.set.checked(),f.should.ignoreCallbacks()||(m.onChecked.call(k),m.onChange.call(k)))},uncheck:function(){f.should.allowUncheck()&&(f.debug("Unchecking checkbox"),f.set.unchecked(),f.should.ignoreCallbacks()||(m.onUnchecked.call(k),m.onChange.call(k)))},indeterminate:function(){if(f.should.allowIndeterminate())return void f.debug("Checkbox is already indeterminate");f.debug("Making checkbox indeterminate"),f.set.indeterminate(),f.should.ignoreCallbacks()||(m.onIndeterminate.call(k),m.onChange.call(k))},determinate:function(){if(f.should.allowDeterminate())return void f.debug("Checkbox is already determinate");f.debug("Making checkbox determinate"),f.set.determinate(),f.should.ignoreCallbacks()||(m.onDeterminate.call(k),m.onChange.call(k))},enable:function(){if(f.is.enabled())return void f.debug("Checkbox is already enabled");f.debug("Enabling checkbox"),f.set.enabled(),m.onEnable.call(k),m.onEnabled.call(k)},disable:function(){if(f.is.disabled())return void f.debug("Checkbox is already disabled");f.debug("Disabling checkbox"),f.set.disabled(),m.onDisable.call(k),m.onDisabled.call(k)},get:{radios:function(){var t=f.get.name();return e('input[name="'+t+'"]').closest(p.checkbox)},otherRadios:function(){return f.get.radios().not(x)},name:function(){return w.attr("name")}},is:{initialLoad:function(){return S},radio:function(){return w.hasClass(g.radio)||"radio"==w.attr("type")},indeterminate:function(){return void 0!==w.prop("indeterminate")&&w.prop("indeterminate")},checked:function(){return void 0!==w.prop("checked")&&w.prop("checked")},disabled:function(){return void 0!==w.prop("disabled")&&w.prop("disabled")},enabled:function(){return!f.is.disabled()},determinate:function(){return!f.is.indeterminate()},unchecked:function(){return!f.is.checked()}},should:{allowCheck:function(){return f.is.determinate()&&f.is.checked()&&!f.should.forceCallbacks()?(f.debug("Should not allow check, checkbox is already checked"),!1):!1!==m.beforeChecked.apply(k)||(f.debug("Should not allow check, beforeChecked cancelled"),!1)},allowUncheck:function(){return f.is.determinate()&&f.is.unchecked()&&!f.should.forceCallbacks()?(f.debug("Should not allow uncheck, checkbox is already unchecked"),!1):!1!==m.beforeUnchecked.apply(k)||(f.debug("Should not allow uncheck, beforeUnchecked cancelled"),!1)},allowIndeterminate:function(){return f.is.indeterminate()&&!f.should.forceCallbacks()?(f.debug("Should not allow indeterminate, checkbox is already indeterminate"),!1):!1!==m.beforeIndeterminate.apply(k)||(f.debug("Should not allow indeterminate, beforeIndeterminate cancelled"),!1)},allowDeterminate:function(){return f.is.determinate()&&!f.should.forceCallbacks()?(f.debug("Should not allow determinate, checkbox is already determinate"),!1):!1!==m.beforeDeterminate.apply(k)||(f.debug("Should not allow determinate, beforeDeterminate cancelled"),!1)},forceCallbacks:function(){return f.is.initialLoad()&&m.fireOnInit},ignoreCallbacks:function(){return S&&!m.fireOnInit}},can:{change:function(){return!(x.hasClass(g.disabled)||x.hasClass(g.readOnly)||w.prop("disabled")||w.prop("readonly"))},uncheck:function(){return"boolean"==typeof m.uncheckable?m.uncheckable:!f.is.radio()}},set:{initialLoad:function(){S=!0},checked:function(){if(f.verbose("Setting class to checked"),x.removeClass(g.indeterminate).addClass(g.checked),f.is.radio()&&f.uncheckOthers(),!f.is.indeterminate()&&f.is.checked())return void f.debug("Input is already checked, skipping input property change");f.verbose("Setting state to checked",k),w.prop("indeterminate",!1).prop("checked",!0),f.trigger.change()},unchecked:function(){if(f.verbose("Removing checked class"),x.removeClass(g.indeterminate).removeClass(g.checked),!f.is.indeterminate()&&f.is.unchecked())return void f.debug("Input is already unchecked");f.debug("Setting state to unchecked"),w.prop("indeterminate",!1).prop("checked",!1),f.trigger.change()},indeterminate:function(){if(f.verbose("Setting class to indeterminate"),x.addClass(g.indeterminate),f.is.indeterminate())return void f.debug("Input is already indeterminate, skipping input property change");f.debug("Setting state to indeterminate"),w.prop("indeterminate",!0),f.trigger.change()},determinate:function(){if(f.verbose("Removing indeterminate class"),x.removeClass(g.indeterminate),f.is.determinate())return void f.debug("Input is already determinate, skipping input property change");f.debug("Setting state to determinate"),w.prop("indeterminate",!1)},disabled:function(){if(f.verbose("Setting class to disabled"),x.addClass(g.disabled),f.is.disabled())return void f.debug("Input is already disabled, skipping input property change");f.debug("Setting state to disabled"),w.prop("disabled","disabled"),f.trigger.change()},enabled:function(){if(f.verbose("Removing disabled class"),x.removeClass(g.disabled),f.is.enabled())return void f.debug("Input is already enabled, skipping input property change");f.debug("Setting state to enabled"),w.prop("disabled",!1),f.trigger.change()},tabbable:function(){f.verbose("Adding tabindex to checkbox"),void 0===w.attr("tabindex")&&w.attr("tabindex",0)}},remove:{initialLoad:function(){S=!1}},trigger:{change:function(){var e=n.createEvent("HTMLEvents"),t=w[0];t&&(f.verbose("Triggering native change event"),e.initEvent("change",!0,!1),t.dispatchEvent(e))}},create:{label:function(){w.prevAll(p.label).length>0?(w.prev(p.label).detach().insertAfter(w),f.debug("Moving existing label",C)):f.has.label()||(C=e("