diff --git a/.dockerignore b/.dockerignore
index 7522f1ba..ac82f884 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -5,3 +5,4 @@ static
 test
 scripts
 docs
+firefox
diff --git a/.eslintignore b/.eslintignore
index a5892099..a435bfcc 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,4 +1,3 @@
-public/bundle.js
-public/webcrypto-shim.js
+public
 test/frontend/bundle.js
-firefox
\ No newline at end of file
+firefox
diff --git a/.gitignore b/.gitignore
index e1f30354..dc0910df 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,9 @@
 .DS_Store
 node_modules
-public/bundle.js
+public/upload.js
+public/download.js
 public/version.json
+public/l20n.min.js
 static/*
 !static/info.txt
 test/frontend/bundle.js
diff --git a/.stylelintrc b/.stylelintrc
index 3c593e83..c0c673c7 100644
--- a/.stylelintrc
+++ b/.stylelintrc
@@ -1,6 +1,6 @@
 extends: stylelint-config-standard
 
 rules:
-  color-hex-case: upper
+  color-hex-case: lower
   declaration-colon-newline-after: null
   selector-list-comma-newline-after: null
diff --git a/circle.yml b/circle.yml
index ed714bbd..e25fa992 100644
--- a/circle.yml
+++ b/circle.yml
@@ -16,7 +16,7 @@ deployment:
   latest:
     branch: master
     commands:
-    - npm run predocker
+    - npm run build
     - docker login -e $DOCKER_EMAIL -u $DOCKER_USER -p $DOCKER_PASS
     - docker build -t mozilla/send:latest .
     - docker push mozilla/send:latest
@@ -24,7 +24,7 @@ deployment:
     tag: /.*/
     owner: mozilla
     commands:
-    - npm run predocker
+    - npm run build
     - docker login -e $DOCKER_EMAIL -u $DOCKER_USER -p $DOCKER_PASS
     - docker build -t mozilla/send:$CIRCLE_TAG .
     - docker push mozilla/send:$CIRCLE_TAG
diff --git a/docker-compose.yml b/docker-compose.yml
index 8274bde1..f72bf161 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -7,6 +7,6 @@ services:
     ports:
       - "1443:1443"
     environment:
-      - P2P_REDIS_HOST=redis
+      - REDIS_HOST=redis
   redis:
     image: redis:alpine
diff --git a/docs/docker.md b/docs/docker.md
index f45b770f..f94b23b3 100644
--- a/docs/docker.md
+++ b/docs/docker.md
@@ -3,12 +3,22 @@
 | Name             | Description
 |------------------|-------------|
 | `PORT`           | Port the server will listen on (defaults to 1443).
-| `P2P_S3_BUCKET`  | The S3 bucket name.
-| `P2P_REDIS_HOST` | Host name of the Redis server.
+| `S3_BUCKET`  | The S3 bucket name.
+| `REDIS_HOST` | Host name of the Redis server.
+| `GOOGLE_ANALYTICS_ID` | Google Analytics ID
+| `SENTRY_CLIENT` | Sentry Client ID
+| `SENTRY_DSN` | Sentry DSN
+| `MAX_FILE_SIZE` | in bytes (defaults to 2147483648)
 | `NODE_ENV`       | "production"
 
 ## Example:
 
 ```sh
-$ docker run --net=host -e 'NODE_ENV=production' -e 'P2P_S3_BUCKET=testpilot-p2p-dev' -e 'P2P_REDIS_HOST=dyf9s2r4vo3.bolxr4.0001.usw2.cache.amazonaws.com' mozilla/send:latest
+$ docker run --net=host -e 'NODE_ENV=production' \
+  -e 'S3_BUCKET=testpilot-p2p-dev' \
+  -e 'REDIS_HOST=dyf9s2r4vo3.bolxr4.0001.usw2.cache.amazonaws.com' \
+  -e 'GOOGLE_ANALYTICS_ID=UA-35433268-78' \
+  -e 'SENTRY_CLIENT=https://51e23d7263e348a7a3b90a5357c61cb2@sentry.prod.mozaws.net/168' \
+  -e 'SENTRY_DSN=https://51e23d7263e348a7a3b90a5357c61cb2:65e23d7263e348a7a3b90a5357c61c44@sentry.prod.mozaws.net/168' \
+  mozilla/send:latest
 ```
diff --git a/docs/metrics.md b/docs/metrics.md
index 035a57fa..54d27deb 100644
--- a/docs/metrics.md
+++ b/docs/metrics.md
@@ -112,6 +112,7 @@ Fired whenever a user deletes a file they’ve uploaded.
 - `cm6`
 - `cm7`
 - `cd1`
+- `cd4`
 
 #### `copied`
 Fired whenever a user copies the URL of an upload file.
diff --git a/frontend/src/common.js b/frontend/src/common.js
new file mode 100644
index 00000000..5fa712a3
--- /dev/null
+++ b/frontend/src/common.js
@@ -0,0 +1,10 @@
+window.Raven = require('raven-js');
+window.Raven.config(window.dsn).install();
+window.dsn = undefined;
+
+const testPilotGA = require('testpilot-ga');
+window.analytics = new testPilotGA({
+  an: 'Firefox Send',
+  ds: 'web',
+  tid: window.trackerId
+});
diff --git a/frontend/src/download.js b/frontend/src/download.js
index a6d96449..b0e05fe9 100644
--- a/frontend/src/download.js
+++ b/frontend/src/download.js
@@ -1,90 +1,175 @@
+require('./common');
 const FileReceiver = require('./fileReceiver');
-const { notify } = require('./utils');
+const { notify, findMetric, gcmCompliant, sendEvent } = require('./utils');
+const bytes = require('bytes');
+const Storage = require('./storage');
+const storage = new Storage(localStorage);
+
 const $ = require('jquery');
 require('jquery-circle-progress');
 
 const Raven = window.Raven;
+
 $(document).ready(function() {
+  gcmCompliant().catch(err => {
+    $('#download').attr('hidden', true);
+    sendEvent('recipient', 'unsupported', {
+      cd6: err
+    }).then(() => {
+      location.replace('/unsupported');
+    });
+  });
   //link back to homepage
   $('.send-new').attr('href', window.location.origin);
 
-  const filename = $('#dl-filename').html();
+  $('.send-new').click(function(target) {
+    target.preventDefault();
+    sendEvent('recipient', 'restarted', {
+      cd2: 'completed'
+    }).then(() => {
+      location.href = target.currentTarget.href;
+    });
+  });
+
+  $('.legal-links a, .social-links a, #dl-firefox').click(function(target) {
+    target.preventDefault();
+    const metric = findMetric(target.currentTarget.href);
+    // record exited event by recipient
+    sendEvent('recipient', 'exited', {
+      cd3: metric
+    }).then(() => {
+      location.href = target.currentTarget.href;
+    });
+  });
+
+  const filename = $('#dl-filename').text();
+  const bytelength = Number($('#dl-bytelength').text());
+  const timeToExpiry = Number($('#dl-ttl').text());
 
   //initiate progress bar
   $('#dl-progress').circleProgress({
     value: 0.0,
     startAngle: -Math.PI / 2,
-    fill: '#00C8D7',
+    fill: '#3B9DFF',
     size: 158,
     animation: { duration: 300 }
   });
   $('#download-btn').click(download);
   function download() {
+    storage.totalDownloads += 1;
+
     const fileReceiver = new FileReceiver();
+    const unexpiredFiles = storage.numFiles;
 
     fileReceiver.on('progress', progress => {
+      window.onunload = function() {
+        storage.referrer = 'cancelled-download';
+        // record download-stopped (cancelled by tab close or reload)
+        sendEvent('recipient', 'download-stopped', {
+          cm1: bytelength,
+          cm5: storage.totalUploads,
+          cm6: unexpiredFiles,
+          cm7: storage.totalDownloads,
+          cd2: 'cancelled'
+        });
+      };
+
       $('#download-page-one').attr('hidden', true);
       $('#download-progress').removeAttr('hidden');
       const percent = progress[0] / progress[1];
       // update progress bar
       $('#dl-progress').circleProgress('value', percent);
-      $('.percent-number').html(`${Math.floor(percent * 100)}`);
-      if (progress[1] < 1000000) {
-        $('.progress-text').html(
-          `${filename} (${(progress[0] / 1000).toFixed(1)}KB of
-           ${(progress[1] / 1000).toFixed(1)}KB)`
-        );
-      } else if (progress[1] < 1000000000) {
-        $('.progress-text').html(
-          `${filename} (${(progress[0] / 1000000).toFixed(1)}MB of ${(progress[1] / 1000000).toFixed(1)}MB)`
-        );
-      } else {
-        $('.progress-text').html(
-          `${filename} (${(progress[0] / 1000000).toFixed(1)}MB of ${(progress[1] / 1000000000).toFixed(1)}GB)`
-        );
-      }
-      //on complete
-      if (percent === 1) {
-        fileReceiver.removeAllListeners('progress');
-        document.l10n.formatValues('downloadNotification', 'downloadFinish')
-                     .then(translated => {
-                       notify(translated[0]);
-                       $('.title').html(translated[1]);
-                     });
-      }
+      $('.percent-number').text(`${Math.floor(percent * 100)}`);
+      $('.progress-text').text(
+        `${filename} (${bytes(progress[0], {
+          decimalPlaces: 1,
+          fixedDecimals: true
+        })} of ${bytes(progress[1], { decimalPlaces: 1 })})`
+      );
     });
 
+    let downloadEnd;
     fileReceiver.on('decrypting', isStillDecrypting => {
       // The file is being decrypted
       if (isStillDecrypting) {
-        console.log('Decrypting');
+        fileReceiver.removeAllListeners('progress');
+        window.onunload = null;
+        document.l10n.formatValue('decryptingFile').then(decryptingFile => {
+          $('.progress-text').text(decryptingFile);
+        });
       } else {
         console.log('Done decrypting');
+        downloadEnd = Date.now();
       }
     });
 
     fileReceiver.on('hashing', isStillHashing => {
       // The file is being hashed to make sure a malicious user hasn't tampered with it
       if (isStillHashing) {
-        console.log('Checking file integrity');
+        document.l10n.formatValue('verifyingFile').then(verifyingFile => {
+          $('.progress-text').text(verifyingFile);
+        });
       } else {
-        console.log('Integrity check done');
+        $('.progress-text').text(' ');
+        document.l10n
+          .formatValues('downloadNotification', 'downloadFinish')
+          .then(translated => {
+            notify(translated[0]);
+            $('.title').text(translated[1]);
+          });
       }
     });
 
+    const startTime = Date.now();
+
+    // record download-started by recipient
+    sendEvent('recipient', 'download-started', {
+      cm1: bytelength,
+      cm4: timeToExpiry,
+      cm5: storage.totalUploads,
+      cm6: unexpiredFiles,
+      cm7: storage.totalDownloads
+    });
+
     fileReceiver
       .download()
-      .catch(() => {
-        document.l10n.formatValue('expiredPageHeader')
-                     .then(translated => {
-                       $('.title').text(translated);
-                     });
+      .catch(err => {
+        // record download-stopped (errored) by recipient
+        sendEvent('recipient', 'download-stopped', {
+          cm1: bytelength,
+          cm5: storage.totalUploads,
+          cm6: unexpiredFiles,
+          cm7: storage.totalDownloads,
+          cd2: 'errored',
+          cd6: err
+        });
+
+        document.l10n.formatValue('expiredPageHeader').then(translated => {
+          $('.title').text(translated);
+        });
         $('#download-btn').attr('hidden', true);
         $('#expired-img').removeAttr('hidden');
         console.log('The file has expired, or has already been deleted.');
         return;
       })
       .then(([decrypted, fname]) => {
+        const endTime = Date.now();
+        const totalTime = endTime - startTime;
+        const downloadTime = endTime - downloadEnd;
+        const downloadSpeed = bytelength / (downloadTime / 1000);
+
+        storage.referrer = 'completed-download';
+        // record download-stopped (completed) by recipient
+        sendEvent('recipient', 'download-stopped', {
+          cm1: bytelength,
+          cm2: totalTime,
+          cm3: downloadSpeed,
+          cm5: storage.totalUploads,
+          cm6: unexpiredFiles,
+          cm7: storage.totalDownloads,
+          cd2: 'completed'
+        });
+
         const dataView = new DataView(decrypted);
         const blob = new Blob([dataView]);
         const downloadUrl = URL.createObjectURL(blob);
diff --git a/frontend/src/fileReceiver.js b/frontend/src/fileReceiver.js
index 155a8dd9..882dcb2b 100644
--- a/frontend/src/fileReceiver.js
+++ b/frontend/src/fileReceiver.js
@@ -58,41 +58,47 @@ class FileReceiver extends EventEmitter {
         true,
         ['encrypt', 'decrypt']
       )
-    ]).then(([fdata, key]) => {
-      this.emit('decrypting', true);
-      return Promise.all([
-        window.crypto.subtle.decrypt(
-          {
-            name: 'AES-GCM',
-            iv: hexToArray(fdata.iv),
-            additionalData: hexToArray(fdata.aad)
-          },
-          key,
-          fdata.data
-        ).then(decrypted => {
-          this.emit('decrypting', false);
-          return Promise.resolve(decrypted)
-        }),
-        fdata.filename,
-        hexToArray(fdata.aad)
-      ]);
-    }).then(([decrypted, fname, proposedHash]) => {
-      this.emit('hashing', true);
-      return window.crypto.subtle.digest('SHA-256', decrypted).then(calculatedHash => {
-        this.emit('hashing', false);
-        const integrity = new Uint8Array(calculatedHash).toString() === proposedHash.toString();
-        if (!integrity) {
-          this.emit('unsafe', true)
-          return Promise.reject();
-        } else {
-          this.emit('safe', true);
-          return Promise.all([
-            decrypted,
-            decodeURIComponent(fname)
-          ]);
-        }
+    ])
+      .then(([fdata, key]) => {
+        this.emit('decrypting', true);
+        return Promise.all([
+          window.crypto.subtle
+            .decrypt(
+              {
+                name: 'AES-GCM',
+                iv: hexToArray(fdata.iv),
+                additionalData: hexToArray(fdata.aad),
+                tagLength: 128
+              },
+              key,
+              fdata.data
+            )
+            .then(decrypted => {
+              this.emit('decrypting', false);
+              return Promise.resolve(decrypted);
+            }),
+          fdata.filename,
+          hexToArray(fdata.aad)
+        ]);
       })
-    })
+      .then(([decrypted, fname, proposedHash]) => {
+        this.emit('hashing', true);
+        return window.crypto.subtle
+          .digest('SHA-256', decrypted)
+          .then(calculatedHash => {
+            this.emit('hashing', false);
+            const integrity =
+              new Uint8Array(calculatedHash).toString() ===
+              proposedHash.toString();
+            if (!integrity) {
+              this.emit('unsafe', true);
+              return Promise.reject();
+            } else {
+              this.emit('safe', true);
+              return Promise.all([decrypted, decodeURIComponent(fname)]);
+            }
+          });
+      });
   }
 }
 
diff --git a/frontend/src/fileSender.js b/frontend/src/fileSender.js
index a16b1849..97c76deb 100644
--- a/frontend/src/fileSender.js
+++ b/frontend/src/fileSender.js
@@ -118,14 +118,16 @@ class FileSender extends EventEmitter {
 
           xhr.onreadystatechange = () => {
             if (xhr.readyState === XMLHttpRequest.DONE) {
-              // uuid field and url field
-              const responseObj = JSON.parse(xhr.responseText);
-              resolve({
-                url: responseObj.url,
-                fileId: responseObj.id,
-                secretKey: keydata.k,
-                deleteToken: responseObj.delete
-              });
+              if (xhr.status === 200) {
+                const responseObj = JSON.parse(xhr.responseText);
+                return resolve({
+                  url: responseObj.url,
+                  fileId: responseObj.id,
+                  secretKey: keydata.k,
+                  deleteToken: responseObj.delete
+                });
+              }
+              reject(xhr.status);
             }
           };
 
diff --git a/frontend/src/main.js b/frontend/src/main.js
deleted file mode 100644
index 12c05a38..00000000
--- a/frontend/src/main.js
+++ /dev/null
@@ -1,5 +0,0 @@
-window.Raven = require('raven-js');
-window.Raven.config(window.dsn).install();
-window.dsn = undefined;
-require('./upload');
-require('./download');
diff --git a/frontend/src/storage.js b/frontend/src/storage.js
new file mode 100644
index 00000000..c95d93a6
--- /dev/null
+++ b/frontend/src/storage.js
@@ -0,0 +1,66 @@
+const { isFile } = require('./utils');
+
+class Storage {
+  constructor(engine) {
+    this.engine = engine;
+  }
+
+  get totalDownloads() {
+    return Number(this.engine.getItem('totalDownloads'));
+  }
+  set totalDownloads(n) {
+    this.engine.setItem('totalDownloads', n);
+  }
+  get totalUploads() {
+    return Number(this.engine.getItem('totalUploads'));
+  }
+  set totalUploads(n) {
+    this.engine.setItem('totalUploads', n);
+  }
+  get referrer() {
+    return this.engine.getItem('referrer');
+  }
+  set referrer(str) {
+    this.engine.setItem('referrer', str);
+  }
+
+  get files() {
+    const fs = [];
+    for (let i = 0; i < this.engine.length; i++) {
+      const k = this.engine.key(i);
+      if (isFile(k)) {
+        fs.push(JSON.parse(this.engine.getItem(k))); // parse or whatever else
+      }
+    }
+    return fs;
+  }
+
+  get numFiles() {
+    let length = 0;
+    for (let i = 0; i < this.engine.length; i++) {
+      const k = this.engine.key(i);
+      if (isFile(k)) {
+        length += 1;
+      }
+    }
+    return length;
+  }
+
+  getFileById(id) {
+    return this.engine.getItem(id);
+  }
+
+  has(property) {
+    return this.engine.hasOwnProperty(property);
+  }
+
+  remove(property) {
+    this.engine.removeItem(property);
+  }
+
+  addFile(id, file) {
+    this.engine.setItem(id, JSON.stringify(file));
+  }
+}
+
+module.exports = Storage;
diff --git a/frontend/src/upload.js b/frontend/src/upload.js
index 82f6c55b..84279760 100644
--- a/frontend/src/upload.js
+++ b/frontend/src/upload.js
@@ -1,17 +1,74 @@
+/* global MAXFILESIZE EXPIRE_SECONDS */
+require('./common');
 const FileSender = require('./fileSender');
-const { notify, gcmCompliant } = require('./utils');
+const {
+  notify,
+  gcmCompliant,
+  findMetric,
+  sendEvent,
+  ONE_DAY_IN_MS
+} = require('./utils');
+const bytes = require('bytes');
+const Storage = require('./storage');
+const storage = new Storage(localStorage);
+
 const $ = require('jquery');
 require('jquery-circle-progress');
 
 const Raven = window.Raven;
 
+if (storage.has('referrer')) {
+  window.referrer = storage.referrer;
+  storage.remove('referrer');
+} else {
+  window.referrer = 'external';
+}
+
 $(document).ready(function() {
   gcmCompliant().catch(err => {
     $('#page-one').attr('hidden', true);
-    $('#unsupported-browser').removeAttr('hidden');
+    sendEvent('sender', 'unsupported', {
+      cd6: err
+    }).then(() => {
+      location.replace('/unsupported');
+    });
   });
 
   $('#file-upload').change(onUpload);
+
+  $('.legal-links a, .social-links a, #dl-firefox').click(function(target) {
+    target.preventDefault();
+    const metric = findMetric(target.currentTarget.href);
+    // record exited event by recipient
+    sendEvent('sender', 'exited', {
+      cd3: metric
+    }).then(() => {
+      location.href = target.currentTarget.href;
+    });
+  });
+
+  $('#send-new-completed').click(function(target) {
+    target.preventDefault();
+    // record restarted event
+    sendEvent('sender', 'restarted', {
+      cd2: 'completed'
+    }).then(() => {
+      storage.referrer = 'completed-upload';
+      location.href = target.currentTarget.href;
+    });
+  });
+
+  $('#send-new-error').click(function(target) {
+    target.preventDefault();
+    // record restarted event
+    sendEvent('sender', 'restarted', {
+      cd2: 'errored'
+    }).then(() => {
+      storage.referrer = 'errored-upload';
+      location.href = target.currentTarget.href;
+    });
+  });
+
   $('body').on('dragover', allowDrop).on('drop', onUpload);
   // reset copy button
   const $copyBtn = $('#copy-btn');
@@ -19,18 +76,23 @@ $(document).ready(function() {
   $('#link').attr('disabled', false);
   $copyBtn.attr('data-l10n-id', 'copyUrlFormButton');
 
-  if (localStorage.length === 0) {
+  const files = storage.files;
+  if (files.length === 0) {
     toggleHeader();
   } else {
-    for (let i = 0; i < localStorage.length; i++) {
-      const id = localStorage.key(i);
-      //check if file exists before adding to list
-      checkExistence(id, true);
+    for (const index in files) {
+      const id = files[index].fileId;
+      //check if file still exists before adding to list
+      checkExistence(id, files[index], true);
     }
   }
 
   // copy link to clipboard
   $copyBtn.click(() => {
+    // record copied event from success screen
+    sendEvent('sender', 'copied', {
+      cd4: 'success-screen'
+    });
     const aux = document.createElement('input');
     aux.setAttribute('value', $('#link').attr('value'));
     document.body.appendChild(aux);
@@ -40,7 +102,9 @@ $(document).ready(function() {
     //disable button for 3s
     $copyBtn.attr('disabled', true);
     $('#link').attr('disabled', true);
-    $copyBtn.html('<img src="/resources/check-16.svg" class="icon-check"></img>');
+    $copyBtn.html(
+      '<img src="/resources/check-16.svg" class="icon-check"></img>'
+    );
     window.setTimeout(() => {
       $copyBtn.attr('disabled', false);
       $('#link').attr('disabled', false);
@@ -69,14 +133,28 @@ $(document).ready(function() {
   // on file upload by browse or drag & drop
   function onUpload(event) {
     event.preventDefault();
+
+    // don't allow upload if not on upload page
+    if ($('#page-one').attr('hidden')){
+      return;
+    }
+
+    storage.totalUploads += 1;
+
     let file = '';
     if (event.type === 'drop') {
-      if (event.originalEvent.dataTransfer.files.length > 1 || event.originalEvent.dataTransfer.files[0].size === 0){
+      if (!event.originalEvent.dataTransfer.files[0]) {
         $('.upload-window').removeClass('ondrag');
-        document.l10n.formatValue('uploadPageMultipleFilesAlert')
-                     .then(str => {
-                       alert(str);
-                     });
+        return;
+      }
+      if (
+        event.originalEvent.dataTransfer.files.length > 1 ||
+        event.originalEvent.dataTransfer.files[0].size === 0
+      ) {
+        $('.upload-window').removeClass('ondrag');
+        document.l10n.formatValue('uploadPageMultipleFilesAlert').then(str => {
+          alert(str);
+        });
         return;
       }
       file = event.originalEvent.dataTransfer.files[0];
@@ -84,21 +162,39 @@ $(document).ready(function() {
       file = event.target.files[0];
     }
 
+    if (file.size > MAXFILESIZE) {
+      return document.l10n
+        .formatValue('fileTooBig', { size: bytes(MAXFILESIZE) })
+        .then(alert);
+    }
+
     $('#page-one').attr('hidden', true);
     $('#upload-error').attr('hidden', true);
     $('#upload-progress').removeAttr('hidden');
+    document.l10n.formatValue('importingFile').then(importingFile => {
+      $('.progress-text').text(importingFile);
+    });
     //don't allow drag and drop when not on page-one
     $('body').off('drop', onUpload);
-    const expiration = 24 * 60 * 60 * 1000; //will eventually come from a field
 
     const fileSender = new FileSender(file);
     $('#cancel-upload').click(() => {
       fileSender.cancel();
       location.reload();
-      document.l10n.formatValue('uploadCancelNotification')
-                   .then(str => {
-                     notify(str);
-                   });
+      document.l10n.formatValue('uploadCancelNotification').then(str => {
+        notify(str);
+      });
+      storage.referrer = 'cancelled-upload';
+
+      // record upload-stopped (cancelled) by sender
+      sendEvent('sender', 'upload-stopped', {
+        cm1: file.size,
+        cm5: storage.totalUploads,
+        cm6: unexpiredFiles,
+        cm7: storage.totalDownloads,
+        cd1: event.type === 'drop' ? 'drop' : 'click',
+        cd2: 'cancelled'
+      });
     });
 
     fileSender.on('progress', progress => {
@@ -106,101 +202,152 @@ $(document).ready(function() {
       // update progress bar
       $('#ul-progress').circleProgress('value', percent);
       $('#ul-progress').circleProgress().on('circle-animation-end', function() {
-        $('.percent-number').html(`${Math.floor(percent * 100)}`);
+        $('.percent-number').text(`${Math.floor(percent * 100)}`);
       });
-      if (progress[1] < 1000000) {
-        $('.progress-text').text(
-          `${file.name} (${(progress[0] / 1000).toFixed(1)}KB of ${(progress[1] / 1000).toFixed(1)}KB)`
-        );
-      } else if (progress[1] < 1000000000) {
-        $('.progress-text').text(
-          `${file.name} (${(progress[0] / 1000000).toFixed(1)}MB of ${(progress[1] / 1000000).toFixed(1)}MB)`
-        );
-      } else {
-        $('.progress-text').text(
-          `${file.name} (${(progress[0] / 1000000).toFixed(1)}MB of ${(progress[1] / 1000000000).toFixed(1)}GB)`
-        );
-      }
-    });
-
-    fileSender.on('loading', isStillLoading => {
-      // The file is loading into Firefox at this stage
-      if (isStillLoading) {
-        console.log('Processing');
-      } else {
-        console.log('Finished processing');
-      }
+      $('.progress-text').text(
+        `${file.name} (${bytes(progress[0], {
+          decimalPlaces: 1,
+          fixedDecimals: true
+        })} of ${bytes(progress[1], { decimalPlaces: 1 })})`
+      );
     });
 
     fileSender.on('hashing', isStillHashing => {
       // The file is being hashed
       if (isStillHashing) {
-        console.log('Hashing');
+        document.l10n.formatValue('verifyingFile').then(verifyingFile => {
+          $('.progress-text').text(verifyingFile);
+        });
       } else {
         console.log('Finished hashing');
       }
     });
 
+    let uploadStart;
     fileSender.on('encrypting', isStillEncrypting => {
       // The file is being encrypted
       if (isStillEncrypting) {
-        console.log('Encrypting');
+        document.l10n.formatValue('encryptingFile').then(encryptingFile => {
+          $('.progress-text').text(encryptingFile);
+        });
       } else {
         console.log('Finished encrypting');
+        uploadStart = Date.now();
       }
     });
-    let t = '';
-    fileSender
-      .upload()
-      .then(info => {
-        const fileData = {
-          name: file.name,
-          fileId: info.fileId,
-          url: info.url,
-          secretKey: info.secretKey,
-          deleteToken: info.deleteToken,
-          creationDate: new Date(),
-          expiry: expiration
-        };
-        localStorage.setItem(info.fileId, JSON.stringify(fileData));
-        $('#upload-filename').attr('data-l10n-id', 'uploadSuccessConfirmHeader');
-        t = window.setTimeout(() => {
+
+    let t;
+    const startTime = Date.now();
+    const unexpiredFiles = storage.numFiles + 1;
+
+    // record upload-started event by sender
+    sendEvent('sender', 'upload-started', {
+      cm1: file.size,
+      cm5: storage.totalUploads,
+      cm6: unexpiredFiles,
+      cm7: storage.totalDownloads,
+      cd1: event.type === 'drop' ? 'drop' : 'click',
+      cd5: window.referrer
+    });
+
+    // For large files we need to give the ui a tick to breathe and update
+    // before we kick off the FileSender
+    setTimeout(() => {
+      fileSender
+        .upload()
+        .then(info => {
+          const endTime = Date.now();
+          const totalTime = endTime - startTime;
+          const uploadTime = endTime - uploadStart;
+          const uploadSpeed = file.size / (uploadTime / 1000);
+          const expiration = EXPIRE_SECONDS * 1000;
+
+          // record upload-stopped (completed) by sender
+          sendEvent('sender', 'upload-stopped', {
+            cm1: file.size,
+            cm2: totalTime,
+            cm3: uploadSpeed,
+            cm5: storage.totalUploads,
+            cm6: unexpiredFiles,
+            cm7: storage.totalDownloads,
+            cd1: event.type === 'drop' ? 'drop' : 'click',
+            cd2: 'completed'
+          });
+
+          const fileData = {
+            name: file.name,
+            size: file.size,
+            fileId: info.fileId,
+            url: info.url,
+            secretKey: info.secretKey,
+            deleteToken: info.deleteToken,
+            creationDate: new Date(),
+            expiry: expiration,
+            totalTime: totalTime,
+            typeOfUpload: event.type === 'drop' ? 'drop' : 'click',
+            uploadSpeed: uploadSpeed
+          };
+
+          storage.addFile(info.fileId, fileData);
+          $('#upload-filename').attr(
+            'data-l10n-id',
+            'uploadSuccessConfirmHeader'
+          );
+          t = window.setTimeout(() => {
+            $('#page-one').attr('hidden', true);
+            $('#upload-progress').attr('hidden', true);
+            $('#upload-error').attr('hidden', true);
+            $('#share-link').removeAttr('hidden');
+          }, 1000);
+
+          populateFileList(fileData);
+          document.l10n.formatValue('notifyUploadDone').then(str => {
+            notify(str);
+          });
+        })
+        .catch(err => {
+          // err is 0 when coming from a cancel upload event
+          if (err === 0) {
+            return;
+          }
+          // only show error page when the error is anything other than user cancelling the upload
+          Raven.captureException(err);
           $('#page-one').attr('hidden', true);
           $('#upload-progress').attr('hidden', true);
-          $('#upload-error').attr('hidden', true);
-          $('#share-link').removeAttr('hidden');
-        }, 1000);
+          $('#upload-error').removeAttr('hidden');
+          window.clearTimeout(t);
 
-        populateFileList(JSON.stringify(fileData));
-        document.l10n.formatValue('notifyUploadDone')
-                     .then(str => {
-                       notify(str);
-                     });
-      })
-      .catch(err => {
-        Raven.captureException(err);
-        console.log(err);
-        $('#page-one').attr('hidden', true);
-        $('#upload-progress').attr('hidden', true);
-        $('#upload-error').removeAttr('hidden');
-        window.clearTimeout(t);
-      });
+          // record upload-stopped (errored) by sender
+          sendEvent('sender', 'upload-stopped', {
+            cm1: file.size,
+            cm5: storage.totalUploads,
+            cm6: unexpiredFiles,
+            cm7: storage.totalDownloads,
+            cd1: event.type === 'drop' ? 'drop' : 'click',
+            cd2: 'errored',
+            cd6: err
+          });
+        });
+    }, 10);
   }
 
   function allowDrop(ev) {
     ev.preventDefault();
   }
 
-  function checkExistence(id, populate) {
+  function checkExistence(id, file, populate) {
     const xhr = new XMLHttpRequest();
     xhr.onreadystatechange = () => {
       if (xhr.readyState === XMLHttpRequest.DONE) {
         if (xhr.status === 200) {
           if (populate) {
-            populateFileList(localStorage.getItem(id));
+            populateFileList(file);
           }
         } else if (xhr.status === 404) {
-          localStorage.removeItem(id);
+          storage.remove(id);
+          if (storage.numFiles === 0) {
+            toggleHeader();
+          }
         }
       }
     };
@@ -208,21 +355,23 @@ $(document).ready(function() {
     xhr.send();
   }
 
-  //update file table with current files in localStorage
+  //update file table with current files in storage
   function populateFileList(file) {
-    try {
-      file = JSON.parse(file);
-    } catch (e) {
-      return;
-    }
-
     const row = document.createElement('tr');
     const name = document.createElement('td');
     const link = document.createElement('td');
-    const $copyIcon = $('<img>', { src: '/resources/copy-16.svg', class: 'icon-copy', 'data-l10n-id': 'copyUrlHover'});
+    const $copyIcon = $('<img>', {
+      src: '/resources/copy-16.svg',
+      class: 'icon-copy',
+      'data-l10n-id': 'copyUrlHover'
+    });
     const expiry = document.createElement('td');
     const del = document.createElement('td');
-    const $delIcon = $('<img>', { src: '/resources/close-16.svg', class: 'icon-delete', 'data-l10n-id': 'deleteButtonHover' });
+    const $delIcon = $('<img>', {
+      src: '/resources/close-16.svg',
+      class: 'icon-delete',
+      'data-l10n-id': 'deleteButtonHover'
+    });
     const popupDiv = document.createElement('div');
     const $popupText = $('<div>', { class: 'popuptext' });
     const cellText = document.createTextNode(file.name);
@@ -230,14 +379,8 @@ $(document).ready(function() {
     const url = file.url.trim() + `#${file.secretKey}`.trim();
 
     $('#link').attr('value', url);
-    $('#copy-text').attr(
-      'data-l10n-args',
-      '{"filename": "' + file.name + '"}'
-    );
-    $('#copy-text').attr(
-      'data-l10n-id',
-      'copyUrlFormLabelWithName'
-    );
+    $('#copy-text').attr('data-l10n-args', '{"filename": "' + file.name + '"}');
+    $('#copy-text').attr('data-l10n-id', 'copyUrlFormLabelWithName');
     $popupText.attr('tabindex', '-1');
 
     name.appendChild(cellText);
@@ -258,16 +401,19 @@ $(document).ready(function() {
 
     //copy link to clipboard when icon clicked
     $copyIcon.click(function() {
+      // record copied event from upload list
+      sendEvent('sender', 'copied', {
+        cd4: 'upload-list'
+      });
       const aux = document.createElement('input');
       aux.setAttribute('value', url);
       document.body.appendChild(aux);
       aux.select();
       document.execCommand('copy');
       document.body.removeChild(aux);
-      document.l10n.formatValue('copiedUrl')
-                   .then(translated => {
-                     link.innerHTML = translated;
-                   });
+      document.l10n.formatValue('copiedUrl').then(translated => {
+        link.innerHTML = translated;
+      });
       window.setTimeout(() => {
         const linkImg = document.createElement('img');
         $(linkImg).addClass('icon-copy');
@@ -283,7 +429,7 @@ $(document).ready(function() {
     future.setTime(file.creationDate.getTime() + file.expiry);
 
     let countdown = 0;
-    countdown = future.getTime() - new Date().getTime();
+    countdown = future.getTime() - Date.now();
     let minutes = Math.floor(countdown / 1000 / 60);
     let hours = Math.floor(minutes / 60);
     let seconds = Math.floor(countdown / 1000 % 60);
@@ -291,7 +437,7 @@ $(document).ready(function() {
     poll();
 
     function poll() {
-      countdown = future.getTime() - new Date().getTime();
+      countdown = future.getTime() - Date.now();
       minutes = Math.floor(countdown / 1000 / 60);
       hours = Math.floor(minutes / 60);
       seconds = Math.floor(countdown / 1000 % 60);
@@ -310,7 +456,7 @@ $(document).ready(function() {
       }
       //remove from list when expired
       if (countdown <= 0) {
-        localStorage.removeItem(file.fileId);
+        storage.remove(file.fileId);
         $(expiry).parents('tr').remove();
         window.clearTimeout(t);
         toggleHeader();
@@ -319,20 +465,14 @@ $(document).ready(function() {
 
     // create popup
     popupDiv.classList.add('popup');
-    const popupDelSpan = document.createElement('span');
-    $(popupDelSpan).addClass('del-file');
-    $(popupDelSpan).attr('data-l10n-id', 'deleteFileList');
-
-    const popupNvmSpan = document.createElement('span');
-    $(popupNvmSpan).addClass('nvm');
-    $(popupNvmSpan).attr('data-l10n-id', 'nevermindButton');
-
-    $popupText.html([
-      popupDelSpan,
-      '<br/>',
-      popupNvmSpan
-    ]);
+    const $popupMessage = $('<div>', { class: 'popup-message' });
+    $popupMessage.attr('data-l10n-id', 'deletePopupText');
+    const $popupDelSpan = $('<span>', { class: 'popup-yes' });
+    $popupDelSpan.attr('data-l10n-id', 'deletePopupYes');
+    const $popupNvmSpan = $('<span>', { class: 'popup-no' });
+    $popupNvmSpan.attr('data-l10n-id', 'deletePopupCancel');
 
+    $popupText.html([$popupMessage, $popupDelSpan,  $popupNvmSpan]);
 
     // add data cells to table row
     row.appendChild(name);
@@ -345,18 +485,51 @@ $(document).ready(function() {
     row.appendChild(del);
     $('tbody').append(row); //add row to table
 
+    const unexpiredFiles = storage.numFiles;
+
     // delete file
-    $popupText.find('.del-file').click(e => {
+    $popupText.find('.popup-yes').click(e => {
       FileSender.delete(file.fileId, file.deleteToken).then(() => {
         $(e.target).parents('tr').remove();
-        localStorage.removeItem(file.fileId);
+        const timeToExpiry =
+          ONE_DAY_IN_MS - (Date.now() - file.creationDate.getTime());
+        // record upload-deleted from file list
+        sendEvent('sender', 'upload-deleted', {
+          cm1: file.size,
+          cm2: file.totalTime,
+          cm3: file.uploadSpeed,
+          cm4: timeToExpiry,
+          cm5: storage.totalUploads,
+          cm6: unexpiredFiles,
+          cm7: storage.totalDownloads,
+          cd1: file.typeOfUpload,
+          cd4: 'upload-list'
+        }).then(() => {
+          storage.remove(file.fileId);
+        });
         toggleHeader();
       });
     });
+
     document.getElementById('delete-file').onclick = () => {
       FileSender.delete(file.fileId, file.deleteToken).then(() => {
-        localStorage.removeItem(file.fileId);
-        location.reload();
+        const timeToExpiry =
+          ONE_DAY_IN_MS - (Date.now() - file.creationDate.getTime());
+        // record upload-deleted from success screen
+        sendEvent('sender', 'upload-deleted', {
+          cm1: file.size,
+          cm2: file.totalTime,
+          cm3: file.uploadSpeed,
+          cm4: timeToExpiry,
+          cm5: storage.totalUploads,
+          cm6: unexpiredFiles,
+          cm7: storage.totalDownloads,
+          cd1: file.typeOfUpload,
+          cd4: 'success-screen'
+        }).then(() => {
+          storage.remove(file.fileId);
+          location.reload();
+        });
       });
     };
     // show popup
@@ -365,7 +538,7 @@ $(document).ready(function() {
       $popupText.focus();
     });
     // hide popup
-    $popupText.find('.nvm').click(function(e) {
+    $popupText.find('.popup-no').click(function(e) {
       e.stopPropagation();
       $popupText.removeClass('show');
     });
@@ -377,7 +550,6 @@ $(document).ready(function() {
       $popupText.removeClass('show');
     });
 
-
     toggleHeader();
   }
   function toggleHeader() {
diff --git a/frontend/src/utils.js b/frontend/src/utils.js
index aff37488..d338b7c9 100644
--- a/frontend/src/utils.js
+++ b/frontend/src/utils.js
@@ -69,9 +69,55 @@ function gcmCompliant() {
   }
 }
 
+function findMetric(href) {
+  switch (href) {
+    case 'https://www.mozilla.org/':
+      return 'mozilla';
+    case 'https://www.mozilla.org/about/legal':
+      return 'legal';
+    case 'https://testpilot.firefox.com/about':
+      return 'about';
+    case 'https://testpilot.firefox.com/privacy':
+      return 'privacy';
+    case 'https://testpilot.firefox.com/terms':
+      return 'terms';
+    case 'https://www.mozilla.org/en-US/privacy/websites/#cookies':
+      return 'cookies';
+    case 'https://github.com/mozilla/send':
+      return 'github';
+    case 'https://twitter.com/FxTestPilot':
+      return 'twitter';
+    case 'https://www.mozilla.org/firefox/new/?scene=2':
+      return 'download-firefox';
+    default:
+      return 'other';
+  }
+}
+
+function isFile(id) {
+  return ![
+    'referrer',
+    'totalDownloads',
+    'totalUploads',
+    'testpilot_ga__cid'
+  ].includes(id);
+}
+
+function sendEvent() {
+  return window.analytics.sendEvent
+    .apply(window.analytics, arguments)
+    .catch(() => 0);
+}
+
+const ONE_DAY_IN_MS = 86400000;
+
 module.exports = {
   arrayToHex,
   hexToArray,
   notify,
-  gcmCompliant
+  gcmCompliant,
+  findMetric,
+  isFile,
+  sendEvent,
+  ONE_DAY_IN_MS
 };
diff --git a/package-lock.json b/package-lock.json
index 8e2466bd..e3cd7b39 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "firefox-send",
-  "version": "0.1.2",
+  "version": "0.2.1",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
@@ -36,18 +36,15 @@
         }
       }
     },
-    "adm-zip": {
-      "version": "0.4.7",
-      "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.7.tgz",
-      "integrity": "sha1-hgbCy/HEJs6MjsABdER/1Jtur8E="
-    },
     "ajv": {
-      "version": "4.11.8",
-      "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz",
-      "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=",
+      "version": "5.2.2",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.2.2.tgz",
+      "integrity": "sha1-R8aNaehvXZUxA7AHSpQw3GPaXjk=",
       "dev": true,
       "requires": {
         "co": "4.6.0",
+        "fast-deep-equal": "1.0.0",
+        "json-schema-traverse": "0.3.1",
         "json-stable-stringify": "1.0.1"
       },
       "dependencies": {
@@ -99,16 +96,6 @@
       "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
       "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
     },
-    "anymatch": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.0.tgz",
-      "integrity": "sha1-o+Uvo5FoyCX/V7AkgSbOWo/5VQc=",
-      "dev": true,
-      "requires": {
-        "arrify": "1.0.1",
-        "micromatch": "2.3.11"
-      }
-    },
     "argparse": {
       "version": "1.0.9",
       "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz",
@@ -124,13 +111,13 @@
       "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=",
       "dev": true,
       "requires": {
-        "arr-flatten": "1.0.3"
+        "arr-flatten": "1.1.0"
       }
     },
     "arr-flatten": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.0.3.tgz",
-      "integrity": "sha1-onTthawIhJtr14R8RYB0XcUa37E=",
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
+      "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
       "dev": true
     },
     "array-differ": {
@@ -196,9 +183,9 @@
       "dev": true
     },
     "asap": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.5.tgz",
-      "integrity": "sha1-UidltQw1EEkOUtfc/ghe+bqWlY8="
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+      "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
     },
     "asn1.js": {
       "version": "4.9.1",
@@ -234,16 +221,11 @@
       "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
       "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo="
     },
-    "async-each": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz",
-      "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=",
-      "dev": true
-    },
     "asynckit": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
-      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
+      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
+      "dev": true
     },
     "autoprefixer": {
       "version": "6.7.7",
@@ -252,7 +234,7 @@
       "dev": true,
       "requires": {
         "browserslist": "1.7.7",
-        "caniuse-db": "1.0.30000693",
+        "caniuse-db": "1.0.30000704",
         "normalize-range": "0.1.2",
         "num2fraction": "1.2.2",
         "postcss": "5.2.17",
@@ -260,12 +242,13 @@
       }
     },
     "aws-sdk": {
-      "version": "2.77.0",
-      "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.77.0.tgz",
-      "integrity": "sha1-gJCQu4dNj0//ysUxZilYdjjnhlw=",
+      "version": "2.89.0",
+      "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.89.0.tgz",
+      "integrity": "sha1-2/RpmcvV50zhsGZAsH+o4ljvB7w=",
       "requires": {
-        "buffer": "5.0.6",
+        "buffer": "4.9.1",
         "crypto-browserify": "1.0.9",
+        "events": "1.1.1",
         "jmespath": "0.15.0",
         "querystring": "0.2.0",
         "sax": "1.2.1",
@@ -283,7 +266,7 @@
       "requires": {
         "chalk": "1.1.3",
         "esutils": "2.0.2",
-        "js-tokens": "3.0.1"
+        "js-tokens": "3.0.2"
       }
     },
     "balanced-match": {
@@ -296,12 +279,6 @@
       "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz",
       "integrity": "sha1-qRlH2h9KUW6jjltOwOw3c2deCIY="
     },
-    "binary-extensions": {
-      "version": "1.8.0",
-      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.8.0.tgz",
-      "integrity": "sha1-SOyNFt9Dd+rl+liEaCSAr02Vx3Q=",
-      "dev": true
-    },
     "bn.js": {
       "version": "4.11.7",
       "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.7.tgz",
@@ -409,7 +386,7 @@
         "concat-stream": "1.5.2",
         "console-browserify": "1.1.0",
         "constants-browserify": "1.0.0",
-        "crypto-browserify": "3.11.0",
+        "crypto-browserify": "3.11.1",
         "defined": "1.0.0",
         "deps-sort": "2.0.0",
         "domain-browser": "1.1.7",
@@ -431,7 +408,7 @@
         "punycode": "1.3.2",
         "querystring-es3": "0.2.1",
         "read-only-stream": "2.0.0",
-        "readable-stream": "2.3.2",
+        "readable-stream": "2.3.3",
         "resolve": "1.3.3",
         "shasum": "1.0.2",
         "shell-quote": "1.6.1",
@@ -449,10 +426,20 @@
         "xtend": "4.0.1"
       },
       "dependencies": {
+        "buffer": {
+          "version": "5.0.6",
+          "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.0.6.tgz",
+          "integrity": "sha1-LqZp9+7Atu2gWwj4tf9mGyhXNYg=",
+          "dev": true,
+          "requires": {
+            "base64-js": "1.2.1",
+            "ieee754": "1.1.8"
+          }
+        },
         "crypto-browserify": {
-          "version": "3.11.0",
-          "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.11.0.tgz",
-          "integrity": "sha1-NlKgkGq5sqfgw85mpAjpV6JIVSI=",
+          "version": "3.11.1",
+          "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.11.1.tgz",
+          "integrity": "sha512-Na7ZlwCOqoaW5RwUK1WpXws2kv8mNhWdTlzob0UXulk6G9BDbyiJaGTYBIX61Ozn9l1EPPJpICZb4DaOpT9NlQ==",
           "dev": true,
           "requires": {
             "browserify-cipher": "1.0.0",
@@ -481,16 +468,10 @@
             "path-is-absolute": "1.0.1"
           }
         },
-        "isarray": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
-          "dev": true
-        },
         "readable-stream": {
-          "version": "2.3.2",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.2.tgz",
-          "integrity": "sha1-WgTfBeT1f+Pw3Gj90R3FyXx+b00=",
+          "version": "2.3.3",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
+          "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
           "dev": true,
           "requires": {
             "core-util-is": "1.0.2",
@@ -530,7 +511,7 @@
       "dev": true,
       "requires": {
         "buffer-xor": "1.0.3",
-        "cipher-base": "1.0.3",
+        "cipher-base": "1.0.4",
         "create-hash": "1.1.3",
         "evp_bytestokey": "1.0.0",
         "inherits": "2.0.3"
@@ -553,7 +534,7 @@
       "integrity": "sha1-2qJ3cXRwki7S/hhZQRihdUOXId0=",
       "dev": true,
       "requires": {
-        "cipher-base": "1.0.3",
+        "cipher-base": "1.0.4",
         "des.js": "1.0.0",
         "inherits": "2.0.3"
       }
@@ -598,17 +579,18 @@
       "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=",
       "dev": true,
       "requires": {
-        "caniuse-db": "1.0.30000693",
-        "electron-to-chromium": "1.3.14"
+        "caniuse-db": "1.0.30000704",
+        "electron-to-chromium": "1.3.16"
       }
     },
     "buffer": {
-      "version": "5.0.6",
-      "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.0.6.tgz",
-      "integrity": "sha1-LqZp9+7Atu2gWwj4tf9mGyhXNYg=",
+      "version": "4.9.1",
+      "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
+      "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
       "requires": {
         "base64-js": "1.2.1",
-        "ieee754": "1.1.8"
+        "ieee754": "1.1.8",
+        "isarray": "1.0.0"
       }
     },
     "buffer-xor": {
@@ -693,9 +675,9 @@
       "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs="
     },
     "caniuse-db": {
-      "version": "1.0.30000693",
-      "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000693.tgz",
-      "integrity": "sha1-hRDnqasErcyiOl3O+jTfnSjBziA=",
+      "version": "1.0.30000704",
+      "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000704.tgz",
+      "integrity": "sha1-jFqm/tgFjmXHDywfXWP3CIZQcFw=",
       "dev": true
     },
     "center-align": {
@@ -719,30 +701,14 @@
         "supports-color": "2.0.0"
       }
     },
-    "chokidar": {
-      "version": "1.7.0",
-      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz",
-      "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=",
-      "dev": true,
-      "requires": {
-        "anymatch": "1.3.0",
-        "async-each": "1.0.1",
-        "fsevents": "1.1.2",
-        "glob-parent": "2.0.0",
-        "inherits": "2.0.3",
-        "is-binary-path": "1.0.1",
-        "is-glob": "2.0.1",
-        "path-is-absolute": "1.0.1",
-        "readdirp": "2.1.0"
-      }
-    },
     "cipher-base": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.3.tgz",
-      "integrity": "sha1-7qvxlEGc6QDaMBjCB9IS8qbfCgc=",
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
+      "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
       "dev": true,
       "requires": {
-        "inherits": "2.0.3"
+        "inherits": "2.0.3",
+        "safe-buffer": "5.1.1"
       }
     },
     "circular-json": {
@@ -799,12 +765,27 @@
       "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
       "dev": true
     },
+    "color-convert": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz",
+      "integrity": "sha1-Gsz5fdc5uYO/mU1W/sj5WFNkG3o=",
+      "dev": true,
+      "requires": {
+        "color-name": "1.1.3"
+      }
+    },
     "color-diff": {
       "version": "0.1.7",
       "resolved": "https://registry.npmjs.org/color-diff/-/color-diff-0.1.7.tgz",
       "integrity": "sha1-bbeM2UgqjkWdQIIer0tQMoPcuOI=",
       "dev": true
     },
+    "color-name": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+      "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+      "dev": true
+    },
     "colorguard": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/colorguard/-/colorguard-1.2.0.tgz",
@@ -867,6 +848,7 @@
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz",
       "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=",
+      "dev": true,
       "requires": {
         "delayed-stream": "1.0.0"
       }
@@ -875,6 +857,7 @@
       "version": "2.9.0",
       "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz",
       "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=",
+      "dev": true,
       "requires": {
         "graceful-readlink": "1.0.1"
       }
@@ -882,7 +865,8 @@
     "component-emitter": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
-      "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
+      "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=",
+      "dev": true
     },
     "concat-map": {
       "version": "0.0.1",
@@ -900,12 +884,6 @@
         "typedarray": "0.0.6"
       },
       "dependencies": {
-        "isarray": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
-          "dev": true
-        },
         "readable-stream": {
           "version": "2.0.6",
           "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz",
@@ -1064,7 +1042,14 @@
     "cookiejar": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.1.tgz",
-      "integrity": "sha1-Qa1XsbVVlR7BcUEqgZQrHoIA00o="
+      "integrity": "sha1-Qa1XsbVVlR7BcUEqgZQrHoIA00o=",
+      "dev": true
+    },
+    "core-js": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.3.0.tgz",
+      "integrity": "sha1-+rg/uwstjchfpjbEudNMdUIMbWU=",
+      "dev": true
     },
     "core-util-is": {
       "version": "1.0.2",
@@ -1072,13 +1057,13 @@
       "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
     },
     "cosmiconfig": {
-      "version": "2.1.3",
-      "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-2.1.3.tgz",
-      "integrity": "sha1-lSdx6w3dwcs/ovb75RpSLpOz7go=",
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-2.2.1.tgz",
+      "integrity": "sha512-17m9pl5cD9jhPUHqaxSA4fyoiAQJUG7V3CQDxCF7gWzGYeUY0YEnLQdQyOEKjEPVv0yGbdCfdfJMq6SphRiRjw==",
       "dev": true,
       "requires": {
         "is-directory": "0.3.1",
-        "js-yaml": "3.8.4",
+        "js-yaml": "3.9.0",
         "minimist": "1.2.0",
         "object-assign": "4.1.1",
         "os-homedir": "1.0.2",
@@ -1110,7 +1095,7 @@
       "integrity": "sha1-YGBCrIuSYnUPSDyt2rD1gZFy2P0=",
       "dev": true,
       "requires": {
-        "cipher-base": "1.0.3",
+        "cipher-base": "1.0.4",
         "inherits": "2.0.3",
         "ripemd160": "2.0.1",
         "sha.js": "2.4.8"
@@ -1122,7 +1107,7 @@
       "integrity": "sha1-rLniIaThe9sHbpBlfEK5PjcmzwY=",
       "dev": true,
       "requires": {
-        "cipher-base": "1.0.3",
+        "cipher-base": "1.0.4",
         "create-hash": "1.1.3",
         "inherits": "2.0.3",
         "ripemd160": "2.0.1",
@@ -1130,19 +1115,11 @@
         "sha.js": "2.4.8"
       }
     },
-    "cross-env": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-5.0.1.tgz",
-      "integrity": "sha1-/05y6kO0faJIa0On8gQ7JgnkSRM=",
-      "requires": {
-        "cross-spawn": "5.1.0",
-        "is-windows": "1.0.1"
-      }
-    },
     "cross-spawn": {
       "version": "5.1.0",
       "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
       "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
+      "dev": true,
       "requires": {
         "lru-cache": "4.1.1",
         "shebang-command": "1.2.0",
@@ -1181,6 +1158,12 @@
             "readable-stream": "1.1.14"
           }
         },
+        "isarray": {
+          "version": "0.0.1",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+          "dev": true
+        },
         "through2": {
           "version": "0.6.5",
           "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz",
@@ -1299,7 +1282,8 @@
     "delayed-stream": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
-      "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
+      "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
+      "dev": true
     },
     "depd": {
       "version": "1.1.0",
@@ -1382,14 +1366,6 @@
       "requires": {
         "esutils": "2.0.2",
         "isarray": "1.0.0"
-      },
-      "dependencies": {
-        "isarray": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
-          "dev": true
-        }
       }
     },
     "doiuse": {
@@ -1399,7 +1375,7 @@
       "dev": true,
       "requires": {
         "browserslist": "1.7.7",
-        "caniuse-db": "1.0.30000693",
+        "caniuse-db": "1.0.30000704",
         "css-rule-stream": "1.1.0",
         "duplexer2": "0.0.2",
         "jsonfilter": "1.1.2",
@@ -1421,6 +1397,12 @@
             "readable-stream": "1.1.14"
           }
         },
+        "isarray": {
+          "version": "0.0.1",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+          "dev": true
+        },
         "through2": {
           "version": "0.6.5",
           "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz",
@@ -1475,19 +1457,13 @@
       "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=",
       "dev": true,
       "requires": {
-        "readable-stream": "2.3.2"
+        "readable-stream": "2.3.3"
       },
       "dependencies": {
-        "isarray": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
-          "dev": true
-        },
         "readable-stream": {
-          "version": "2.3.2",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.2.tgz",
-          "integrity": "sha1-WgTfBeT1f+Pw3Gj90R3FyXx+b00=",
+          "version": "2.3.3",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
+          "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
           "dev": true,
           "requires": {
             "core-util-is": "1.0.2",
@@ -1516,9 +1492,9 @@
       "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
     },
     "electron-to-chromium": {
-      "version": "1.3.14",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.14.tgz",
-      "integrity": "sha1-ZK8Pnv08PGrNV9cfg7Scp+6cS0M=",
+      "version": "1.3.16",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.16.tgz",
+      "integrity": "sha1-0OAmc1dUdwkBrjAaIWZMukXZL30=",
       "dev": true
     },
     "elliptic": {
@@ -1529,7 +1505,7 @@
       "requires": {
         "bn.js": "4.11.7",
         "brorand": "1.1.0",
-        "hash.js": "1.1.1",
+        "hash.js": "1.1.3",
         "hmac-drbg": "1.0.1",
         "inherits": "2.0.3",
         "minimalistic-assert": "1.0.0",
@@ -1573,6 +1549,12 @@
         "is-symbol": "1.0.1"
       }
     },
+    "es6-promise": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.0.2.tgz",
+      "integrity": "sha1-AQ1YWEI6XxGJeWZfRkhqlcbuK7Y=",
+      "dev": true
+    },
     "escape-html": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@@ -1584,14 +1566,16 @@
       "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
     },
     "eslint": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.1.0.tgz",
-      "integrity": "sha1-u7VaKCIO4Itp2pVU1FprLr/X2RM=",
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.3.0.tgz",
+      "integrity": "sha1-/NfJY3a780yF7mftABKimWQrEI8=",
       "dev": true,
       "requires": {
+        "ajv": "5.2.2",
         "babel-code-frame": "6.22.0",
         "chalk": "1.1.3",
         "concat-stream": "1.6.0",
+        "cross-spawn": "5.1.0",
         "debug": "2.6.8",
         "doctrine": "2.0.0",
         "eslint-scope": "3.7.1",
@@ -1600,14 +1584,14 @@
         "estraverse": "4.2.0",
         "esutils": "2.0.2",
         "file-entry-cache": "2.0.0",
+        "functional-red-black-tree": "1.0.1",
         "glob": "7.1.2",
         "globals": "9.18.0",
         "ignore": "3.3.3",
         "imurmurhash": "0.1.4",
-        "inquirer": "3.1.1",
-        "is-my-json-valid": "2.16.0",
+        "inquirer": "3.2.0",
         "is-resolvable": "1.0.0",
-        "js-yaml": "3.8.4",
+        "js-yaml": "3.9.0",
         "json-stable-stringify": "1.0.1",
         "levn": "0.3.0",
         "lodash": "4.17.4",
@@ -1619,6 +1603,7 @@
         "pluralize": "4.0.0",
         "progress": "2.0.0",
         "require-uncached": "1.0.3",
+        "semver": "5.3.0",
         "strip-json-comments": "2.0.1",
         "table": "4.0.1",
         "text-table": "0.2.0"
@@ -1631,7 +1616,7 @@
           "dev": true,
           "requires": {
             "inherits": "2.0.3",
-            "readable-stream": "2.3.2",
+            "readable-stream": "2.3.3",
             "typedarray": "0.0.6"
           }
         },
@@ -1647,7 +1632,7 @@
         "glob": {
           "version": "7.1.2",
           "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
-          "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=",
+          "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
           "dev": true,
           "requires": {
             "fs.realpath": "1.0.0",
@@ -1658,12 +1643,6 @@
             "path-is-absolute": "1.0.1"
           }
         },
-        "isarray": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
-          "dev": true
-        },
         "json-stable-stringify": {
           "version": "1.0.1",
           "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz",
@@ -1674,9 +1653,9 @@
           }
         },
         "readable-stream": {
-          "version": "2.3.2",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.2.tgz",
-          "integrity": "sha1-WgTfBeT1f+Pw3Gj90R3FyXx+b00=",
+          "version": "2.3.3",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
+          "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
           "dev": true,
           "requires": {
             "core-util-is": "1.0.2",
@@ -1691,7 +1670,7 @@
         "string_decoder": {
           "version": "1.0.3",
           "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
-          "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=",
+          "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
           "dev": true,
           "requires": {
             "safe-buffer": "5.1.1"
@@ -1709,9 +1688,9 @@
       }
     },
     "eslint-plugin-node": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-5.0.0.tgz",
-      "integrity": "sha512-9xERRx9V/8ciUHlTDlz9S4JiTL6Dc5oO+jKTy2mvQpxjhycpYZXzTT1t90IXjf+nAYw6/8sDnZfkeixJHxromA==",
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-5.1.1.tgz",
+      "integrity": "sha512-3xdoEbPyyQNyGhhqttjgSO3cU/non8QDBJF8ttGaHM2h8CaY5zFIngtqW6ZbLEIvhpoFPDVwiQg61b8zanx5zQ==",
       "dev": true,
       "requires": {
         "ignore": "3.3.3",
@@ -1745,22 +1724,22 @@
       "integrity": "sha1-KRC1zNSc6JPC//+qtP2LOjG4I3Q=",
       "dev": true,
       "requires": {
-        "acorn": "5.0.3",
+        "acorn": "5.1.1",
         "acorn-jsx": "3.0.1"
       },
       "dependencies": {
         "acorn": {
-          "version": "5.0.3",
-          "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.0.3.tgz",
-          "integrity": "sha1-xGDfCEkUY/AozLguqzcwvwEIez0=",
+          "version": "5.1.1",
+          "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.1.1.tgz",
+          "integrity": "sha512-vOk6uEMctu0vQrvuSqFdJyqj1Q0S5VTDL79qtjo+DhRr+1mmaD+tluFSCZqhvi/JUhXSzoZN2BhtstaPEeE8cw==",
           "dev": true
         }
       }
     },
     "esprima": {
-      "version": "3.1.3",
-      "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz",
-      "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=",
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz",
+      "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==",
       "dev": true
     },
     "esquery": {
@@ -1817,8 +1796,7 @@
     "events": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
-      "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=",
-      "dev": true
+      "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ="
     },
     "evp_bytestokey": {
       "version": "1.0.0",
@@ -1911,7 +1889,8 @@
     "extend": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz",
-      "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ="
+      "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=",
+      "dev": true
     },
     "external-editor": {
       "version": "2.0.4",
@@ -1920,7 +1899,7 @@
       "dev": true,
       "requires": {
         "iconv-lite": "0.4.18",
-        "jschardet": "1.4.2",
+        "jschardet": "1.5.0",
         "tmp": "0.0.31"
       },
       "dependencies": {
@@ -1941,6 +1920,12 @@
         "is-extglob": "1.0.0"
       }
     },
+    "fast-deep-equal": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz",
+      "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=",
+      "dev": true
+    },
     "fast-levenshtein": {
       "version": "2.0.6",
       "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
@@ -2040,12 +2025,14 @@
     "fluent": {
       "version": "0.3.1",
       "resolved": "https://registry.npmjs.org/fluent/-/fluent-0.3.1.tgz",
-      "integrity": "sha1-f6TOw/9X1wjI4me3sMSjTXog7nI="
+      "integrity": "sha1-f6TOw/9X1wjI4me3sMSjTXog7nI=",
+      "dev": true
     },
     "fluent-intl-polyfill": {
       "version": "0.1.0",
       "resolved": "https://registry.npmjs.org/fluent-intl-polyfill/-/fluent-intl-polyfill-0.1.0.tgz",
       "integrity": "sha1-ETOUSrJHeINHOZVZaIPg05z4hc8=",
+      "dev": true,
       "requires": {
         "intl-pluralrules": "github:projectfluent/IntlPluralRules#94cb0fa1c23ad943bc5aafef43cea132fa51d68b"
       }
@@ -2053,7 +2040,8 @@
     "fluent-langneg": {
       "version": "0.0.2",
       "resolved": "https://registry.npmjs.org/fluent-langneg/-/fluent-langneg-0.0.2.tgz",
-      "integrity": "sha1-1cZuKvEV70f3TQYn7UawrvbLe2w="
+      "integrity": "sha1-1cZuKvEV70f3TQYn7UawrvbLe2w=",
+      "dev": true
     },
     "for-in": {
       "version": "1.0.2",
@@ -2079,6 +2067,7 @@
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.2.0.tgz",
       "integrity": "sha1-ml47kpX5gLJiPPZPojixTOvKcHs=",
+      "dev": true,
       "requires": {
         "asynckit": "0.4.0",
         "combined-stream": "1.0.5",
@@ -2097,7 +2086,8 @@
     "formidable": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.1.1.tgz",
-      "integrity": "sha1-lriIb3w8NQi5Mta9cMTTqI818ak="
+      "integrity": "sha1-lriIb3w8NQi5Mta9cMTTqI818ak=",
+      "dev": true
     },
     "forwarded": {
       "version": "0.1.0",
@@ -2123,1046 +2113,26 @@
     "fs.realpath": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
-      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
-    },
-    "fsevents": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.2.tgz",
-      "integrity": "sha1-MoK3E/s62A7eDp/PRhG1qm/AM/Q=",
-      "dev": true,
-      "optional": true,
-      "requires": {
-        "nan": "2.6.2",
-        "node-pre-gyp": "0.6.36"
-      },
-      "dependencies": {
-        "abbrev": {
-          "version": "1.1.0",
-          "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz",
-          "integrity": "sha1-0FVMIlZjbi9W58LlrRg/hZQo2B8=",
-          "dev": true,
-          "optional": true
-        },
-        "ajv": {
-          "version": "4.11.8",
-          "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz",
-          "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "co": "4.6.0",
-            "json-stable-stringify": "1.0.1"
-          }
-        },
-        "ansi-regex": {
-          "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-          "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
-          "dev": true
-        },
-        "aproba": {
-          "version": "1.1.1",
-          "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.1.1.tgz",
-          "integrity": "sha1-ldNgDwdxCqDpKYxyatXs8urLq6s=",
-          "dev": true,
-          "optional": true
-        },
-        "are-we-there-yet": {
-          "version": "1.1.4",
-          "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz",
-          "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "delegates": "1.0.0",
-            "readable-stream": "2.2.9"
-          }
-        },
-        "asn1": {
-          "version": "0.2.3",
-          "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
-          "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=",
-          "dev": true,
-          "optional": true
-        },
-        "assert-plus": {
-          "version": "0.2.0",
-          "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz",
-          "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=",
-          "dev": true,
-          "optional": true
-        },
-        "asynckit": {
-          "version": "0.4.0",
-          "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
-          "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
-          "dev": true,
-          "optional": true
-        },
-        "aws-sign2": {
-          "version": "0.6.0",
-          "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz",
-          "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=",
-          "dev": true,
-          "optional": true
-        },
-        "aws4": {
-          "version": "1.6.0",
-          "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz",
-          "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=",
-          "dev": true,
-          "optional": true
-        },
-        "balanced-match": {
-          "version": "0.4.2",
-          "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz",
-          "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=",
-          "dev": true
-        },
-        "bcrypt-pbkdf": {
-          "version": "1.0.1",
-          "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz",
-          "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "tweetnacl": "0.14.5"
-          }
-        },
-        "block-stream": {
-          "version": "0.0.9",
-          "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz",
-          "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=",
-          "dev": true,
-          "requires": {
-            "inherits": "2.0.3"
-          }
-        },
-        "boom": {
-          "version": "2.10.1",
-          "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz",
-          "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=",
-          "dev": true,
-          "requires": {
-            "hoek": "2.16.3"
-          }
-        },
-        "brace-expansion": {
-          "version": "1.1.7",
-          "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz",
-          "integrity": "sha1-Pv/DxQ4ABTH7cg6v+A8K6O8jz1k=",
-          "dev": true,
-          "requires": {
-            "balanced-match": "0.4.2",
-            "concat-map": "0.0.1"
-          }
-        },
-        "buffer-shims": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz",
-          "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=",
-          "dev": true
-        },
-        "caseless": {
-          "version": "0.12.0",
-          "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
-          "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
-          "dev": true,
-          "optional": true
-        },
-        "co": {
-          "version": "4.6.0",
-          "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
-          "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
-          "dev": true,
-          "optional": true
-        },
-        "code-point-at": {
-          "version": "1.1.0",
-          "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
-          "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
-          "dev": true
-        },
-        "combined-stream": {
-          "version": "1.0.5",
-          "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz",
-          "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=",
-          "dev": true,
-          "requires": {
-            "delayed-stream": "1.0.0"
-          }
-        },
-        "concat-map": {
-          "version": "0.0.1",
-          "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
-          "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
-          "dev": true
-        },
-        "console-control-strings": {
-          "version": "1.1.0",
-          "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
-          "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
-          "dev": true
-        },
-        "core-util-is": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
-          "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
-          "dev": true
-        },
-        "cryptiles": {
-          "version": "2.0.5",
-          "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz",
-          "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "boom": "2.10.1"
-          }
-        },
-        "dashdash": {
-          "version": "1.14.1",
-          "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
-          "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "assert-plus": "1.0.0"
-          },
-          "dependencies": {
-            "assert-plus": {
-              "version": "1.0.0",
-              "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
-              "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
-              "dev": true,
-              "optional": true
-            }
-          }
-        },
-        "debug": {
-          "version": "2.6.8",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz",
-          "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "ms": "2.0.0"
-          }
-        },
-        "deep-extend": {
-          "version": "0.4.2",
-          "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz",
-          "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=",
-          "dev": true,
-          "optional": true
-        },
-        "delayed-stream": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
-          "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
-          "dev": true
-        },
-        "delegates": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
-          "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
-          "dev": true,
-          "optional": true
-        },
-        "ecc-jsbn": {
-          "version": "0.1.1",
-          "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz",
-          "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "jsbn": "0.1.1"
-          }
-        },
-        "extend": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz",
-          "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=",
-          "dev": true,
-          "optional": true
-        },
-        "extsprintf": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz",
-          "integrity": "sha1-4QgOBljjALBilJkMxw4VAiNf1VA=",
-          "dev": true
-        },
-        "forever-agent": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
-          "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
-          "dev": true,
-          "optional": true
-        },
-        "form-data": {
-          "version": "2.1.4",
-          "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz",
-          "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "asynckit": "0.4.0",
-            "combined-stream": "1.0.5",
-            "mime-types": "2.1.15"
-          }
-        },
-        "fs.realpath": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
-          "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
-          "dev": true
-        },
-        "fstream": {
-          "version": "1.0.11",
-          "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz",
-          "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=",
-          "dev": true,
-          "requires": {
-            "graceful-fs": "4.1.11",
-            "inherits": "2.0.3",
-            "mkdirp": "0.5.1",
-            "rimraf": "2.6.1"
-          }
-        },
-        "fstream-ignore": {
-          "version": "1.0.5",
-          "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz",
-          "integrity": "sha1-nDHa40dnAY/h0kmyTa2mfQktoQU=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "fstream": "1.0.11",
-            "inherits": "2.0.3",
-            "minimatch": "3.0.4"
-          }
-        },
-        "gauge": {
-          "version": "2.7.4",
-          "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
-          "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "aproba": "1.1.1",
-            "console-control-strings": "1.1.0",
-            "has-unicode": "2.0.1",
-            "object-assign": "4.1.1",
-            "signal-exit": "3.0.2",
-            "string-width": "1.0.2",
-            "strip-ansi": "3.0.1",
-            "wide-align": "1.1.2"
-          }
-        },
-        "getpass": {
-          "version": "0.1.7",
-          "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
-          "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "assert-plus": "1.0.0"
-          },
-          "dependencies": {
-            "assert-plus": {
-              "version": "1.0.0",
-              "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
-              "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
-              "dev": true,
-              "optional": true
-            }
-          }
-        },
-        "glob": {
-          "version": "7.1.2",
-          "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
-          "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
-          "dev": true,
-          "requires": {
-            "fs.realpath": "1.0.0",
-            "inflight": "1.0.6",
-            "inherits": "2.0.3",
-            "minimatch": "3.0.4",
-            "once": "1.4.0",
-            "path-is-absolute": "1.0.1"
-          }
-        },
-        "graceful-fs": {
-          "version": "4.1.11",
-          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
-          "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
-          "dev": true
-        },
-        "har-schema": {
-          "version": "1.0.5",
-          "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz",
-          "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=",
-          "dev": true,
-          "optional": true
-        },
-        "har-validator": {
-          "version": "4.2.1",
-          "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz",
-          "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "ajv": "4.11.8",
-            "har-schema": "1.0.5"
-          }
-        },
-        "has-unicode": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
-          "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
-          "dev": true,
-          "optional": true
-        },
-        "hawk": {
-          "version": "3.1.3",
-          "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz",
-          "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "boom": "2.10.1",
-            "cryptiles": "2.0.5",
-            "hoek": "2.16.3",
-            "sntp": "1.0.9"
-          }
-        },
-        "hoek": {
-          "version": "2.16.3",
-          "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz",
-          "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=",
-          "dev": true
-        },
-        "http-signature": {
-          "version": "1.1.1",
-          "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz",
-          "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "assert-plus": "0.2.0",
-            "jsprim": "1.4.0",
-            "sshpk": "1.13.0"
-          }
-        },
-        "inflight": {
-          "version": "1.0.6",
-          "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
-          "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
-          "dev": true,
-          "requires": {
-            "once": "1.4.0",
-            "wrappy": "1.0.2"
-          }
-        },
-        "inherits": {
-          "version": "2.0.3",
-          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
-          "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
-          "dev": true
-        },
-        "ini": {
-          "version": "1.3.4",
-          "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz",
-          "integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4=",
-          "dev": true,
-          "optional": true
-        },
-        "is-fullwidth-code-point": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
-          "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
-          "dev": true,
-          "requires": {
-            "number-is-nan": "1.0.1"
-          }
-        },
-        "is-typedarray": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
-          "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
-          "dev": true,
-          "optional": true
-        },
-        "isarray": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
-          "dev": true
-        },
-        "isstream": {
-          "version": "0.1.2",
-          "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
-          "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
-          "dev": true,
-          "optional": true
-        },
-        "jodid25519": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz",
-          "integrity": "sha1-BtSRIlUJNBlHfUJWM2BuDpB4KWc=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "jsbn": "0.1.1"
-          }
-        },
-        "jsbn": {
-          "version": "0.1.1",
-          "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
-          "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
-          "dev": true,
-          "optional": true
-        },
-        "json-schema": {
-          "version": "0.2.3",
-          "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
-          "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
-          "dev": true,
-          "optional": true
-        },
-        "json-stable-stringify": {
-          "version": "1.0.1",
-          "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz",
-          "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "jsonify": "0.0.0"
-          }
-        },
-        "json-stringify-safe": {
-          "version": "5.0.1",
-          "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
-          "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
-          "dev": true,
-          "optional": true
-        },
-        "jsonify": {
-          "version": "0.0.0",
-          "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
-          "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=",
-          "dev": true,
-          "optional": true
-        },
-        "jsprim": {
-          "version": "1.4.0",
-          "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz",
-          "integrity": "sha1-o7h+QCmNjDgFUtjMdiigu5WiKRg=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "assert-plus": "1.0.0",
-            "extsprintf": "1.0.2",
-            "json-schema": "0.2.3",
-            "verror": "1.3.6"
-          },
-          "dependencies": {
-            "assert-plus": {
-              "version": "1.0.0",
-              "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
-              "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
-              "dev": true,
-              "optional": true
-            }
-          }
-        },
-        "mime-db": {
-          "version": "1.27.0",
-          "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz",
-          "integrity": "sha1-gg9XIpa70g7CXtVeW13oaeVDbrE=",
-          "dev": true
-        },
-        "mime-types": {
-          "version": "2.1.15",
-          "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz",
-          "integrity": "sha1-pOv1BkCUVpI3uM9wBGd20J/JKu0=",
-          "dev": true,
-          "requires": {
-            "mime-db": "1.27.0"
-          }
-        },
-        "minimatch": {
-          "version": "3.0.4",
-          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
-          "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
-          "dev": true,
-          "requires": {
-            "brace-expansion": "1.1.7"
-          }
-        },
-        "minimist": {
-          "version": "0.0.8",
-          "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
-          "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
-          "dev": true
-        },
-        "mkdirp": {
-          "version": "0.5.1",
-          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
-          "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
-          "dev": true,
-          "requires": {
-            "minimist": "0.0.8"
-          }
-        },
-        "ms": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
-          "dev": true,
-          "optional": true
-        },
-        "node-pre-gyp": {
-          "version": "0.6.36",
-          "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.36.tgz",
-          "integrity": "sha1-22BBEst04NR3VU6bUFsXq936t4Y=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "mkdirp": "0.5.1",
-            "nopt": "4.0.1",
-            "npmlog": "4.1.0",
-            "rc": "1.2.1",
-            "request": "2.81.0",
-            "rimraf": "2.6.1",
-            "semver": "5.3.0",
-            "tar": "2.2.1",
-            "tar-pack": "3.4.0"
-          }
-        },
-        "nopt": {
-          "version": "4.0.1",
-          "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz",
-          "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "abbrev": "1.1.0",
-            "osenv": "0.1.4"
-          }
-        },
-        "npmlog": {
-          "version": "4.1.0",
-          "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.0.tgz",
-          "integrity": "sha512-ocolIkZYZt8UveuiDS0yAkkIjid1o7lPG8cYm05yNYzBn8ykQtaiPMEGp8fY9tKdDgm8okpdKzkvu1y9hUYugA==",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "are-we-there-yet": "1.1.4",
-            "console-control-strings": "1.1.0",
-            "gauge": "2.7.4",
-            "set-blocking": "2.0.0"
-          }
-        },
-        "number-is-nan": {
-          "version": "1.0.1",
-          "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
-          "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
-          "dev": true
-        },
-        "oauth-sign": {
-          "version": "0.8.2",
-          "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz",
-          "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=",
-          "dev": true,
-          "optional": true
-        },
-        "object-assign": {
-          "version": "4.1.1",
-          "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
-          "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
-          "dev": true,
-          "optional": true
-        },
-        "once": {
-          "version": "1.4.0",
-          "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
-          "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
-          "dev": true,
-          "requires": {
-            "wrappy": "1.0.2"
-          }
-        },
-        "os-homedir": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
-          "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
-          "dev": true,
-          "optional": true
-        },
-        "os-tmpdir": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
-          "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
-          "dev": true,
-          "optional": true
-        },
-        "osenv": {
-          "version": "0.1.4",
-          "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz",
-          "integrity": "sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "os-homedir": "1.0.2",
-            "os-tmpdir": "1.0.2"
-          }
-        },
-        "path-is-absolute": {
-          "version": "1.0.1",
-          "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
-          "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
-          "dev": true
-        },
-        "performance-now": {
-          "version": "0.2.0",
-          "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz",
-          "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=",
-          "dev": true,
-          "optional": true
-        },
-        "process-nextick-args": {
-          "version": "1.0.7",
-          "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
-          "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=",
-          "dev": true
-        },
-        "punycode": {
-          "version": "1.4.1",
-          "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
-          "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
-          "dev": true,
-          "optional": true
-        },
-        "qs": {
-          "version": "6.4.0",
-          "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz",
-          "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=",
-          "dev": true,
-          "optional": true
-        },
-        "rc": {
-          "version": "1.2.1",
-          "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.1.tgz",
-          "integrity": "sha1-LgPo5C7kULjLPc5lvhv4l04d/ZU=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "deep-extend": "0.4.2",
-            "ini": "1.3.4",
-            "minimist": "1.2.0",
-            "strip-json-comments": "2.0.1"
-          },
-          "dependencies": {
-            "minimist": {
-              "version": "1.2.0",
-              "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
-              "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
-              "dev": true,
-              "optional": true
-            }
-          }
-        },
-        "readable-stream": {
-          "version": "2.2.9",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz",
-          "integrity": "sha1-z3jsb0ptHrQ9JkiMrJfwQudLf8g=",
-          "dev": true,
-          "requires": {
-            "buffer-shims": "1.0.0",
-            "core-util-is": "1.0.2",
-            "inherits": "2.0.3",
-            "isarray": "1.0.0",
-            "process-nextick-args": "1.0.7",
-            "string_decoder": "1.0.1",
-            "util-deprecate": "1.0.2"
-          }
-        },
-        "request": {
-          "version": "2.81.0",
-          "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz",
-          "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "aws-sign2": "0.6.0",
-            "aws4": "1.6.0",
-            "caseless": "0.12.0",
-            "combined-stream": "1.0.5",
-            "extend": "3.0.1",
-            "forever-agent": "0.6.1",
-            "form-data": "2.1.4",
-            "har-validator": "4.2.1",
-            "hawk": "3.1.3",
-            "http-signature": "1.1.1",
-            "is-typedarray": "1.0.0",
-            "isstream": "0.1.2",
-            "json-stringify-safe": "5.0.1",
-            "mime-types": "2.1.15",
-            "oauth-sign": "0.8.2",
-            "performance-now": "0.2.0",
-            "qs": "6.4.0",
-            "safe-buffer": "5.0.1",
-            "stringstream": "0.0.5",
-            "tough-cookie": "2.3.2",
-            "tunnel-agent": "0.6.0",
-            "uuid": "3.0.1"
-          }
-        },
-        "rimraf": {
-          "version": "2.6.1",
-          "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz",
-          "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=",
-          "dev": true,
-          "requires": {
-            "glob": "7.1.2"
-          }
-        },
-        "safe-buffer": {
-          "version": "5.0.1",
-          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz",
-          "integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c=",
-          "dev": true
-        },
-        "semver": {
-          "version": "5.3.0",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
-          "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=",
-          "dev": true,
-          "optional": true
-        },
-        "set-blocking": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
-          "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
-          "dev": true,
-          "optional": true
-        },
-        "signal-exit": {
-          "version": "3.0.2",
-          "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
-          "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
-          "dev": true,
-          "optional": true
-        },
-        "sntp": {
-          "version": "1.0.9",
-          "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz",
-          "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "hoek": "2.16.3"
-          }
-        },
-        "sshpk": {
-          "version": "1.13.0",
-          "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.0.tgz",
-          "integrity": "sha1-/yo+T9BEl1Vf7Zezmg/YL6+zozw=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "asn1": "0.2.3",
-            "assert-plus": "1.0.0",
-            "bcrypt-pbkdf": "1.0.1",
-            "dashdash": "1.14.1",
-            "ecc-jsbn": "0.1.1",
-            "getpass": "0.1.7",
-            "jodid25519": "1.0.2",
-            "jsbn": "0.1.1",
-            "tweetnacl": "0.14.5"
-          },
-          "dependencies": {
-            "assert-plus": {
-              "version": "1.0.0",
-              "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
-              "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
-              "dev": true,
-              "optional": true
-            }
-          }
-        },
-        "string_decoder": {
-          "version": "1.0.1",
-          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.1.tgz",
-          "integrity": "sha1-YuIA8DmVWmgQ2N8KM//A8BNmLZg=",
-          "dev": true,
-          "requires": {
-            "safe-buffer": "5.0.1"
-          }
-        },
-        "string-width": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
-          "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
-          "dev": true,
-          "requires": {
-            "code-point-at": "1.1.0",
-            "is-fullwidth-code-point": "1.0.0",
-            "strip-ansi": "3.0.1"
-          }
-        },
-        "stringstream": {
-          "version": "0.0.5",
-          "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz",
-          "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=",
-          "dev": true,
-          "optional": true
-        },
-        "strip-ansi": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
-          "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
-          "dev": true,
-          "requires": {
-            "ansi-regex": "2.1.1"
-          }
-        },
-        "strip-json-comments": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
-          "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
-          "dev": true,
-          "optional": true
-        },
-        "tar": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz",
-          "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=",
-          "dev": true,
-          "requires": {
-            "block-stream": "0.0.9",
-            "fstream": "1.0.11",
-            "inherits": "2.0.3"
-          }
-        },
-        "tar-pack": {
-          "version": "3.4.0",
-          "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.0.tgz",
-          "integrity": "sha1-I74tf2cagzk3bL2wuP4/3r8xeYQ=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "debug": "2.6.8",
-            "fstream": "1.0.11",
-            "fstream-ignore": "1.0.5",
-            "once": "1.4.0",
-            "readable-stream": "2.2.9",
-            "rimraf": "2.6.1",
-            "tar": "2.2.1",
-            "uid-number": "0.0.6"
-          }
-        },
-        "tough-cookie": {
-          "version": "2.3.2",
-          "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz",
-          "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "punycode": "1.4.1"
-          }
-        },
-        "tunnel-agent": {
-          "version": "0.6.0",
-          "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
-          "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "safe-buffer": "5.0.1"
-          }
-        },
-        "tweetnacl": {
-          "version": "0.14.5",
-          "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
-          "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
-          "dev": true,
-          "optional": true
-        },
-        "uid-number": {
-          "version": "0.0.6",
-          "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz",
-          "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=",
-          "dev": true,
-          "optional": true
-        },
-        "util-deprecate": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
-          "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
-          "dev": true
-        },
-        "uuid": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz",
-          "integrity": "sha1-ZUS7ot/ajBzxfmKaOjBeK7H+5sE=",
-          "dev": true,
-          "optional": true
-        },
-        "verror": {
-          "version": "1.3.6",
-          "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz",
-          "integrity": "sha1-z/XfEpRtKX0rqu+qJoniW+AcAFw=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "extsprintf": "1.0.2"
-          }
-        },
-        "wide-align": {
-          "version": "1.1.2",
-          "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz",
-          "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "string-width": "1.0.2"
-          }
-        },
-        "wrappy": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
-          "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
-          "dev": true
-        }
-      }
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+      "dev": true
     },
     "function-bind": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz",
       "integrity": "sha1-FhdnFMgBeY5Ojyz391KUZ7tKV3E="
     },
+    "functional-red-black-tree": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+      "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
+      "dev": true
+    },
     "gather-stream": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/gather-stream/-/gather-stream-1.0.0.tgz",
       "integrity": "sha1-szmUr0V6gRVwDUEPMXczy+egkEs=",
       "dev": true
     },
-    "generate-function": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz",
-      "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=",
-      "dev": true
-    },
-    "generate-object-property": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz",
-      "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=",
-      "dev": true,
-      "requires": {
-        "is-property": "1.0.2"
-      }
-    },
     "get-stdin": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz",
@@ -3261,7 +2231,8 @@
     "graceful-readlink": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz",
-      "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU="
+      "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=",
+      "dev": true
     },
     "growl": {
       "version": "1.9.2",
@@ -3278,27 +2249,6 @@
         "optimist": "0.6.1",
         "source-map": "0.4.4",
         "uglify-js": "2.8.29"
-      },
-      "dependencies": {
-        "uglify-js": {
-          "version": "2.8.29",
-          "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",
-          "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=",
-          "optional": true,
-          "requires": {
-            "source-map": "0.5.6",
-            "uglify-to-browserify": "1.0.2",
-            "yargs": "3.10.0"
-          },
-          "dependencies": {
-            "source-map": {
-              "version": "0.5.6",
-              "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz",
-              "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=",
-              "optional": true
-            }
-          }
-        }
       }
     },
     "has": {
@@ -3319,9 +2269,9 @@
       }
     },
     "has-flag": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
-      "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz",
+      "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=",
       "dev": true
     },
     "hash-base": {
@@ -3334,9 +2284,9 @@
       }
     },
     "hash.js": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.1.tgz",
-      "integrity": "sha512-I2TYCUjYQMmqmRMCp6jKMC5bvdXxGIZ/heITRR/0F1u0OP920ImEj/cXt3WgcTKBnNYGn7enxUzdai3db829JA==",
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz",
+      "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==",
       "dev": true,
       "requires": {
         "inherits": "2.0.3",
@@ -3344,19 +2294,19 @@
       }
     },
     "helmet": {
-      "version": "3.6.1",
-      "resolved": "https://registry.npmjs.org/helmet/-/helmet-3.6.1.tgz",
-      "integrity": "sha1-kfOqf6TJRnFZX7Vo39jChImjiL4=",
+      "version": "3.8.0",
+      "resolved": "https://registry.npmjs.org/helmet/-/helmet-3.8.0.tgz",
+      "integrity": "sha512-66RfO0koYNDIX94d8iXc0Fg+EfuQ2fvDr104bTiCno3RyD+igovNOFSe9F/X3ROrcCceLYQgxRSFD5P7fSveuA==",
       "requires": {
         "connect": "3.6.2",
         "dns-prefetch-control": "0.1.0",
         "dont-sniff-mimetype": "1.0.0",
         "expect-ct": "0.1.0",
         "frameguard": "3.0.0",
-        "helmet-csp": "2.4.0",
+        "helmet-csp": "2.5.0",
         "hide-powered-by": "1.0.0",
         "hpkp": "2.0.0",
-        "hsts": "2.0.0",
+        "hsts": "2.1.0",
         "ienoopen": "1.0.0",
         "nocache": "2.0.0",
         "referrer-policy": "1.1.0",
@@ -3364,15 +2314,15 @@
       }
     },
     "helmet-csp": {
-      "version": "2.4.0",
-      "resolved": "https://registry.npmjs.org/helmet-csp/-/helmet-csp-2.4.0.tgz",
-      "integrity": "sha1-flOhVxZ6BkWq3XF30SrmxgXBhC4=",
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/helmet-csp/-/helmet-csp-2.5.0.tgz",
+      "integrity": "sha512-KdGChwze2cJke00PYAg75ztUqMoC8N5ez/Exz183xHsAT5MAHqQR1A5YnivBxkISDnT+fD38jtEEat1aH5QfYw==",
       "requires": {
         "camelize": "1.0.0",
         "content-security-policy-builder": "1.1.0",
         "dasherize": "2.0.0",
         "lodash.reduce": "4.6.0",
-        "platform": "1.3.3"
+        "platform": "1.3.4"
       }
     },
     "hide-powered-by": {
@@ -3386,15 +2336,15 @@
       "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
       "dev": true,
       "requires": {
-        "hash.js": "1.1.1",
+        "hash.js": "1.1.3",
         "minimalistic-assert": "1.0.0",
         "minimalistic-crypto-utils": "1.0.1"
       }
     },
     "hosted-git-info": {
-      "version": "2.4.2",
-      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.4.2.tgz",
-      "integrity": "sha1-AHa59GonBQbduq6lZJaJdGBhKmc=",
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz",
+      "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==",
       "dev": true
     },
     "hpkp": {
@@ -3403,17 +2353,14 @@
       "integrity": "sha1-EOFCJk52IVpdMMROxD3mTe5tFnI="
     },
     "hsts": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/hsts/-/hsts-2.0.0.tgz",
-      "integrity": "sha1-pSI0xgcN7PIUsra3C7FE0H5Hdsc=",
-      "requires": {
-        "core-util-is": "1.0.2"
-      }
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/hsts/-/hsts-2.1.0.tgz",
+      "integrity": "sha512-zXhh/DqgrTXJ7erTN6Fh5k/xjMhDGXCqdYN3wvxUvGUQvnxcFfUd8E+6vLg/nk3ss1TYMb+DhRl25fYABioTvA=="
     },
     "html-tags": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-1.2.0.tgz",
-      "integrity": "sha1-x43mW1Zjqll5id0rerSSANfk25g=",
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-2.0.0.tgz",
+      "integrity": "sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos=",
       "dev": true
     },
     "htmlescape": {
@@ -3460,6 +2407,12 @@
       "integrity": "sha1-QyNS5XrM2HqzEQ6C0/6g5HgSFW0=",
       "dev": true
     },
+    "immediate": {
+      "version": "3.0.6",
+      "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+      "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=",
+      "dev": true
+    },
     "imurmurhash": {
       "version": "0.1.4",
       "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
@@ -3519,13 +2472,13 @@
       }
     },
     "inquirer": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.1.1.tgz",
-      "integrity": "sha512-H50sHQwgvvaTBd3HpKMVtL/u6LoHDvYym51gd7bGQe/+9HkCE+J0/3N5FJLfd6O6oz44hHewC2Pc2LodzWVafQ==",
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.2.0.tgz",
+      "integrity": "sha512-4CyUYMP7lOBkiUU1rR24WGrfRX6SucwbY2Mqb1PdApU24wnTIk4TsnkQwV72dDdIKZ2ycLP+fWCV+tA7wwgoew==",
       "dev": true,
       "requires": {
         "ansi-escapes": "2.0.0",
-        "chalk": "1.1.3",
+        "chalk": "2.0.1",
         "cli-cursor": "2.1.0",
         "cli-width": "2.1.0",
         "external-editor": "2.0.4",
@@ -3535,9 +2488,55 @@
         "run-async": "2.3.0",
         "rx-lite": "4.0.8",
         "rx-lite-aggregates": "4.0.8",
-        "string-width": "2.0.0",
-        "strip-ansi": "3.0.1",
+        "string-width": "2.1.1",
+        "strip-ansi": "4.0.0",
         "through": "2.3.8"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+          "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+          "dev": true
+        },
+        "ansi-styles": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.1.0.tgz",
+          "integrity": "sha1-CcIC1ckX7CMYjKpcnLkXnNlUd1A=",
+          "dev": true,
+          "requires": {
+            "color-convert": "1.9.0"
+          }
+        },
+        "chalk": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.0.1.tgz",
+          "integrity": "sha512-Mp+FXEI+FrwY/XYV45b2YD3E8i3HwnEAoFcM0qlZzq/RZ9RwWitt2Y/c7cqRAz70U7hfekqx6qNYthuKFO6K0g==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "3.1.0",
+            "escape-string-regexp": "1.0.5",
+            "supports-color": "4.2.0"
+          }
+        },
+        "strip-ansi": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+          "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "3.0.0"
+          }
+        },
+        "supports-color": {
+          "version": "4.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.2.0.tgz",
+          "integrity": "sha512-Ts0Mu/A1S1aZxEJNG88I4Oc9rcZSBFNac5e27yh4j2mqbhZSSzR1Ah79EYwSn9Zuh7lrlGD2cVGzw1RKGzyLSg==",
+          "dev": true,
+          "requires": {
+            "has-flag": "2.0.0"
+          }
+        }
       }
     },
     "insert-module-globals": {
@@ -3576,7 +2575,8 @@
       "dev": true
     },
     "intl-pluralrules": {
-      "version": "github:projectfluent/IntlPluralRules#94cb0fa1c23ad943bc5aafef43cea132fa51d68b"
+      "version": "github:projectfluent/IntlPluralRules#94cb0fa1c23ad943bc5aafef43cea132fa51d68b",
+      "dev": true
     },
     "ipaddr.js": {
       "version": "1.3.0",
@@ -3584,9 +2584,9 @@
       "integrity": "sha1-HgOlL9rYOou7KyXL9JmLTP/NPew="
     },
     "irregular-plurals": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-1.2.0.tgz",
-      "integrity": "sha1-OPKZg0uowAwwvpxVThNyaXUv86w=",
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-1.3.0.tgz",
+      "integrity": "sha512-njf5A+Mxb3kojuHd1DzISjjIl+XhyzovXEOyPPSzdQozq/Lf2tN27mOrAAsxEPZxpn6I4MGzs1oo9TxXxPFpaA==",
       "dev": true
     },
     "is-arrayish": {
@@ -3595,15 +2595,6 @@
       "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
       "dev": true
     },
-    "is-binary-path": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
-      "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
-      "dev": true,
-      "requires": {
-        "binary-extensions": "1.8.0"
-      }
-    },
     "is-buffer": {
       "version": "1.1.5",
       "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz",
@@ -3687,18 +2678,6 @@
         "is-extglob": "1.0.0"
       }
     },
-    "is-my-json-valid": {
-      "version": "2.16.0",
-      "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz",
-      "integrity": "sha1-8Hndm/2uZe4gOKrorLyGqxCeNpM=",
-      "dev": true,
-      "requires": {
-        "generate-function": "2.0.0",
-        "generate-object-property": "1.2.0",
-        "jsonpointer": "4.0.1",
-        "xtend": "4.0.1"
-      }
-    },
     "is-number": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz",
@@ -3756,12 +2735,6 @@
       "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
       "dev": true
     },
-    "is-property": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
-      "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=",
-      "dev": true
-    },
     "is-regex": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
@@ -3804,20 +2777,16 @@
       "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=",
       "dev": true
     },
-    "is-windows": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.1.tgz",
-      "integrity": "sha1-MQ23D3QtJZoWo2kgK1GvhCMzENk="
-    },
     "isarray": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
-      "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+      "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
     },
     "isexe": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
-      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
+      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+      "dev": true
     },
     "isobject": {
       "version": "2.1.0",
@@ -3826,14 +2795,6 @@
       "dev": true,
       "requires": {
         "isarray": "1.0.0"
-      },
-      "dependencies": {
-        "isarray": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
-          "dev": true
-        }
       }
     },
     "jmespath": {
@@ -3844,12 +2805,14 @@
     "jquery": {
       "version": "3.2.1",
       "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.2.1.tgz",
-      "integrity": "sha1-XE2d5lKvbNCncBVKYxu6ErAVx4c="
+      "integrity": "sha1-XE2d5lKvbNCncBVKYxu6ErAVx4c=",
+      "dev": true
     },
     "jquery-circle-progress": {
       "version": "1.2.2",
       "resolved": "https://registry.npmjs.org/jquery-circle-progress/-/jquery-circle-progress-1.2.2.tgz",
       "integrity": "sha1-Jg6RMKyOK1Vy6qepO56Kaye8juo=",
+      "dev": true,
       "requires": {
         "jquery": "3.2.1"
       }
@@ -3861,25 +2824,31 @@
       "dev": true
     },
     "js-tokens": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.1.tgz",
-      "integrity": "sha1-COnxMkhKLEWjCQfp3E1VZ7fxFNc=",
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
+      "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
       "dev": true
     },
     "js-yaml": {
-      "version": "3.8.4",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.8.4.tgz",
-      "integrity": "sha1-UgtFZPhlc7qWZir4Woyvp7S1pvY=",
+      "version": "3.9.0",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.9.0.tgz",
+      "integrity": "sha512-0LoUNELX4S+iofCT8f4uEHIiRBR+c2AINyC8qRWfC6QNruLtxVZRJaPcu/xwMgFIgDxF25tGHaDjvxzJCNE9yw==",
       "dev": true,
       "requires": {
         "argparse": "1.0.9",
-        "esprima": "3.1.3"
+        "esprima": "4.0.0"
       }
     },
     "jschardet": {
-      "version": "1.4.2",
-      "resolved": "https://registry.npmjs.org/jschardet/-/jschardet-1.4.2.tgz",
-      "integrity": "sha1-KqEH8UKvQSHRRWWdRPUIMJYeaZo=",
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/jschardet/-/jschardet-1.5.0.tgz",
+      "integrity": "sha512-+Q8JsoEQbrdE+a/gg1F9XO92gcKXgpE5UACqr0sIubjDmBEkd+OOWPGzQeMrWSLxd73r4dHxBeRW7edHu5LmJQ==",
+      "dev": true
+    },
+    "json-schema-traverse": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
+      "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=",
       "dev": true
     },
     "json-stable-stringify": {
@@ -3914,6 +2883,12 @@
         "through2": "0.6.5"
       },
       "dependencies": {
+        "isarray": {
+          "version": "0.0.1",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+          "dev": true
+        },
         "jsonparse": {
           "version": "0.0.5",
           "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-0.0.5.tgz",
@@ -3982,12 +2957,6 @@
       "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=",
       "dev": true
     },
-    "jsonpointer": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz",
-      "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=",
-      "dev": true
-    },
     "JSONStream": {
       "version": "1.3.1",
       "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz",
@@ -3998,6 +2967,41 @@
         "through": "2.3.8"
       }
     },
+    "jszip": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.1.3.tgz",
+      "integrity": "sha1-ipIEA7KxZRwPwSa+kBktkICVfDc=",
+      "dev": true,
+      "requires": {
+        "core-js": "2.3.0",
+        "es6-promise": "3.0.2",
+        "lie": "3.1.1",
+        "pako": "1.0.5",
+        "readable-stream": "2.0.6"
+      },
+      "dependencies": {
+        "pako": {
+          "version": "1.0.5",
+          "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.5.tgz",
+          "integrity": "sha1-0iBd/ludqK95fnwWPbTR+E5GALw=",
+          "dev": true
+        },
+        "readable-stream": {
+          "version": "2.0.6",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz",
+          "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=",
+          "dev": true,
+          "requires": {
+            "core-util-is": "1.0.2",
+            "inherits": "2.0.3",
+            "isarray": "1.0.0",
+            "process-nextick-args": "1.0.7",
+            "string_decoder": "0.10.31",
+            "util-deprecate": "1.0.2"
+          }
+        }
+      }
+    },
     "kind-of": {
       "version": "3.2.2",
       "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
@@ -4016,6 +3020,7 @@
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/l20n/-/l20n-5.0.0.tgz",
       "integrity": "sha1-XfkVSyCgrPAdTIsMRa8wuXpzGjM=",
+      "dev": true,
       "requires": {
         "fluent": "0.3.1",
         "fluent-intl-polyfill": "0.1.0",
@@ -4031,6 +3036,14 @@
         "inherits": "2.0.3",
         "isarray": "0.0.1",
         "stream-splicer": "2.0.0"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "0.0.1",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+          "dev": true
+        }
       }
     },
     "lazy-cache": {
@@ -4048,6 +3061,12 @@
         "through2": "0.6.5"
       },
       "dependencies": {
+        "isarray": {
+          "version": "0.0.1",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+          "dev": true
+        },
         "readable-stream": {
           "version": "1.0.34",
           "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
@@ -4091,6 +3110,15 @@
         "astw": "2.2.0"
       }
     },
+    "lie": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz",
+      "integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=",
+      "dev": true,
+      "requires": {
+        "immediate": "3.0.6"
+      }
+    },
     "load-json-file": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
@@ -4221,6 +3249,7 @@
       "version": "4.1.1",
       "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz",
       "integrity": "sha1-Yi4y6CSItJJ5EUpPns9F581rulU=",
+      "dev": true,
       "requires": {
         "pseudomap": "1.0.2",
         "yallist": "2.1.2"
@@ -4244,9 +3273,9 @@
       "dev": true
     },
     "mathml-tag-names": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.0.0.tgz",
-      "integrity": "sha1-7uYVESorEn5w9VjWnJ6+FAdlA9c=",
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.0.1.tgz",
+      "integrity": "sha1-jUEmgWi/htEQK5gQnijlMeejRXg=",
       "dev": true
     },
     "media-typer": {
@@ -4265,7 +3294,7 @@
         "loud-rejection": "1.6.0",
         "map-obj": "1.0.1",
         "minimist": "1.2.0",
-        "normalize-package-data": "2.3.8",
+        "normalize-package-data": "2.4.0",
         "object-assign": "4.1.1",
         "read-pkg-up": "1.0.1",
         "redent": "1.0.0",
@@ -4434,6 +3463,12 @@
             "path-is-absolute": "1.0.1"
           }
         },
+        "has-flag": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
+          "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
+          "dev": true
+        },
         "ms": {
           "version": "0.7.2",
           "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz",
@@ -4466,7 +3501,7 @@
         "inherits": "2.0.3",
         "JSONStream": "1.3.1",
         "parents": "1.0.1",
-        "readable-stream": "2.3.2",
+        "readable-stream": "2.3.3",
         "resolve": "1.3.3",
         "stream-combiner2": "1.1.1",
         "subarg": "1.0.0",
@@ -4474,16 +3509,10 @@
         "xtend": "4.0.1"
       },
       "dependencies": {
-        "isarray": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
-          "dev": true
-        },
         "readable-stream": {
-          "version": "2.3.2",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.2.tgz",
-          "integrity": "sha1-WgTfBeT1f+Pw3Gj90R3FyXx+b00=",
+          "version": "2.3.3",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
+          "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
           "dev": true,
           "requires": {
             "core-util-is": "1.0.2",
@@ -4544,13 +3573,6 @@
       "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=",
       "dev": true
     },
-    "nan": {
-      "version": "2.6.2",
-      "resolved": "https://registry.npmjs.org/nan/-/nan-2.6.2.tgz",
-      "integrity": "sha1-5P805slf37WuzAjeZZb0NgWn20U=",
-      "dev": true,
-      "optional": true
-    },
     "native-promise-only": {
       "version": "0.8.1",
       "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz",
@@ -4574,12 +3596,12 @@
       "integrity": "sha1-ICtIAhoMTL3i34DeFaF0Q8i0OYA="
     },
     "normalize-package-data": {
-      "version": "2.3.8",
-      "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.3.8.tgz",
-      "integrity": "sha1-2Bntoqne29H/pWPqQHHZNngilbs=",
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
+      "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==",
       "dev": true,
       "requires": {
-        "hosted-git-info": "2.4.2",
+        "hosted-git-info": "2.5.0",
         "is-builtin-module": "1.0.0",
         "semver": "5.3.0",
         "validate-npm-package-license": "3.0.1"
@@ -4741,16 +3763,8 @@
     "os-tmpdir": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
-      "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
-    },
-    "outpipe": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/outpipe/-/outpipe-1.1.1.tgz",
-      "integrity": "sha1-UM+GFjZeh+Ax4ppeyTOaPaRyX6I=",
-      "dev": true,
-      "requires": {
-        "shell-quote": "1.6.1"
-      }
+      "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
+      "dev": true
     },
     "pako": {
       "version": "0.2.9",
@@ -4912,9 +3926,9 @@
       }
     },
     "platform": {
-      "version": "1.3.3",
-      "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.3.tgz",
-      "integrity": "sha1-ZGx3ARiZhwtqCQPnXpl+jlHadGE="
+      "version": "1.3.4",
+      "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.4.tgz",
+      "integrity": "sha1-bw+xftqqSPIUQrOpdcBjEw8cPr0="
     },
     "plur": {
       "version": "2.1.2",
@@ -4922,7 +3936,7 @@
       "integrity": "sha1-dIJFLBoPUI4+NE6uwxLJHCncZVo=",
       "dev": true,
       "requires": {
-        "irregular-plurals": "1.2.0"
+        "irregular-plurals": "1.3.0"
       }
     },
     "pluralize": {
@@ -4943,6 +3957,12 @@
         "supports-color": "3.2.3"
       },
       "dependencies": {
+        "has-flag": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
+          "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
+          "dev": true
+        },
         "source-map": {
           "version": "0.5.6",
           "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz",
@@ -5032,9 +4052,9 @@
       "dev": true
     },
     "prettier": {
-      "version": "1.4.4",
-      "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.4.4.tgz",
-      "integrity": "sha512-GuuPazIvjW1DG26yLQgO+nagmRF/h9M4RaCtZWqu/eFW7csdZkQEwPJUeXX10d+LzmCnR9DuIZndqIOn3p2YoA==",
+      "version": "1.5.3",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.5.3.tgz",
+      "integrity": "sha1-WdrcaDNF7GuI+IuU7Urn4do5S/4=",
       "dev": true
     },
     "process": {
@@ -5046,7 +4066,8 @@
     "process-nextick-args": {
       "version": "1.0.7",
       "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
-      "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M="
+      "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=",
+      "dev": true
     },
     "progress": {
       "version": "2.0.0",
@@ -5059,7 +4080,7 @@
       "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
       "integrity": "sha1-BktyYCsY+Q8pGSuLG8QY/9Hr078=",
       "requires": {
-        "asap": "2.0.5"
+        "asap": "2.0.6"
       }
     },
     "proxy-addr": {
@@ -5102,7 +4123,8 @@
     "pseudomap": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
-      "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
+      "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
+      "dev": true
     },
     "public-encrypt": {
       "version": "4.0.0",
@@ -5225,9 +4247,10 @@
       }
     },
     "raven-js": {
-      "version": "3.16.0",
-      "resolved": "https://registry.npmjs.org/raven-js/-/raven-js-3.16.0.tgz",
-      "integrity": "sha1-p5naT90ExjlD9n3rk9qg7P4QHqs="
+      "version": "3.17.0",
+      "resolved": "https://registry.npmjs.org/raven-js/-/raven-js-3.17.0.tgz",
+      "integrity": "sha1-d5RXrHkQUSw8LMm7bQqe61mpaew=",
+      "dev": true
     },
     "raw-body": {
       "version": "2.2.0",
@@ -5261,19 +4284,13 @@
       "integrity": "sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A=",
       "dev": true,
       "requires": {
-        "readable-stream": "2.3.2"
+        "readable-stream": "2.3.3"
       },
       "dependencies": {
-        "isarray": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
-          "dev": true
-        },
         "readable-stream": {
-          "version": "2.3.2",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.2.tgz",
-          "integrity": "sha1-WgTfBeT1f+Pw3Gj90R3FyXx+b00=",
+          "version": "2.3.3",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
+          "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
           "dev": true,
           "requires": {
             "core-util-is": "1.0.2",
@@ -5303,7 +4320,7 @@
       "dev": true,
       "requires": {
         "load-json-file": "2.0.0",
-        "normalize-package-data": "2.3.8",
+        "normalize-package-data": "2.4.0",
         "path-type": "2.0.0"
       }
     },
@@ -5348,7 +4365,7 @@
           "dev": true,
           "requires": {
             "load-json-file": "1.1.0",
-            "normalize-package-data": "2.3.8",
+            "normalize-package-data": "2.4.0",
             "path-type": "1.1.0"
           }
         },
@@ -5372,49 +4389,12 @@
         "inherits": "2.0.3",
         "isarray": "0.0.1",
         "string_decoder": "0.10.31"
-      }
-    },
-    "readdirp": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz",
-      "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=",
-      "dev": true,
-      "requires": {
-        "graceful-fs": "4.1.11",
-        "minimatch": "3.0.4",
-        "readable-stream": "2.3.2",
-        "set-immediate-shim": "1.0.1"
       },
       "dependencies": {
         "isarray": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
-          "dev": true
-        },
-        "readable-stream": {
-          "version": "2.3.2",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.2.tgz",
-          "integrity": "sha1-WgTfBeT1f+Pw3Gj90R3FyXx+b00=",
-          "dev": true,
-          "requires": {
-            "core-util-is": "1.0.2",
-            "inherits": "2.0.3",
-            "isarray": "1.0.0",
-            "process-nextick-args": "1.0.7",
-            "safe-buffer": "5.1.1",
-            "string_decoder": "1.0.3",
-            "util-deprecate": "1.0.2"
-          }
-        },
-        "string_decoder": {
-          "version": "1.0.3",
-          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
-          "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=",
-          "dev": true,
-          "requires": {
-            "safe-buffer": "5.1.1"
-          }
+          "version": "0.0.1",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
         }
       }
     },
@@ -5557,6 +4537,7 @@
       "version": "2.6.1",
       "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz",
       "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=",
+      "dev": true,
       "requires": {
         "glob": "7.1.2"
       },
@@ -5565,6 +4546,7 @@
           "version": "7.1.2",
           "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
           "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=",
+          "dev": true,
           "requires": {
             "fs.realpath": "1.0.0",
             "inflight": "1.0.6",
@@ -5613,7 +4595,8 @@
     "safe-buffer": {
       "version": "5.1.1",
       "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
-      "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM="
+      "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM=",
+      "dev": true
     },
     "safe-regex": {
       "version": "1.1.0",
@@ -5636,11 +4619,12 @@
       "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o="
     },
     "selenium-webdriver": {
-      "version": "3.4.0",
-      "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.4.0.tgz",
-      "integrity": "sha1-FR90RSlNpqZsScwwB0eioX5TxSo=",
+      "version": "3.5.0",
+      "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.5.0.tgz",
+      "integrity": "sha512-1bCZYRfDy7vsu1dkLrclTLvWPxSo6rOIkxZXvB2wnzeWkEoiTKpw612EUGA3jRZxPzAzI9OlxuULJV8ge1vVXQ==",
+      "dev": true,
       "requires": {
-        "adm-zip": "0.4.7",
+        "jszip": "3.1.3",
         "rimraf": "2.6.1",
         "tmp": "0.0.30",
         "xml2js": "0.4.17"
@@ -5650,9 +4634,7 @@
           "version": "0.0.30",
           "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz",
           "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=",
-          "requires": {
-            "os-tmpdir": "1.0.2"
-          }
+          "dev": true
         }
       }
     },
@@ -5693,12 +4675,6 @@
         "send": "0.15.3"
       }
     },
-    "set-immediate-shim": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz",
-      "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=",
-      "dev": true
-    },
     "setprototypeof": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz",
@@ -5727,6 +4703,7 @@
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
       "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+      "dev": true,
       "requires": {
         "shebang-regex": "1.0.0"
       }
@@ -5734,7 +4711,8 @@
     "shebang-regex": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
-      "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
+      "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
+      "dev": true
     },
     "shell-quote": {
       "version": "1.6.1",
@@ -5782,9 +4760,9 @@
       "dev": true
     },
     "sinon": {
-      "version": "2.3.5",
-      "resolved": "https://registry.npmjs.org/sinon/-/sinon-2.3.5.tgz",
-      "integrity": "sha1-mi/A/41SbacW8wlTqixl1RiRf2w=",
+      "version": "2.3.8",
+      "resolved": "https://registry.npmjs.org/sinon/-/sinon-2.3.8.tgz",
+      "integrity": "sha1-Md4G/tj7o6Zx5XbdltClhjeW8lw=",
       "dev": true,
       "requires": {
         "diff": "3.2.0",
@@ -5797,6 +4775,12 @@
         "type-detect": "4.0.3"
       },
       "dependencies": {
+        "isarray": {
+          "version": "0.0.1",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+          "dev": true
+        },
         "path-to-regexp": {
           "version": "1.7.0",
           "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz",
@@ -5844,9 +4828,9 @@
       "dev": true
     },
     "specificity": {
-      "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/specificity/-/specificity-0.3.0.tgz",
-      "integrity": "sha1-MyRy1OXrWvIIIRcZM5mKa8Oxzm8=",
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/specificity/-/specificity-0.3.1.tgz",
+      "integrity": "sha1-8bBoQkzjF64HR42V3jwhz4Xo1Wc=",
       "dev": true
     },
     "split": {
@@ -5867,6 +4851,12 @@
         "through2": "0.6.5"
       },
       "dependencies": {
+        "isarray": {
+          "version": "0.0.1",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+          "dev": true
+        },
         "readable-stream": {
           "version": "1.0.34",
           "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
@@ -5914,19 +4904,13 @@
       "dev": true,
       "requires": {
         "inherits": "2.0.3",
-        "readable-stream": "2.3.2"
+        "readable-stream": "2.3.3"
       },
       "dependencies": {
-        "isarray": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
-          "dev": true
-        },
         "readable-stream": {
-          "version": "2.3.2",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.2.tgz",
-          "integrity": "sha1-WgTfBeT1f+Pw3Gj90R3FyXx+b00=",
+          "version": "2.3.3",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
+          "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
           "dev": true,
           "requires": {
             "core-util-is": "1.0.2",
@@ -5965,19 +4949,13 @@
       "dev": true,
       "requires": {
         "duplexer2": "0.1.4",
-        "readable-stream": "2.3.2"
+        "readable-stream": "2.3.3"
       },
       "dependencies": {
-        "isarray": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
-          "dev": true
-        },
         "readable-stream": {
-          "version": "2.3.2",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.2.tgz",
-          "integrity": "sha1-WgTfBeT1f+Pw3Gj90R3FyXx+b00=",
+          "version": "2.3.3",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
+          "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
           "dev": true,
           "requires": {
             "core-util-is": "1.0.2",
@@ -6008,21 +4986,15 @@
       "requires": {
         "builtin-status-codes": "3.0.0",
         "inherits": "2.0.3",
-        "readable-stream": "2.3.2",
+        "readable-stream": "2.3.3",
         "to-arraybuffer": "1.0.1",
         "xtend": "4.0.1"
       },
       "dependencies": {
-        "isarray": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
-          "dev": true
-        },
         "readable-stream": {
-          "version": "2.3.2",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.2.tgz",
-          "integrity": "sha1-WgTfBeT1f+Pw3Gj90R3FyXx+b00=",
+          "version": "2.3.3",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
+          "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
           "dev": true,
           "requires": {
             "core-util-is": "1.0.2",
@@ -6052,19 +5024,13 @@
       "dev": true,
       "requires": {
         "inherits": "2.0.3",
-        "readable-stream": "2.3.2"
+        "readable-stream": "2.3.3"
       },
       "dependencies": {
-        "isarray": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
-          "dev": true
-        },
         "readable-stream": {
-          "version": "2.3.2",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.2.tgz",
-          "integrity": "sha1-WgTfBeT1f+Pw3Gj90R3FyXx+b00=",
+          "version": "2.3.3",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
+          "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
           "dev": true,
           "requires": {
             "core-util-is": "1.0.2",
@@ -6103,13 +5069,30 @@
       "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
     },
     "string-width": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.0.0.tgz",
-      "integrity": "sha1-Y1xUNsxypuDDh87KJ41OLuxSaH4=",
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+      "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
       "dev": true,
       "requires": {
         "is-fullwidth-code-point": "2.0.0",
-        "strip-ansi": "3.0.1"
+        "strip-ansi": "4.0.0"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+          "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+          "dev": true
+        },
+        "strip-ansi": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+          "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "3.0.0"
+          }
+        }
       }
     },
     "string.prototype.padend": {
@@ -6206,16 +5189,16 @@
       }
     },
     "stylelint": {
-      "version": "7.11.1",
-      "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-7.11.1.tgz",
-      "integrity": "sha1-yBbGWLr32eXRZ9gic/6tN8l65J0=",
+      "version": "7.13.0",
+      "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-7.13.0.tgz",
+      "integrity": "sha1-ER+Xttpy53XICADWu29fhpmXeF0=",
       "dev": true,
       "requires": {
         "autoprefixer": "6.7.7",
         "balanced-match": "0.4.2",
-        "chalk": "1.1.3",
+        "chalk": "2.0.1",
         "colorguard": "1.2.0",
-        "cosmiconfig": "2.1.3",
+        "cosmiconfig": "2.2.1",
         "debug": "2.6.7",
         "doiuse": "2.6.0",
         "execall": "1.0.0",
@@ -6223,13 +5206,13 @@
         "get-stdin": "5.0.1",
         "globby": "6.1.0",
         "globjoin": "0.1.4",
-        "html-tags": "1.2.0",
+        "html-tags": "2.0.0",
         "ignore": "3.3.3",
         "imurmurhash": "0.1.4",
         "known-css-properties": "0.2.0",
         "lodash": "4.17.4",
         "log-symbols": "1.0.2",
-        "mathml-tag-names": "2.0.0",
+        "mathml-tag-names": "2.0.1",
         "meow": "3.7.0",
         "micromatch": "2.3.11",
         "normalize-selector": "0.2.0",
@@ -6243,8 +5226,8 @@
         "postcss-selector-parser": "2.2.3",
         "postcss-value-parser": "3.3.0",
         "resolve-from": "3.0.0",
-        "specificity": "0.3.0",
-        "string-width": "2.0.0",
+        "specificity": "0.3.1",
+        "string-width": "2.1.1",
         "style-search": "0.1.0",
         "stylehacks": "2.3.2",
         "sugarss": "0.2.0",
@@ -6252,16 +5235,36 @@
         "table": "4.0.1"
       },
       "dependencies": {
+        "ansi-styles": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.1.0.tgz",
+          "integrity": "sha1-CcIC1ckX7CMYjKpcnLkXnNlUd1A=",
+          "dev": true,
+          "requires": {
+            "color-convert": "1.9.0"
+          }
+        },
         "balanced-match": {
           "version": "0.4.2",
           "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz",
           "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=",
           "dev": true
         },
+        "chalk": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.0.1.tgz",
+          "integrity": "sha512-Mp+FXEI+FrwY/XYV45b2YD3E8i3HwnEAoFcM0qlZzq/RZ9RwWitt2Y/c7cqRAz70U7hfekqx6qNYthuKFO6K0g==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "3.1.0",
+            "escape-string-regexp": "1.0.5",
+            "supports-color": "4.2.0"
+          }
+        },
         "glob": {
           "version": "7.1.2",
           "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
-          "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=",
+          "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
           "dev": true,
           "requires": {
             "fs.realpath": "1.0.0",
@@ -6290,6 +5293,15 @@
           "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
           "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
           "dev": true
+        },
+        "supports-color": {
+          "version": "4.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.2.0.tgz",
+          "integrity": "sha512-Ts0Mu/A1S1aZxEJNG88I4Oc9rcZSBFNac5e27yh4j2mqbhZSSzR1Ah79EYwSn9Zuh7lrlGD2cVGzw1RKGzyLSg==",
+          "dev": true,
+          "requires": {
+            "has-flag": "2.0.0"
+          }
         }
       }
     },
@@ -6329,6 +5341,7 @@
       "version": "3.5.2",
       "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.5.2.tgz",
       "integrity": "sha1-M2GjlxVnUEw1EGOr6q4PqiPb8/g=",
+      "dev": true,
       "requires": {
         "component-emitter": "1.2.1",
         "cookiejar": "2.1.1",
@@ -6342,15 +5355,11 @@
         "readable-stream": "2.3.3"
       },
       "dependencies": {
-        "isarray": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
-        },
         "readable-stream": {
           "version": "2.3.3",
           "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
           "integrity": "sha1-No8lEtefnUb9/HE0mueHi7weuVw=",
+          "dev": true,
           "requires": {
             "core-util-is": "1.0.2",
             "inherits": "2.0.3",
@@ -6365,6 +5374,7 @@
           "version": "1.0.3",
           "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
           "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=",
+          "dev": true,
           "requires": {
             "safe-buffer": "5.1.1"
           }
@@ -6375,6 +5385,7 @@
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/supertest/-/supertest-3.0.0.tgz",
       "integrity": "sha1-jUu2j9GDDuBwM7HFpamkAhyWUpY=",
+      "dev": true,
       "requires": {
         "methods": "1.1.2",
         "superagent": "3.5.2"
@@ -6425,9 +5436,36 @@
         "chalk": "1.1.3",
         "lodash": "4.17.4",
         "slice-ansi": "0.0.4",
-        "string-width": "2.0.0"
+        "string-width": "2.1.1"
+      },
+      "dependencies": {
+        "ajv": {
+          "version": "4.11.8",
+          "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz",
+          "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=",
+          "dev": true,
+          "requires": {
+            "co": "4.6.0",
+            "json-stable-stringify": "1.0.1"
+          }
+        },
+        "json-stable-stringify": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz",
+          "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=",
+          "dev": true,
+          "requires": {
+            "jsonify": "0.0.0"
+          }
+        }
       }
     },
+    "testpilot-ga": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/testpilot-ga/-/testpilot-ga-0.3.0.tgz",
+      "integrity": "sha512-z4PJbw3KK0R0iflA+u/3BhWZrtsLHLu+0rMviMd8H1wp8qPV0rggNBjsKckBJCcXq4uEjXETGZzApHH7Tovpzw==",
+      "dev": true
+    },
     "text-encoding": {
       "version": "0.6.4",
       "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz",
@@ -6452,20 +5490,14 @@
       "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=",
       "dev": true,
       "requires": {
-        "readable-stream": "2.3.2",
+        "readable-stream": "2.3.3",
         "xtend": "4.0.1"
       },
       "dependencies": {
-        "isarray": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
-          "dev": true
-        },
         "readable-stream": {
-          "version": "2.3.2",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.2.tgz",
-          "integrity": "sha1-WgTfBeT1f+Pw3Gj90R3FyXx+b00=",
+          "version": "2.3.3",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
+          "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
           "dev": true,
           "requires": {
             "core-util-is": "1.0.2",
@@ -6565,19 +5597,22 @@
       "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
       "dev": true
     },
-    "uglify-es": {
-      "version": "3.0.19",
-      "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.0.19.tgz",
-      "integrity": "sha1-U9RI3fWXcMHq++LiV6J+L0hR0OU=",
+    "uglify-js": {
+      "version": "2.8.29",
+      "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",
+      "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=",
+      "optional": true,
       "requires": {
-        "commander": "2.9.0",
-        "source-map": "0.5.6"
+        "source-map": "0.5.6",
+        "uglify-to-browserify": "1.0.2",
+        "yargs": "3.10.0"
       },
       "dependencies": {
         "source-map": {
           "version": "0.5.6",
           "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz",
-          "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI="
+          "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=",
+          "optional": true
         }
       }
     },
@@ -6587,6 +5622,43 @@
       "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=",
       "optional": true
     },
+    "uglifyify": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/uglifyify/-/uglifyify-4.0.3.tgz",
+      "integrity": "sha512-k+XKvVDmLOTVOS34sEBQdeUL0Od9zN0a1lzjJZMgkAaoNarskzFFWcpjpyh6lM7o9yRGNfbCIGdWOjIKZq6EAg==",
+      "dev": true,
+      "requires": {
+        "convert-source-map": "1.1.3",
+        "extend": "1.3.0",
+        "minimatch": "3.0.4",
+        "through": "2.3.8",
+        "uglify-es": "3.0.25"
+      },
+      "dependencies": {
+        "extend": {
+          "version": "1.3.0",
+          "resolved": "https://registry.npmjs.org/extend/-/extend-1.3.0.tgz",
+          "integrity": "sha1-0VFvsP9WJNLr+RI+odrFoZlABPg=",
+          "dev": true
+        },
+        "source-map": {
+          "version": "0.5.6",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz",
+          "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=",
+          "dev": true
+        },
+        "uglify-es": {
+          "version": "3.0.25",
+          "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.0.25.tgz",
+          "integrity": "sha512-UcW+mvxBCW0/3YxJRFl5w7Fsoz/GMx63aujVcLbeyfpLh8RcaEv/KjVUpl4/2YM7Zhscr5B68UCbOB67umG/EQ==",
+          "dev": true,
+          "requires": {
+            "commander": "2.9.0",
+            "source-map": "0.5.6"
+          }
+        }
+      }
+    },
     "umd": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.1.tgz",
@@ -6638,7 +5710,8 @@
     "util-deprecate": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
-      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
+      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+      "dev": true
     },
     "utils-merge": {
       "version": "1.0.0",
@@ -6674,25 +5747,11 @@
         "indexof": "0.0.1"
       }
     },
-    "watchify": {
-      "version": "3.9.0",
-      "resolved": "https://registry.npmjs.org/watchify/-/watchify-3.9.0.tgz",
-      "integrity": "sha1-8HX9LoqGrN6Eztum5cKgvt1SPZ4=",
-      "dev": true,
-      "requires": {
-        "anymatch": "1.3.0",
-        "browserify": "14.4.0",
-        "chokidar": "1.7.0",
-        "defined": "1.0.0",
-        "outpipe": "1.1.1",
-        "through2": "2.0.3",
-        "xtend": "4.0.1"
-      }
-    },
     "which": {
       "version": "1.2.14",
       "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz",
       "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=",
+      "dev": true,
       "requires": {
         "isexe": "2.0.0"
       }
@@ -6758,7 +5817,8 @@
     "yallist": {
       "version": "2.1.2",
       "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
-      "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
+      "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
+      "dev": true
     },
     "yargs": {
       "version": "3.10.0",
diff --git a/package.json b/package.json
index 3e3cfd54..ccc149da 100644
--- a/package.json
+++ b/package.json
@@ -1,44 +1,43 @@
 {
   "name": "firefox-send",
   "description": "File Sharing Experiment",
-  "version": "0.1.2",
+  "version": "0.2.1",
   "author": "Mozilla (https://mozilla.org)",
   "dependencies": {
-    "aws-sdk": "^2.62.0",
+    "aws-sdk": "^2.89.0",
     "body-parser": "^1.17.2",
     "bytes": "^2.5.0",
     "connect-busboy": "0.0.2",
     "convict": "^3.0.0",
-    "cross-env": "^5.0.1",
     "express": "^4.15.3",
     "express-handlebars": "^3.0.0",
-    "helmet": "^3.6.1",
-    "jquery": "^3.2.1",
-    "jquery-circle-progress": "^1.2.2",
-    "l20n": "^5.0.0",
+    "helmet": "^3.8.0",
     "mozlog": "^2.1.1",
     "raven": "^2.1.0",
-    "raven-js": "^3.16.0",
-    "redis": "^2.7.1",
-    "selenium-webdriver": "^3.4.0",
-    "supertest": "^3.0.0",
-    "uglify-es": "3.0.19"
+    "redis": "^2.7.1"
   },
   "devDependencies": {
     "browserify": "^14.4.0",
-    "eslint": "^4.0.0",
+    "eslint": "^4.3.0",
     "eslint-plugin-mocha": "^4.11.0",
-    "eslint-plugin-node": "^5.0.0",
+    "eslint-plugin-node": "^5.1.1",
     "eslint-plugin-security": "^1.4.0",
     "git-rev-sync": "^1.9.1",
+    "jquery": "^3.2.1",
+    "jquery-circle-progress": "^1.2.2",
+    "l20n": "^5.0.0",
     "mocha": "^3.4.2",
     "npm-run-all": "^4.0.2",
-    "prettier": "^1.4.4",
+    "prettier": "^1.5.3",
     "proxyquire": "^1.8.0",
-    "sinon": "^2.3.5",
-    "stylelint": "^7.11.0",
+    "raven-js": "^3.17.0",
+    "selenium-webdriver": "^3.5.0",
+    "sinon": "^2.3.8",
+    "stylelint": "^7.13.0",
     "stylelint-config-standard": "^16.0.0",
-    "watchify": "^3.9.0"
+    "supertest": "^3.0.0",
+    "testpilot-ga": "^0.3.0",
+    "uglifyify": "^4.0.3"
   },
   "engines": {
     "node": ">=8.0.0"
@@ -47,15 +46,20 @@
   "license": "MPL-2.0",
   "repository": "mozilla/send",
   "scripts": {
-    "predocker": "browserify frontend/src/main.js | uglifyjs > public/bundle.js && npm run version",
-    "dev": "npm run version && watchify frontend/src/main.js -o public/bundle.js -d | node server/server",
-    "format": "prettier '{frontend/src/,scripts/,server/,test/}*.js' 'public/*.css' --single-quote --write",
+    "build": "npm-run-all build:*",
+    "build:upload": "browserify frontend/src/upload.js -g uglifyify -o public/upload.js",
+    "build:download": "browserify frontend/src/download.js -g uglifyify -o public/download.js",
+    "build:version": "node scripts/version",
+    "build:l10n": "cp node_modules/l20n/dist/web/l20n.min.js public",
+    "dev": "npm run build && npm start",
+    "format": "prettier '{frontend/src/,scripts/,server/,test/**/}*.js' 'public/*.css' --single-quote --write",
     "lint": "npm-run-all lint:*",
     "lint:css": "stylelint 'public/*.css'",
     "lint:js": "eslint .",
     "start": "node server/server",
-    "test": "mocha test/unit && mocha test/server && npm run test-browser && node test/frontend/driver.js",
-    "test-browser": "browserify test/frontend/frontend.bundle.js -o test/frontend/bundle.js -d",
-    "version": "node scripts/version"
+    "test": "npm-run-all test:*",
+    "test:unit": "mocha test/unit",
+    "test:server": "mocha test/server",
+    "test:browser": "browserify test/frontend/frontend.bundle.js -o test/frontend/bundle.js -d && node test/frontend/driver.js"
   }
 }
diff --git a/public/download.js b/public/download.js
index 45997e2d..932d9e26 100644
--- a/public/download.js
+++ b/public/download.js
@@ -2,10 +2,10 @@
 window.Raven=require("raven-js"),window.Raven.config(window.dsn).install(),window.dsn=void 0;const testPilotGA=require("testpilot-ga");window.analytics=new testPilotGA({an:"Firefox Send",ds:"web",tid:window.trackerId});
 
 },{"raven-js":13,"testpilot-ga":17}],2:[function(require,module,exports){
-require("./common");const FileReceiver=require("./fileReceiver"),{notify:notify,findMetric:findMetric,sendEvent:sendEvent}=require("./utils"),bytes=require("bytes"),Storage=require("./storage"),storage=new Storage(localStorage),$=require("jquery");require("jquery-circle-progress");const Raven=window.Raven;$(document).ready(function(){$(".send-new").attr("href",window.location.origin),$(".send-new").click(function(e){e.preventDefault(),sendEvent("recipient","restarted",{cd2:"completed"}).then(()=>{location.href=e.currentTarget.href})}),$(".legal-links a, .social-links a, #dl-firefox").click(function(e){e.preventDefault();const t=findMetric(e.currentTarget.href);sendEvent("recipient","exited",{cd3:t}).then(()=>{location.href=e.currentTarget.href})}),$("#expired-send-new").click(function(){storage.referrer="errored-download"});const e=$("#dl-filename").html(),t=Number($("#dl-bytelength").text()),o=Number($("#dl-ttl").text());$("#dl-progress").circleProgress({value:0,startAngle:-Math.PI/2,fill:"#00C8D7",size:158,animation:{duration:300}}),$("#download-btn").click(function(){storage.totalDownloads+=1;const n=new FileReceiver,r=storage.numFiles;n.on("progress",o=>{window.onunload=function(){storage.referrer="cancelled-download",sendEvent("recipient","download-stopped",{cm1:t,cm5:storage.totalUploads,cm6:r,cm7:storage.totalDownloads,cd2:"cancelled"})},$("#download-page-one").attr("hidden",!0),$("#download-progress").removeAttr("hidden");const a=o[0]/o[1];$("#dl-progress").circleProgress("value",a),$(".percent-number").html(`${Math.floor(100*a)}`),$(".progress-text").text(`${e} (${bytes(o[0],{decimalPlaces:1,fixedDecimals:!0})} of ${bytes(o[1],{decimalPlaces:1})})`),1===a&&(n.removeAllListeners("progress"),document.l10n.formatValues("downloadNotification","downloadFinish").then(e=>{notify(e[0]),$(".title").html(e[1])}),window.onunload=null)});let a;n.on("decrypting",e=>{e?console.log("Decrypting"):(console.log("Done decrypting"),a=Date.now())}),n.on("hashing",e=>{e?console.log("Checking file integrity"):console.log("Integrity check done")});const l=Date.now();sendEvent("recipient","download-started",{cm1:t,cm4:o,cm5:storage.totalUploads,cm6:r,cm7:storage.totalDownloads}),n.download().catch(e=>{sendEvent("recipient","download-stopped",{cm1:t,cm5:storage.totalUploads,cm6:r,cm7:storage.totalDownloads,cd2:"errored",cd6:e}),document.l10n.formatValue("expiredPageHeader").then(e=>{$(".title").text(e)}),$("#download-btn").attr("hidden",!0),$("#expired-img").removeAttr("hidden"),console.log("The file has expired, or has already been deleted.")}).then(([e,o])=>{const n=Date.now(),d=n-l,c=t/((n-a)/1e3);storage.referrer="completed-download",sendEvent("recipient","download-stopped",{cm1:t,cm2:d,cm3:c,cm5:storage.totalUploads,cm6:r,cm7:storage.totalDownloads,cd2:"completed"});const i=new DataView(e),s=new Blob([i]),g=URL.createObjectURL(s),m=document.createElement("a");m.href=g,window.navigator.msSaveBlob?window.navigator.msSaveBlob(s,o):(m.download=o,document.body.appendChild(m),m.click())}).catch(e=>(Raven.captureException(e),Promise.reject(e)))})});
+require("./common");const FileReceiver=require("./fileReceiver"),{notify:notify,findMetric:findMetric,gcmCompliant:gcmCompliant,sendEvent:sendEvent}=require("./utils"),bytes=require("bytes"),Storage=require("./storage"),storage=new Storage(localStorage),$=require("jquery");require("jquery-circle-progress");const Raven=window.Raven;$(document).ready(function(){gcmCompliant().catch(e=>{$("#download").attr("hidden",!0),sendEvent("recipient","unsupported",{cd6:e}).then(()=>{location.replace("/unsupported")})}),$(".send-new").attr("href",window.location.origin),$(".send-new").click(function(e){e.preventDefault(),sendEvent("recipient","restarted",{cd2:"completed"}).then(()=>{location.href=e.currentTarget.href})}),$(".legal-links a, .social-links a, #dl-firefox").click(function(e){e.preventDefault();const t=findMetric(e.currentTarget.href);sendEvent("recipient","exited",{cd3:t}).then(()=>{location.href=e.currentTarget.href})});const e=$("#dl-filename").text(),t=Number($("#dl-bytelength").text()),o=Number($("#dl-ttl").text());$("#dl-progress").circleProgress({value:0,startAngle:-Math.PI/2,fill:"#3B9DFF",size:158,animation:{duration:300}}),$("#download-btn").click(function(){storage.totalDownloads+=1;const n=new FileReceiver,r=storage.numFiles;n.on("progress",o=>{window.onunload=function(){storage.referrer="cancelled-download",sendEvent("recipient","download-stopped",{cm1:t,cm5:storage.totalUploads,cm6:r,cm7:storage.totalDownloads,cd2:"cancelled"})},$("#download-page-one").attr("hidden",!0),$("#download-progress").removeAttr("hidden");const n=o[0]/o[1];$("#dl-progress").circleProgress("value",n),$(".percent-number").text(`${Math.floor(100*n)}`),$(".progress-text").text(`${e} (${bytes(o[0],{decimalPlaces:1,fixedDecimals:!0})} of ${bytes(o[1],{decimalPlaces:1})})`)});let a;n.on("decrypting",e=>{e?(n.removeAllListeners("progress"),window.onunload=null,document.l10n.formatValue("decryptingFile").then(e=>{$(".progress-text").text(e)})):(console.log("Done decrypting"),a=Date.now())}),n.on("hashing",e=>{e?document.l10n.formatValue("verifyingFile").then(e=>{$(".progress-text").text(e)}):($(".progress-text").text(" "),document.l10n.formatValues("downloadNotification","downloadFinish").then(e=>{notify(e[0]),$(".title").text(e[1])}))});const d=Date.now();sendEvent("recipient","download-started",{cm1:t,cm4:o,cm5:storage.totalUploads,cm6:r,cm7:storage.totalDownloads}),n.download().catch(e=>{sendEvent("recipient","download-stopped",{cm1:t,cm5:storage.totalUploads,cm6:r,cm7:storage.totalDownloads,cd2:"errored",cd6:e}),document.l10n.formatValue("expiredPageHeader").then(e=>{$(".title").text(e)}),$("#download-btn").attr("hidden",!0),$("#expired-img").removeAttr("hidden"),console.log("The file has expired, or has already been deleted.")}).then(([e,o])=>{const n=Date.now(),l=n-d,c=t/((n-a)/1e3);storage.referrer="completed-download",sendEvent("recipient","download-stopped",{cm1:t,cm2:l,cm3:c,cm5:storage.totalUploads,cm6:r,cm7:storage.totalDownloads,cd2:"completed"});const i=new DataView(e),s=new Blob([i]),m=URL.createObjectURL(s),p=document.createElement("a");p.href=m,window.navigator.msSaveBlob?window.navigator.msSaveBlob(s,o):(p.download=o,document.body.appendChild(p),p.click())}).catch(e=>(Raven.captureException(e),Promise.reject(e)))})});
 
 },{"./common":1,"./fileReceiver":3,"./storage":4,"./utils":5,"bytes":6,"jquery":9,"jquery-circle-progress":8}],3:[function(require,module,exports){
-const EventEmitter=require("events"),{hexToArray:hexToArray}=require("./utils");class FileReceiver extends EventEmitter{constructor(){super()}download(){return Promise.all([new Promise((e,t)=>{const r=new XMLHttpRequest;r.onprogress=(e=>{e.lengthComputable&&404!==e.target.status&&this.emit("progress",[e.loaded,e.total])}),r.onload=function(i){if(404===r.status)return void t(new Error("The file has expired, or has already been deleted."));const a=new Blob([this.response]),s=new FileReader;s.onload=function(){const t=JSON.parse(r.getResponseHeader("X-File-Metadata"));e({data:this.result,aad:t.aad,filename:t.filename,iv:t.id})},s.readAsArrayBuffer(a)},r.open("get","/assets"+location.pathname.slice(0,-1),!0),r.responseType="blob",r.send()}),window.crypto.subtle.importKey("jwk",{kty:"oct",k:location.hash.slice(1),alg:"A128GCM",ext:!0},{name:"AES-GCM"},!0,["encrypt","decrypt"])]).then(([e,t])=>(this.emit("decrypting",!0),Promise.all([window.crypto.subtle.decrypt({name:"AES-GCM",iv:hexToArray(e.iv),additionalData:hexToArray(e.aad)},t,e.data).then(e=>(this.emit("decrypting",!1),Promise.resolve(e))),e.filename,hexToArray(e.aad)]))).then(([e,t,r])=>(this.emit("hashing",!0),window.crypto.subtle.digest("SHA-256",e).then(i=>(this.emit("hashing",!1),new Uint8Array(i).toString()===r.toString()?(this.emit("safe",!0),Promise.all([e,decodeURIComponent(t)])):(this.emit("unsafe",!0),Promise.reject())))))}}module.exports=FileReceiver;
+const EventEmitter=require("events"),{hexToArray:hexToArray}=require("./utils");class FileReceiver extends EventEmitter{constructor(){super()}download(){return Promise.all([new Promise((e,t)=>{const r=new XMLHttpRequest;r.onprogress=(e=>{e.lengthComputable&&404!==e.target.status&&this.emit("progress",[e.loaded,e.total])}),r.onload=function(a){if(404===r.status)return void t(new Error("The file has expired, or has already been deleted."));const i=new Blob([this.response]),s=new FileReader;s.onload=function(){const t=JSON.parse(r.getResponseHeader("X-File-Metadata"));e({data:this.result,aad:t.aad,filename:t.filename,iv:t.id})},s.readAsArrayBuffer(i)},r.open("get","/assets"+location.pathname.slice(0,-1),!0),r.responseType="blob",r.send()}),window.crypto.subtle.importKey("jwk",{kty:"oct",k:location.hash.slice(1),alg:"A128GCM",ext:!0},{name:"AES-GCM"},!0,["encrypt","decrypt"])]).then(([e,t])=>(this.emit("decrypting",!0),Promise.all([window.crypto.subtle.decrypt({name:"AES-GCM",iv:hexToArray(e.iv),additionalData:hexToArray(e.aad),tagLength:128},t,e.data).then(e=>(this.emit("decrypting",!1),Promise.resolve(e))),e.filename,hexToArray(e.aad)]))).then(([e,t,r])=>(this.emit("hashing",!0),window.crypto.subtle.digest("SHA-256",e).then(a=>(this.emit("hashing",!1),new Uint8Array(a).toString()===r.toString()?(this.emit("safe",!0),Promise.all([e,decodeURIComponent(t)])):(this.emit("unsafe",!0),Promise.reject())))))}}module.exports=FileReceiver;
 
 },{"./utils":5,"events":7}],4:[function(require,module,exports){
 const{isFile:isFile}=require("./utils");class Storage{constructor(e){this.engine=e}get totalDownloads(){return Number(this.engine.getItem("totalDownloads"))}set totalDownloads(e){this.engine.setItem("totalDownloads",e)}get totalUploads(){return Number(this.engine.getItem("totalUploads"))}set totalUploads(e){this.engine.setItem("totalUploads",e)}get referrer(){return this.engine.getItem("referrer")}set referrer(e){this.engine.setItem("referrer",e)}get files(){const e=[];for(let t=0;t<this.engine.length;t++){const n=this.engine.key(t);isFile(n)&&e.push(JSON.parse(this.engine.getItem(n)))}return e}get numFiles(){let e=0;for(let t=0;t<this.engine.length;t++){const n=this.engine.key(t);isFile(n)&&(e+=1)}return e}getFileById(e){return this.engine.getItem(e)}has(e){return this.engine.hasOwnProperty(e)}remove(e){this.engine.removeItem(e)}addFile(e,t){this.engine.setItem(e,JSON.stringify(t))}}module.exports=Storage;
diff --git a/public/locales/en-US/send.ftl b/public/locales/en-US/send.ftl
index 85dd3144..a6c75083 100644
--- a/public/locales/en-US/send.ftl
+++ b/public/locales/en-US/send.ftl
@@ -1,5 +1,7 @@
 // Firefox Send is a brand name and should not be localized.
 title = Firefox Send
+siteSubtitle = web experiment
+siteFeedback = Feedback
 uploadPageHeader = Private, Encrypted File Sharing
 uploadPageExplainer = Send files through a safe, private, and encrypted link that automatically expires to ensure your stuff does not remain online forever.
 uploadPageLearnMore = Learn more
@@ -10,6 +12,10 @@ uploadPageBrowseButton = Select a file on your computer
 uploadPageMultipleFilesAlert = Uploading multiple files or a folder is currently not supported.
 uploadPageBrowseButtonTitle = Upload file
 uploadingPageHeader = Uploading Your File
+importingFile = Importing...
+verifyingFile = Verifying...
+encryptingFile = Encrypting...
+decryptingFile = Decrypting...
 notifyUploadDone = Your upload has finished.
 uploadingPageMessage = Once your file uploads you will be able to set expiry options.
 uploadingPageCancel = Cancel upload
@@ -54,8 +60,8 @@ errorAltText
 errorPageHeader = Something went wrong!
 errorPageMessage = There has been an error uploading the file.
 errorPageLink = Send another file
-linkExpiredAlt
-    .alt = Link expired
+fileTooBig = That file is too big to upload. It should be less than { $size }.
+linkExpiredAlt.alt = Link expired
 expiredPageHeader = This link has expired or never existed in the first place!
 notSupportedHeader = Your browser is not supported.
 // Firefox Send is a brand name and should not be localized.
@@ -67,6 +73,16 @@ copyFileList = Copy URL
 expiryFileList = Expires In
 deleteFileList = Delete
 nevermindButton = Never mind
+deleteButtonHover
+    .title = Delete
+copyUrlHover
+    .title = Copy URL
+legalHeader = Terms & Privacy
+legalNoticeTestPilot = Firefox Send is currently a Test Pilot experiment, and subject to the Test Pilot <a>Terms of Service</a> and <a>Privacy Notice</a>. You can learn more about this experiment and its data collection <a>here</a>.
+legalNoticeMozilla = Use of the Firefox Send website is also subject to Mozilla’s <a>Websites Privacy Notice</a> and <a>Websites Terms of Use</a>.
+deletePopupText = Delete this file?
+deletePopupYes = Yes
+deletePopupCancel = Cancel
 deleteButtonHover
     .title = Delete
 copyUrlHover
diff --git a/public/main.css b/public/main.css
index e1a1bf7b..5677378b 100644
--- a/public/main.css
+++ b/public/main.css
@@ -1,7 +1,12 @@
 /***     index.html     ***/
 html {
   background: url('resources/send_bg.svg');
-  font-family: 'SF Pro Text', sans-serif;
+  font-family: -apple-system,
+    BlinkMacSystemFont,
+    'SF Pro Text',
+    Helvetica,
+    Arial,
+    sans-serif;
   font-weight: 200;
   background-size: 100%;
   background-repeat: no-repeat;
@@ -10,24 +15,95 @@ html {
 }
 
 body {
-  min-height: 100%;
-  position: relative;
+  display: flex;
+  flex-direction: column;
   margin: 0;
+  min-height: 100vh;
+  position: relative;
+}
+
+.header {
+  align-items: flex-start;
+  box-sizing: border-box;
+  display: flex;
+  justify-content: space-between;
+  padding: 31px;
+  width: 100%;
 }
 
 .send-logo {
+  display: flex;
   position: relative;
-  top: 31px;
-  left: 31px;
-  display: inline-block;
+  align-items: center;
+}
+
+.site-title {
+  color: #3e3d40;
+  font-size: 32px;
+  font-weight: 500;
+  margin: 0;
+  position: relative;
+  top: -1px;
+}
+
+.site-subtitle {
+  color: #3e3d40;
+  font-size: 12px;
+  margin: 0 8px;
+}
+
+.site-subtitle a {
+  font-weight: bold;
+  color: #3e3d40;
+  transition: color 50ms;
+}
+
+.send-logo:hover a {
+  color: #0297f8;
+}
+
+.feedback {
+  background-color: #0297f8;
+  background-image: url('resources/feedback.svg');
+  background-position: 4px 6px;
+  background-repeat: no-repeat;
+  background-size: 14px;
+  border-radius: 3px;
+  border: 1px solid #0297f8;
+  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
+  color: #fff;
+  cursor: pointer;
+  display: block;
+  float: right;
+  font-size: 12px;
+  line-height: 12px;
+  opacity: 0.9;
+  padding: 6px 6px 5px 20px;
+}
+
+.feedback:hover,
+.feedback:focus {
+  background-color: #0287e8;
+}
+
+.feedback:active {
+  background-color: #0277d8;
 }
 
 .all {
-  padding-top: 10%;
-  padding-bottom: 51px;
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  justify-content: flex-start;
+  max-width: 630px;
+  margin: 0 auto;
+  width: 96%;
 }
 
-input, select, textarea, button {
+input,
+select,
+textarea,
+button {
   font-family: inherit;
 }
 
@@ -38,24 +114,26 @@ a {
 /**   page-one    **/
 .title {
   font-size: 33px;
+  line-height: 40px;
   margin: 20px auto;
   text-align: center;
+  max-width: 520px;
   font-family: 'SF Pro Display', sans-serif;
 }
 
 .description {
   font-size: 15px;
   line-height: 23px;
-  width: 630px;
+  max-width: 630px;
   text-align: center;
   margin: 0 auto 60px;
-  color: #0C0C0D;
+  color: #0c0c0d;
+  width: 92%;
 }
 
 .upload-window {
   border: 1px dashed rgba(0, 148, 251, 0.5);
   margin: 0 auto;
-  width: 640px;
   height: 255px;
   border-radius: 4px;
   display: flex;
@@ -63,14 +141,15 @@ a {
   align-items: center;
   flex-direction: column;
   text-align: center;
+  transition: transform 150ms;
+  padding: 15px;
 }
 
 .upload-window.ondrag {
   border: 3px dashed rgba(0, 148, 251, 0.5);
   margin: 0 auto;
-  width: 636px;
   height: 251px;
-  transform: scale(1.05);
+  transform: scale(1.04);
   border-radius: 4.2px;
   display: flex;
   justify-content: center;
@@ -80,7 +159,7 @@ a {
 }
 
 .link {
-  color: #0094FB;
+  color: #0094fb;
   text-decoration: none;
 }
 
@@ -92,10 +171,10 @@ a {
 }
 
 #browse {
-  background: #0297F8;
+  background: #0297f8;
   border-radius: 5px;
   font-size: 15px;
-  color: #FFF;
+  color: #fff;
   width: 240px;
   height: 44px;
   display: flex;
@@ -104,12 +183,17 @@ a {
   cursor: pointer;
 }
 
+#browse:hover {
+  background-color: #0287e8;
+}
+
 input[type="file"] {
   display: none;
 }
 
 #file-size-msg {
   font-size: 12px;
+  line-height: 16px;
   color: #737373;
   margin-bottom: 22px;
 }
@@ -129,9 +213,10 @@ th {
 td {
   font-size: 15px;
   vertical-align: top;
-  color: #4A4A4A;
+  color: #4a4a4a;
   padding: 17px 19px 0;
   line-height: 23px;
+  position: relative;
 }
 
 table {
@@ -141,42 +226,44 @@ table {
 
 tbody {
   word-wrap: break-word;
+  word-break: break-all;
 }
 
 #uploaded-files {
-  width: 640px;
   margin: 45.3px auto;
   table-layout: fixed;
 }
 
-.icon-delete, .icon-copy, .icon-check {
+.icon-delete,
+.icon-copy,
+.icon-check {
   cursor: pointer;
 }
 
 /* Popup container */
 .popup {
-  position: relative;
+  position: absolute;
   display: inline-block;
-  cursor: pointer;
 }
 
 /* The actual popup (appears on top) */
 .popup .popuptext {
   visibility: hidden;
-  width: 160px;
-  background-color: #555;
-  color: #FFF;
+  min-width: 115px;
+  background-color: #fff;
+  color: #000;
+  border: 1px solid #0297f8;
   text-align: center;
-  border-radius: 6px;
-  padding: 8px 0;
+  border-radius: 5px;
+  padding: 7px 8px;
   position: absolute;
   z-index: 1;
-  bottom: 20px;
-  left: 50%;
-  margin-left: -88px;
+  bottom: 8px;
+  right: -28px;
   transition: opacity 0.5s;
   opacity: 0;
   outline: 0;
+  box-shadow: 3px 3px 7px #888;
 }
 
 /* Popup arrow */
@@ -184,11 +271,11 @@ tbody {
   content: "";
   position: absolute;
   top: 100%;
-  left: 50%;
+  right: 30px;
   margin-left: -5px;
   border-width: 5px;
   border-style: solid;
-  border-color: #555 transparent transparent;
+  border-color: #0297f8 transparent transparent;
 }
 
 .popup .show {
@@ -196,6 +283,29 @@ tbody {
   opacity: 1;
 }
 
+.popup-message {
+  margin-bottom: 4px;
+}
+
+.popup-yes {
+  color: #fff;
+  background-color: #0297f8;
+  border-radius: 5px;
+  padding: 2px 11px;
+  cursor: pointer;
+}
+
+.popup-yes:hover {
+  background-color: #0287e8;
+}
+
+.popup-no {
+  color: #4a4a4a;
+  border-radius: 6px;
+  padding: 3px 5px;
+  cursor: pointer;
+}
+
 /**   upload-progress   **/
 .progress-bar {
   margin-top: 3px;
@@ -239,14 +349,13 @@ tbody {
 }
 
 #cancel-upload {
-  color: #D70022;
+  color: #d70022;
   cursor: pointer;
   text-decoration: underline;
 }
 
 /**   share-link    **/
 #share-window {
-  width: 645px;
   margin: 0 auto;
   display: flex;
   justify-content: center;
@@ -262,53 +371,59 @@ tbody {
 #copy {
   display: flex;
   flex-wrap: nowrap;
+  width: 640px;
 }
 
 #copy-text {
   align-self: flex-start;
   margin-top: 60px;
   margin-bottom: 10px;
-  color: #0C0C0D;
+  color: #0c0c0d;
 }
 
 #link {
-  width: 480px;
+  flex: 1;
   height: 56px;
-  border: 1px solid #0297F8;
+  border: 1px solid #0297f8;
   border-radius: 6px 0 0 6px;
   font-size: 24px;
   color: #737373;
   font-family: 'SF Pro Display', sans-serif;
   letter-spacing: 0;
   line-height: 23px;
+  padding-left: 5px;
+  padding-right: 5px;
 }
 
 #link:disabled {
-  border: 1px solid #05A700;
-  background: #FFF;
+  border: 1px solid #05a700;
+  background: #fff;
 }
 
 #copy-btn {
-  width: 165px;
-  height: 60px;
-  background: #0297F8;
-  border: 1px solid #0297F8;
+  flex: 0 1 165px;
+  background: #0297f8;
   border-radius: 0 6px 6px 0;
+  border: 1px solid #0297f8;
   color: white;
   cursor: pointer;
   font-size: 15px;
+  height: 60px;
+  padding-left: 10px;
+  padding-right: 10px;
+  white-space: nowrap;
 }
 
 #copy-btn:disabled {
-  background: #05A700;
-  border: 1px solid #05A700;
+  background: #05a700;
+  border: 1px solid #05a700;
   cursor: auto;
 }
 
 #delete-file {
   width: 176px;
   height: 44px;
-  background: #FFF;
+  background: #fff;
   border: 1px solid rgba(12, 12, 13, 0.3);
   border-radius: 5px;
   font-size: 15px;
@@ -322,7 +437,7 @@ tbody {
   font-size: 15px;
   margin: auto;
   text-align: center;
-  color: #0094FB;
+  color: #0094fb;
   cursor: pointer;
   text-decoration: underline;
 }
@@ -336,7 +451,8 @@ tbody {
   text-align: center;
 }
 
-#upload-error[hidden], #unsupported-browser[hidden] {
+#upload-error[hidden],
+#unsupported-browser[hidden] {
   display: none;
 }
 
@@ -356,9 +472,8 @@ tbody {
 .unsupported-description {
   font-size: 13px;
   line-height: 23px;
-  width: 630px;
   text-align: center;
-  color: #7D7D7D;
+  color: #7d7d7d;
   margin: 0 auto 23px;
 }
 
@@ -370,14 +485,14 @@ tbody {
   margin-bottom: 181px;
   width: 260px;
   height: 80px;
-  background: #12BC00;
+  background: #12bc00;
   border-radius: 3px;
   cursor: pointer;
   border: 0;
   box-shadow: 0 5px 3px rgb(234, 234, 234);
   font-family: 'Fira Sans';
   font-weight: 500;
-  color: #FFF;
+  color: #fff;
   font-size: 26px;
   display: flex;
   justify-content: center;
@@ -406,15 +521,15 @@ tbody {
   margin-top: 20px;
   margin-bottom: 30px;
   text-align: center;
-  background: #0297F8;
-  border: 1px solid #0297F8;
+  background: #0297f8;
+  border: 1px solid #0297f8;
   border-radius: 5px;
   font-weight: 300;
   cursor: pointer;
 }
 
 #download-btn:disabled {
-  background: #47B04B;
+  background: #47b04b;
   cursor: auto;
 }
 
@@ -434,9 +549,8 @@ tbody {
 .expired-description {
   font-size: 15px;
   line-height: 23px;
-  width: 630px;
   text-align: center;
-  color: #7D7D7D;
+  color: #7d7d7d;
   margin: 0 auto 23px;
 }
 
@@ -460,14 +574,13 @@ tbody {
 
 /*    footer    */
 .footer {
-  position: absolute;
   right: 0;
   bottom: 0;
   left: 0;
   font-size: 15px;
   display: flex;
   align-items: flex-end;
-  padding: 10px;
+  padding: 50px 10px 10px;
 }
 
 .mozilla-logo {
@@ -495,8 +608,69 @@ tbody {
   margin-left: 30px;
 }
 
-.github, .twitter {
+.github,
+.twitter {
   width: 32px;
   height: 32px;
   margin-bottom: -5px;
 }
+
+@media (max-device-width: 768px) {
+  .description {
+    margin: 0 auto 25px;
+  }
+
+  #copy {
+    width: 100%;
+  }
+
+  #link {
+    font-size: 18px;
+  }
+
+  .mozilla-logo {
+    margin-left: -7px;
+  }
+
+  .legal-links > * {
+    display: block;
+    padding: 10px 0;
+  }
+}
+
+@media (max-device-width: 520px) {
+  .header {
+    flex-direction: column;
+    justify-content: flex-start;
+  }
+
+  .feedback {
+    margin-top: 10px;
+  }
+
+  #copy {
+    width: 100%;
+    flex-direction: column;
+  }
+
+  #link {
+    font-size: 22px;
+    padding: 15px 10px;
+    border-radius: 6px 6px 0 0;
+  }
+
+  #copy-btn {
+    border-radius: 0 0 6px 6px;
+    flex: 0 1 65px;
+  }
+
+  th {
+    font-size: 14px;
+    padding: 0 5px;
+  }
+
+  td {
+    font-size: 13px;
+    padding: 17px 5px 0;
+  }
+}
diff --git a/public/resources/feedback.svg b/public/resources/feedback.svg
new file mode 100644
index 00000000..1e3e3fe5
--- /dev/null
+++ b/public/resources/feedback.svg
@@ -0,0 +1 @@
+<svg width="15" height="13" viewBox="0 0 15 13" xmlns="http://www.w3.org/2000/svg"><title>Combined Shape</title><path d="M10.274 9.193a5.957 5.957 0 0 1-2.98.778C4.37 9.97 2 7.963 2 5.485 2 3.008 4.37 1 7.294 1c2.924 0 5.294 2.008 5.294 4.485 0 .843-.274 1.632-.751 2.305l.577 2.21-2.14-.807zm-5.983-2.96a.756.756 0 0 0 .763-.748.756.756 0 0 0-.763-.747.756.756 0 0 0-.764.747c0 .413.342.748.764.748zm3.054 0a.756.756 0 0 0 .764-.748.756.756 0 0 0-.764-.747.756.756 0 0 0-.764.747c0 .413.342.748.764.748zm3.054 0a.756.756 0 0 0 .764-.748.756.756 0 0 0-.764-.747.756.756 0 0 0-.763.747c0 .413.342.748.763.748z" fill="#FFF" fill-rule="evenodd"/></svg>
\ No newline at end of file
diff --git a/public/resources/send_logo_type.svg b/public/resources/send_logo_type.svg
deleted file mode 100644
index 07091885..00000000
--- a/public/resources/send_logo_type.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg width="180" height="25" viewBox="0 0 180 25" xmlns="http://www.w3.org/2000/svg"><title>logo design_01 copy</title><path d="M82.652.6h5.408v1.299h-3.89v2.446h3.556v1.252H84.17v3.407h-1.517V.6zm7.025.772a.733.733 0 0 1-.253-.574c0-.233.084-.424.253-.574A.938.938 0 0 1 90.323 0c.267 0 .483.075.65.224a.74.74 0 0 1 .25.574.736.736 0 0 1-.25.574c-.167.15-.383.224-.65.224a.938.938 0 0 1-.646-.224zm1.375 1.26v6.372H89.6V2.633h1.452zm3.21 0v.99h.1c.058-.209.17-.396.332-.561.162-.165.363-.295.602-.39.24-.096.494-.143.764-.143.11 0 .222.006.336.017.113.012.203.027.27.047v1.34a1.703 1.703 0 0 0-.32-.056 3.936 3.936 0 0 0-.427-.026c-.313 0-.591.061-.834.183a1.364 1.364 0 0 0-.568.519 1.46 1.46 0 0 0-.202.771v3.681h-1.458V2.633h1.405zm8.488 5.621a2.512 2.512 0 0 1-.985.652 3.77 3.77 0 0 1-1.349.228c-.627 0-1.168-.132-1.622-.396A2.615 2.615 0 0 1 97.747 7.6c-.242-.493-.364-1.08-.364-1.76 0-.679.122-1.269.367-1.77.245-.5.595-.887 1.05-1.159.454-.272.99-.408 1.604-.408.608 0 1.132.129 1.573.385a2.5 2.5 0 0 1 1.01 1.11c.234.483.35 1.057.35 1.72v.466h-4.49v.076c.011.35.084.654.217.915.133.26.32.46.559.6.239.14.517.21.834.21.36 0 .664-.064.908-.19a1.12 1.12 0 0 0 .527-.545h1.38c-.09.385-.264.719-.522 1.002zm-3.122-4.409a1.43 1.43 0 0 0-.543.545 1.841 1.841 0 0 0-.232.812h3.027a1.811 1.811 0 0 0-.2-.818 1.333 1.333 0 0 0-.517-.542 1.506 1.506 0 0 0-.764-.19 1.54 1.54 0 0 0-.77.193zm4.591-.017V2.673h1.052V2.05c0-.637.18-1.1.538-1.392.359-.291.899-.437 1.62-.437.223 0 .468.022.735.064v1.078a3.028 3.028 0 0 0-.559-.047c-.306 0-.532.068-.679.204-.147.136-.22.35-.22.64v.513h1.399v1.154h-1.382v5.177h-1.452V3.827h-1.052zm6.175 4.91a2.615 2.615 0 0 1-1.067-1.145c-.247-.499-.37-1.09-.37-1.773 0-.68.124-1.27.373-1.768a2.646 2.646 0 0 1 1.073-1.148c.466-.266 1.015-.399 1.646-.399.635 0 1.184.133 1.649.4.464.265.82.647 1.07 1.144.248.497.373 1.087.373 1.77 0 .684-.125 1.275-.373 1.774-.25.499-.606.88-1.07 1.145-.465.264-1.014.396-1.65.396-.638 0-1.19-.132-1.654-.396zm2.834-1.366c.276-.367.414-.885.414-1.552 0-.664-.14-1.18-.417-1.55-.279-.369-.67-.553-1.176-.553-.51 0-.903.184-1.182.553-.278.37-.417.886-.417 1.55 0 .667.14 1.185.417 1.552.279.367.673.55 1.182.55.51 0 .902-.183 1.179-.55zm4.14 1.633H115.8l2.081-3.162-2.098-3.21h1.687l1.281 2.19h.1l1.27-2.19h1.604l-2.075 3.152 2.099 3.22h-1.646l-1.317-2.195h-.1l-1.316 2.195zm11.14-7.105h-2.61v-1.3h6.742v1.3h-2.616v7.105h-1.516V1.9zm9.693 6.354a2.512 2.512 0 0 1-.985.652 3.77 3.77 0 0 1-1.349.228c-.627 0-1.168-.132-1.622-.396a2.615 2.615 0 0 1-1.047-1.136c-.243-.493-.364-1.08-.364-1.76 0-.679.122-1.269.367-1.77.245-.5.595-.887 1.05-1.159.454-.272.99-.408 1.604-.408.608 0 1.132.129 1.573.385a2.5 2.5 0 0 1 1.01 1.11c.234.483.35 1.057.35 1.72v.466h-4.49v.076c.011.35.084.654.217.915.133.26.32.46.559.6.239.14.517.21.834.21.36 0 .663-.064.908-.19s.42-.308.526-.545h1.382c-.09.385-.265.719-.523 1.002zm-3.122-4.409a1.43 1.43 0 0 0-.543.545 1.841 1.841 0 0 0-.232.812h3.027a1.811 1.811 0 0 0-.2-.818 1.333 1.333 0 0 0-.517-.542 1.506 1.506 0 0 0-.765-.19 1.54 1.54 0 0 0-.77.193zm5.352-.402c.218-.295.522-.525.912-.69.39-.165.835-.248 1.337-.248.505 0 .947.072 1.325.216.378.144.675.352.89.626.216.274.342.603.377.987h-1.37a.848.848 0 0 0-.396-.538c-.21-.13-.483-.196-.82-.196-.22 0-.418.033-.594.1a1.013 1.013 0 0 0-.417.273.593.593 0 0 0-.153.402c0 .179.077.326.232.443.155.116.395.211.72.285l1.135.256c.619.14 1.073.348 1.363.624.29.275.435.646.435 1.112 0 .4-.116.754-.35 1.063-.233.309-.557.548-.972.72-.416.17-.888.256-1.417.256-.533 0-.997-.073-1.393-.219-.396-.145-.708-.357-.938-.635a1.775 1.775 0 0 1-.396-.993h1.44c.07.241.218.426.444.556.225.13.518.196.878.196.243 0 .459-.033.647-.1a1.01 1.01 0 0 0 .44-.279.608.608 0 0 0 .16-.414.553.553 0 0 0-.215-.448c-.143-.116-.367-.208-.673-.274l-1.14-.262c-.62-.14-1.077-.356-1.373-.65-.296-.292-.444-.678-.444-1.155 0-.38.109-.719.326-1.014zm8.327-.769h1.375v1.154h-1.375v3.127c0 .315.07.545.211.69.141.146.365.219.67.219.134 0 .298-.01.494-.03v1.13c-.243.047-.497.07-.764.07-.498 0-.896-.058-1.196-.174-.3-.117-.52-.301-.658-.553-.14-.253-.21-.587-.21-1.002V3.827h-1.016V2.673h1.017V1.072h1.452v1.601zM159.862.958c.43.239.766.573 1.005 1.002.239.429.358.923.358 1.482 0 .548-.123 1.034-.37 1.46a2.58 2.58 0 0 1-1.032.992c-.44.237-.945.356-1.513.356h-1.84v2.754h-1.517V.6h3.421c.56 0 1.056.12 1.488.358zm-1.893 4.045c.545 0 .967-.136 1.267-.408.3-.271.45-.656.45-1.153 0-.512-.149-.905-.445-1.176-.295-.272-.718-.408-1.266-.408h-1.505v3.145h1.499zm4.867-3.631a.733.733 0 0 1-.253-.574c0-.233.084-.424.253-.574a.938.938 0 0 1 .646-.224c.267 0 .483.075.65.224.167.15.25.341.25.574a.736.736 0 0 1-.25.574c-.167.15-.383.224-.65.224a.938.938 0 0 1-.646-.224zm1.375 1.26v6.372h-1.452V2.633h1.452zM167.533.17v8.835h-1.458V.17h1.458zm2.96 8.568a2.615 2.615 0 0 1-1.068-1.145c-.247-.499-.37-1.09-.37-1.773 0-.68.124-1.27.373-1.768a2.646 2.646 0 0 1 1.073-1.148c.466-.266 1.015-.399 1.646-.399.635 0 1.184.133 1.649.4.464.265.82.647 1.07 1.144.248.497.373 1.087.373 1.77 0 .684-.125 1.275-.374 1.774-.248.499-.605.88-1.07 1.145-.464.264-1.013.396-1.648.396-.639 0-1.19-.132-1.655-.396zm2.832-1.366c.277-.367.415-.885.415-1.552 0-.664-.14-1.18-.418-1.55-.278-.369-.67-.553-1.175-.553-.51 0-.903.184-1.182.553-.278.37-.417.886-.417 1.55 0 .667.14 1.185.417 1.552.279.367.672.55 1.182.55.51 0 .902-.183 1.178-.55zm5.3-4.698H180v1.154h-1.375v3.127c0 .315.07.545.211.69.141.146.365.219.67.219.133 0 .298-.01.494-.03v1.13a4.02 4.02 0 0 1-.764.07c-.498 0-.897-.058-1.196-.174a1.23 1.23 0 0 1-.659-.553c-.139-.253-.208-.587-.208-1.002V3.827h-1.017V2.673h1.017V1.072h1.452v1.601zm-90.83 20.248l-1.557-5.207h-.076l-1.558 5.207h-.811L82 16.648h.782l1.405 5.323h.07l1.564-5.323h.77l1.563 5.323h.07l1.418-5.323h.775l-1.804 6.273h-.817zm8.404-.67a2.47 2.47 0 0 1-.903.574c-.356.134-.754.2-1.193.2-.576 0-1.077-.131-1.502-.396a2.602 2.602 0 0 1-.984-1.127c-.232-.487-.347-1.059-.347-1.715 0-.652.115-1.223.347-1.712a2.6 2.6 0 0 1 .987-1.133c.427-.266.925-.399 1.493-.399.557 0 1.04.125 1.45.376.409.25.723.61.943 1.077.219.468.329 1.019.329 1.651v.338h-4.756v.035c.012.47.104.88.277 1.23.172.349.411.618.717.809.305.19.66.285 1.064.285.446 0 .827-.084 1.14-.253.314-.17.539-.409.676-.72h.782a2.096 2.096 0 0 1-.52.88zm-3.125-4.767a1.9 1.9 0 0 0-.708.748 2.638 2.638 0 0 0-.297 1.124h3.944a2.542 2.542 0 0 0-.246-1.124 1.794 1.794 0 0 0-.673-.748 1.878 1.878 0 0 0-1.003-.265c-.38 0-.719.088-1.017.265zm7.142 5.18a2.203 2.203 0 0 1-.858-.996h-.076v1.253h-.735V14.15h.77v3.716h.07a2.09 2.09 0 0 1 .841-.967 2.414 2.414 0 0 1 1.293-.355c.53 0 .996.134 1.4.402.403.268.714.646.934 1.136.22.489.329 1.056.329 1.7 0 .641-.11 1.207-.332 1.698-.222.491-.533.872-.935 1.142-.401.27-.867.405-1.396.405-.486 0-.92-.12-1.305-.362zm.062-5.12c-.312.21-.553.509-.723.895-.17.386-.256.834-.256 1.342 0 .51.086.957.256 1.343s.411.684.723.894c.312.21.673.314 1.085.314.411 0 .77-.103 1.075-.311.306-.208.541-.505.706-.891.164-.387.246-.838.246-1.354 0-.517-.082-.966-.246-1.349a1.987 1.987 0 0 0-.706-.885 1.867 1.867 0 0 0-1.075-.312c-.412 0-.773.105-1.085.315zm13.437 4.707c-.245.248-.545.44-.902.574a3.37 3.37 0 0 1-1.193.2c-.576 0-1.077-.131-1.502-.396a2.602 2.602 0 0 1-.985-1.127c-.23-.487-.346-1.059-.346-1.715 0-.652.115-1.223.346-1.712.232-.49.56-.867.988-1.133.427-.266.925-.399 1.493-.399.556 0 1.04.125 1.449.376.41.25.724.61.943 1.077.22.468.33 1.019.33 1.651v.338h-4.756v.035c.012.47.104.88.276 1.23.173.349.412.618.718.809.305.19.66.285 1.063.285.447 0 .827-.084 1.14-.253.314-.17.54-.409.677-.72h.782a2.096 2.096 0 0 1-.52.88zm-3.124-4.767a1.9 1.9 0 0 0-.708.748 2.638 2.638 0 0 0-.297 1.124h3.944a2.542 2.542 0 0 0-.247-1.124 1.794 1.794 0 0 0-.673-.748 1.878 1.878 0 0 0-1.002-.265c-.38 0-.719.088-1.017.265zm5.567 5.437h-.882l2.234-3.157-2.252-3.116h.93l1.721 2.504h.071l1.71-2.504h.888l-2.222 3.087 2.246 3.186h-.923l-1.723-2.563h-.07l-1.728 2.563zm10.219-5.976c.402.268.713.646.934 1.136.222.489.333 1.056.333 1.7 0 .645-.111 1.212-.333 1.701-.22.49-.532.869-.934 1.139s-.867.405-1.396.405c-.49 0-.924-.116-1.302-.347a2.056 2.056 0 0 1-.832-.952h-.07V25h-.77v-8.352h.734v1.281h.07c.197-.43.487-.77.87-1.016.385-.247.818-.37 1.3-.37.529 0 .994.134 1.396.402zm-.485 5.076c.306-.208.542-.503.708-.885.167-.383.25-.832.25-1.349 0-.516-.083-.968-.25-1.354a2.002 2.002 0 0 0-.708-.891 1.867 1.867 0 0 0-1.076-.312 1.88 1.88 0 0 0-1.084.318 2.05 2.05 0 0 0-.723.894c-.17.384-.256.83-.256 1.34 0 .508.085.955.256 1.339.17.384.411.682.723.894.311.212.67.317 1.078.317.416 0 .776-.103 1.082-.311zm8.026.23c-.244.248-.545.44-.902.574-.356.134-.754.2-1.193.2-.576 0-1.077-.131-1.502-.396a2.602 2.602 0 0 1-.985-1.127c-.23-.487-.346-1.059-.346-1.715 0-.652.115-1.223.346-1.712.232-.49.56-.867.988-1.133.427-.266.925-.399 1.493-.399.557 0 1.04.125 1.449.376.41.25.724.61.943 1.077.22.468.33 1.019.33 1.651v.338h-4.756v.035c.012.47.104.88.277 1.23.172.349.411.618.717.809.305.19.66.285 1.064.285.446 0 .826-.084 1.14-.253.313-.17.539-.409.676-.72h.782a2.096 2.096 0 0 1-.52.88zm-3.124-4.767a1.9 1.9 0 0 0-.708.748 2.638 2.638 0 0 0-.297 1.124h3.944a2.542 2.542 0 0 0-.247-1.124 1.794 1.794 0 0 0-.673-.748 1.878 1.878 0 0 0-1.002-.265c-.38 0-.719.088-1.017.265zm6.149-.836v1.03h.076a1.44 1.44 0 0 1 .362-.593c.17-.171.377-.304.62-.4.243-.094.503-.142.782-.142.25 0 .448.018.593.052v.763a3.366 3.366 0 0 0-.688-.076c-.32 0-.612.075-.872.225-.26.15-.466.355-.615.617a1.754 1.754 0 0 0-.223.883v3.914h-.77v-6.273h.735zm3.853-1.567a.551.551 0 0 1-.168-.402c0-.155.056-.288.168-.399a.552.552 0 0 1 .403-.166c.156 0 .291.056.405.166.114.111.17.244.17.4a.547.547 0 0 1-.167.401.554.554 0 0 1-.408.17.547.547 0 0 1-.403-.17zm.79 1.567v6.273h-.77v-6.273h.77zm2.751 0v1.13h.077a1.79 1.79 0 0 1 .69-.906c.324-.22.703-.329 1.138-.329.435 0 .808.11 1.12.33.311.219.536.52.673.905h.076a1.97 1.97 0 0 1 .773-.903c.35-.221.752-.332 1.202-.332.635 0 1.128.182 1.479.545.35.363.526.876.526 1.54v4.293h-.77v-4.118c0-.513-.125-.903-.374-1.17-.248-.269-.612-.403-1.09-.403-.317 0-.6.075-.846.225-.247.15-.44.354-.576.614-.138.26-.206.551-.206.874v3.978h-.77v-4.2c0-.299-.061-.56-.182-.786a1.278 1.278 0 0 0-.512-.521 1.544 1.544 0 0 0-.764-.184c-.302 0-.577.082-.826.245a1.7 1.7 0 0 0-.588.667 2.02 2.02 0 0 0-.214.929v3.85h-.77v-6.273h.734zm14.293 5.603c-.245.248-.546.44-.902.574-.357.134-.755.2-1.193.2-.577 0-1.077-.131-1.502-.396a2.602 2.602 0 0 1-.985-1.127c-.231-.487-.347-1.059-.347-1.715 0-.652.116-1.223.347-1.712.231-.49.56-.867.988-1.133.427-.266.924-.399 1.493-.399.556 0 1.04.125 1.449.376.41.25.724.61.943 1.077.22.468.33 1.019.33 1.651v.338h-4.756v.035c.012.47.104.88.276 1.23.173.349.412.618.717.809.306.19.66.285 1.064.285.447 0 .827-.084 1.14-.253.314-.17.54-.409.677-.72h.781a2.096 2.096 0 0 1-.52.88zm-3.124-4.767a1.9 1.9 0 0 0-.708.748 2.638 2.638 0 0 0-.297 1.124h3.944a2.542 2.542 0 0 0-.247-1.124 1.794 1.794 0 0 0-.673-.748 1.878 1.878 0 0 0-1.002-.265c-.38 0-.72.088-1.017.265zm6.148-.836v1.13h.077c.168-.392.427-.696.776-.912.348-.215.772-.323 1.27-.323.705 0 1.25.201 1.636.603.386.402.58.968.58 1.698v4.077h-.77v-3.885c0-.598-.139-1.049-.415-1.351-.276-.303-.689-.455-1.237-.455a1.93 1.93 0 0 0-.979.245 1.703 1.703 0 0 0-.664.69c-.159.297-.238.64-.238 1.028v3.728h-.77v-6.273h.734zm7.442 0h1.464v.646h-1.464v3.891c0 .384.078.663.235.836.157.173.414.259.77.259.177 0 .33-.006.459-.017v.652a4.021 4.021 0 0 1-.54.04c-.409 0-.736-.053-.983-.16a1.039 1.039 0 0 1-.54-.51c-.114-.232-.17-.54-.17-.925v-4.066h-1.035v-.646h1.034v-1.63h.77v1.63zM4.739 19.117c.448.494 1.042.875 1.782 1.143s1.585.402 2.534.402c.876 0 1.663-.137 2.361-.41.699-.273 1.244-.654 1.635-1.143.39-.49.586-1.043.586-1.661 0-.804-.326-1.458-.977-1.963-.652-.505-1.702-.922-3.152-1.252l-2.924-.664c-2.044-.454-3.558-1.193-4.543-2.218C1.056 10.326.563 8.984.563 7.325c0-1.34.35-2.516 1.048-3.531.698-1.015 1.681-1.803 2.948-2.365C5.825.868 7.293.587 8.96.587c1.564 0 2.966.276 4.207.827 1.24.551 2.226 1.321 2.956 2.31.73.99 1.126 2.118 1.188 3.385H13.45c-.135-.979-.61-1.757-1.423-2.334-.813-.577-1.84-.865-3.08-.865-.855 0-1.609.129-2.26.386-.652.258-1.158.624-1.517 1.097a2.66 2.66 0 0 0-.54 1.654c0 .752.308 1.363.923 1.831.615.47 1.621.863 3.018 1.183l2.596.587c1.533.34 2.776.785 3.73 1.337.954.55 1.655 1.23 2.103 2.04.449.808.673 1.784.673 2.928 0 1.432-.357 2.679-1.071 3.74-.715 1.061-1.739 1.878-3.073 2.45-1.335.571-2.91.857-4.723.857-1.71 0-3.217-.27-4.52-.811-1.303-.541-2.328-1.309-3.073-2.303-.745-.994-1.15-2.16-1.212-3.5h3.941c.083.66.35 1.236.798 1.73zm29.895 2.333c-.688.752-1.561 1.33-2.62 1.73-1.058.403-2.254.604-3.589.604-1.668 0-3.107-.35-4.316-1.051-1.21-.7-2.137-1.705-2.784-3.014-.646-1.308-.97-2.864-.97-4.667s.326-3.369.978-4.698C21.985 9.025 22.915 8 24.125 7.28c1.209-.721 2.632-1.082 4.269-1.082 1.616 0 3.01.34 4.183 1.02 1.173.68 2.07 1.661 2.69 2.944.62 1.283.93 2.805.93 4.567v1.236H24.25v.2c.03.928.224 1.737.578 2.427.355.69.85 1.221 1.486 1.592.636.37 1.376.556 2.22.556.96 0 1.765-.167 2.417-.502.652-.335 1.118-.817 1.4-1.445h3.675a5.853 5.853 0 0 1-1.392 2.658zM26.33 9.751a3.8 3.8 0 0 0-1.447 1.445c-.36.624-.566 1.342-.618 2.156h8.054c-.02-.824-.198-1.548-.531-2.171a3.542 3.542 0 0 0-1.377-1.437c-.584-.335-1.261-.503-2.033-.503-.76 0-1.444.17-2.048.51zm16.899-3.214v2.704h.266c.417-.968 1.055-1.717 1.916-2.248.86-.53 1.915-.796 3.166-.796 1.898 0 3.355.546 4.372 1.638 1.016 1.092 1.524 2.648 1.524 4.667v10.942h-3.878v-10.03c0-1.298-.282-2.27-.845-2.913-.563-.644-1.433-.966-2.611-.966-.761 0-1.426.165-1.994.495a3.307 3.307 0 0 0-1.314 1.414c-.308.613-.461 1.331-.461 2.155v9.845h-3.88V6.537h3.738zm17.697 16.119c-1.064-.711-1.88-1.724-2.448-3.037-.568-1.314-.852-2.862-.852-4.644 0-1.772.287-3.312.86-4.62.574-1.31 1.39-2.319 2.448-3.03 1.058-.71 2.301-1.066 3.73-1.066 1.167 0 2.21.263 3.127.788a5.042 5.042 0 0 1 2.065 2.179h.266V0H74v23.444h-3.753V20.77h-.266a5.252 5.252 0 0 1-2.135 2.171c-.933.52-1.994.78-3.182.78-1.429 0-2.675-.355-3.738-1.065zm2.666-12.48c-.64.438-1.133 1.07-1.478 1.894-.344.824-.516 1.797-.516 2.92 0 1.134.172 2.11.516 2.929.345.819.837 1.447 1.478 1.885.642.438 1.4.657 2.276.657.886 0 1.652-.219 2.299-.657.646-.438 1.141-1.069 1.485-1.893.344-.824.516-1.798.516-2.92 0-1.113-.174-2.082-.523-2.906-.35-.824-.848-1.458-1.494-1.9-.646-.444-1.407-.665-2.283-.665-.876 0-1.634.219-2.276.656z" fill="#3E3D40" fill-rule="evenodd"/></svg>
\ No newline at end of file
diff --git a/public/upload.js b/public/upload.js
index 1698586e..0a50fe60 100644
--- a/public/upload.js
+++ b/public/upload.js
@@ -8,7 +8,7 @@ const EventEmitter=require("events"),{arrayToHex:arrayToHex}=require("./utils"),
 const{isFile:isFile}=require("./utils");class Storage{constructor(e){this.engine=e}get totalDownloads(){return Number(this.engine.getItem("totalDownloads"))}set totalDownloads(e){this.engine.setItem("totalDownloads",e)}get totalUploads(){return Number(this.engine.getItem("totalUploads"))}set totalUploads(e){this.engine.setItem("totalUploads",e)}get referrer(){return this.engine.getItem("referrer")}set referrer(e){this.engine.setItem("referrer",e)}get files(){const e=[];for(let t=0;t<this.engine.length;t++){const n=this.engine.key(t);isFile(n)&&e.push(JSON.parse(this.engine.getItem(n)))}return e}get numFiles(){let e=0;for(let t=0;t<this.engine.length;t++){const n=this.engine.key(t);isFile(n)&&(e+=1)}return e}getFileById(e){return this.engine.getItem(e)}has(e){return this.engine.hasOwnProperty(e)}remove(e){this.engine.removeItem(e)}addFile(e,t){this.engine.setItem(e,JSON.stringify(t))}}module.exports=Storage;
 
 },{"./utils":5}],4:[function(require,module,exports){
-require("./common");const FileSender=require("./fileSender"),{notify:notify,gcmCompliant:gcmCompliant,findMetric:findMetric,sendEvent:sendEvent,ONE_DAY_IN_MS:ONE_DAY_IN_MS}=require("./utils"),bytes=require("bytes"),Storage=require("./storage"),storage=new Storage(localStorage),$=require("jquery");require("jquery-circle-progress");const Raven=window.Raven;storage.has("referrer")?(window.referrer=storage.referrer,storage.remove("referrer")):window.referrer="external",$(document).ready(function(){function e(o){o.preventDefault(),storage.totalUploads+=1;let n="";if("drop"===o.type){if(o.originalEvent.dataTransfer.files.length>1||0===o.originalEvent.dataTransfer.files[0].size)return $(".upload-window").removeClass("ondrag"),void document.l10n.formatValue("uploadPageMultipleFilesAlert").then(e=>{alert(e)});n=o.originalEvent.dataTransfer.files[0]}else n=o.target.files[0];if(n.size>MAXFILESIZE)return document.l10n.formatValue("fileTooBig",{size:bytes(MAXFILESIZE)}).then(alert);$("#page-one").attr("hidden",!0),$("#upload-error").attr("hidden",!0),$("#upload-progress").removeAttr("hidden"),$("body").off("drop",e);const r=new FileSender(n);$("#cancel-upload").click(()=>{r.cancel(),location.reload(),document.l10n.formatValue("uploadCancelNotification").then(e=>{notify(e)}),storage.referrer="cancelled-upload",sendEvent("sender","upload-stopped",{cm1:n.size,cm5:storage.totalUploads,cm6:c,cm7:storage.totalDownloads,cd1:"drop"===o.type?"drop":"click",cd2:"cancelled"})}),r.on("progress",e=>{const t=e[0]/e[1];$("#ul-progress").circleProgress("value",t),$("#ul-progress").circleProgress().on("circle-animation-end",function(){$(".percent-number").html(`${Math.floor(100*t)}`)}),$(".progress-text").text(`${n.name} (${bytes(e[0],{decimalPlaces:1,fixedDecimals:!0})} of ${bytes(e[1],{decimalPlaces:1})})`)}),r.on("loading",e=>{e?console.log("Processing"):console.log("Finished processing")}),r.on("hashing",e=>{e?console.log("Hashing"):console.log("Finished hashing")});let a;r.on("encrypting",e=>{e?console.log("Encrypting"):(console.log("Finished encrypting"),a=Date.now())});let d;const l=Date.now(),c=storage.numFiles+1;sendEvent("sender","upload-started",{cm1:n.size,cm5:storage.totalUploads,cm6:c,cm7:storage.totalDownloads,cd1:"drop"===o.type?"drop":"click",cd5:window.referrer}),r.upload().then(e=>{const r=Date.now(),s=r-l,i=r-a,p=n.size/(i/1e3);sendEvent("sender","upload-stopped",{cm1:n.size,cm2:s,cm3:p,cm5:storage.totalUploads,cm6:c,cm7:storage.totalDownloads,cd1:"drop"===o.type?"drop":"click",cd2:"completed"});const m={name:n.name,size:n.size,fileId:e.fileId,url:e.url,secretKey:e.secretKey,deleteToken:e.deleteToken,creationDate:new Date,expiry:864e5,totalTime:s,typeOfUpload:"drop"===o.type?"drop":"click",uploadSpeed:p};storage.addFile(e.fileId,m),$("#upload-filename").attr("data-l10n-id","uploadSuccessConfirmHeader"),d=window.setTimeout(()=>{$("#page-one").attr("hidden",!0),$("#upload-progress").attr("hidden",!0),$("#upload-error").attr("hidden",!0),$("#share-link").removeAttr("hidden")},1e3),t(m),document.l10n.formatValue("notifyUploadDone").then(e=>{notify(e)})}).catch(e=>{Raven.captureException(e),console.log(e),$("#page-one").attr("hidden",!0),$("#upload-progress").attr("hidden",!0),$("#upload-error").removeAttr("hidden"),window.clearTimeout(d),sendEvent("sender","upload-stopped",{cm1:n.size,cm5:storage.totalUploads,cm6:c,cm7:storage.totalDownloads,cd1:"drop"===o.type?"drop":"click",cd2:"errored",cd6:e})})}function t(e){function t(){v=h.getTime()-Date.now(),w=Math.floor(v/1e3/60),y=Math.floor(w/60),E=Math.floor(v/1e3%60);let n;y>=1?(l.innerHTML=y+"h "+w%60+"m",n=window.setTimeout(()=>{t()},6e4)):0===y&&(l.innerHTML=w+"m "+E+"s",n=window.setTimeout(()=>{t()},1e3)),v<=0&&(storage.remove(e.fileId),$(l).parents("tr").remove(),window.clearTimeout(n),o())}const n=document.createElement("tr"),r=document.createElement("td"),a=document.createElement("td"),d=$("<img>",{src:"/resources/copy-16.svg",class:"icon-copy","data-l10n-id":"copyUrlHover"}),l=document.createElement("td"),c=document.createElement("td"),s=$("<img>",{src:"/resources/close-16.svg",class:"icon-delete","data-l10n-id":"deleteButtonHover"}),i=document.createElement("div"),p=$("<div>",{class:"popuptext"}),m=document.createTextNode(e.name),u=e.url.trim()+`#${e.secretKey}`.trim();$("#link").attr("value",u),$("#copy-text").attr("data-l10n-args",'{"filename": "'+e.name+'"}'),$("#copy-text").attr("data-l10n-id","copyUrlFormLabelWithName"),p.attr("tabindex","-1"),r.appendChild(m);const g=document.createElement("span");$(g).addClass("icon-cancel-1"),$(g).attr("data-l10n-id","deleteButtonHover"),c.appendChild(g);const f=document.createElement("span");$(f).addClass("icon-docs"),$(f).attr("data-l10n-id","copyUrlHover"),a.appendChild(f),a.style.color="#0A8DFF",d.click(function(){sendEvent("sender","copied",{cd4:"upload-list"});const e=document.createElement("input");e.setAttribute("value",u),document.body.appendChild(e),e.select(),document.execCommand("copy"),document.body.removeChild(e),document.l10n.formatValue("copiedUrl").then(e=>{a.innerHTML=e}),window.setTimeout(()=>{const e=document.createElement("img");$(e).addClass("icon-copy"),$(e).attr("data-l10n-id","copyUrlHover"),$(e).attr("src","/resources/copy-16.svg"),$(a).html(e)},500)}),e.creationDate=new Date(e.creationDate);const h=new Date;h.setTime(e.creationDate.getTime()+e.expiry);let v=0;v=h.getTime()-Date.now();let w=Math.floor(v/1e3/60),y=Math.floor(w/60),E=Math.floor(v/1e3%60);t(),i.classList.add("popup");const D=document.createElement("span");$(D).addClass("del-file"),$(D).attr("data-l10n-id","sentFilesTitle4");const T=document.createElement("span");$(T).addClass("nvm"),$(T).attr("data-l10n-id","nevermindButton"),p.html([D,"&nbsp;","&nbsp;",T]),n.appendChild(r),$(a).append(d),n.appendChild(a),n.appendChild(l),$(i).append(p),$(c).append(s),c.appendChild(i),n.appendChild(c),$("tbody").append(n);const k=storage.numFiles;p.find(".del-file").click(t=>{FileSender.delete(e.fileId,e.deleteToken).then(()=>{$(t.target).parents("tr").remove();const n=ONE_DAY_IN_MS-(Date.now()-e.creationDate.getTime());sendEvent("sender","upload-deleted",{cm1:e.size,cm2:e.totalTime,cm3:e.uploadSpeed,cm4:n,cm5:storage.totalUploads,cm6:k,cm7:storage.totalDownloads,cd1:e.typeOfUpload,cd4:"upload-list"}).then(()=>{storage.remove(e.fileId)}),o()})}),document.getElementById("delete-file").onclick=(()=>{FileSender.delete(e.fileId,e.deleteToken).then(()=>{const t=ONE_DAY_IN_MS-(Date.now()-e.creationDate.getTime());sendEvent("sender","upload-deleted",{cm1:e.size,cm2:e.totalTime,cm3:e.uploadSpeed,cm4:t,cm5:storage.totalUploads,cm6:k,cm7:storage.totalDownloads,cd1:e.typeOfUpload,cd4:"success-screen"}).then(()=>{storage.remove(e.fileId),location.reload()})})}),s.click(function(){p.addClass("show"),p.focus()}),p.find(".nvm").click(function(e){e.stopPropagation(),p.removeClass("show")}),p.click(function(e){e.stopPropagation()}),p.blur(()=>{p.removeClass("show")}),o()}function o(){1===document.querySelector("tbody").childNodes.length?$("#file-list").attr("hidden",!0):$("#file-list").removeAttr("hidden")}gcmCompliant().catch(e=>{$("#page-one").attr("hidden",!0),$("#unsupported-browser").removeAttr("hidden"),sendEvent("sender","unsupported",{cd6:e})}),$("#file-upload").change(e),$(".legal-links a, .social-links a, #dl-firefox").click(function(e){e.preventDefault();const t=findMetric(e.currentTarget.href);sendEvent("sender","exited",{cd3:t}).then(()=>{location.href=e.currentTarget.href})}),$("#send-new-completed").click(function(e){e.preventDefault(),sendEvent("sender","restarted",{cd2:"completed"}).then(()=>{storage.referrer="completed-upload",location.href=e.currentTarget.href})}),$("#send-new-error").click(function(e){e.preventDefault(),sendEvent("sender","restarted",{cd2:"errored"}).then(()=>{storage.referrer="errored-upload",location.href=e.currentTarget.href})}),$("body").on("dragover",function(e){e.preventDefault()}).on("drop",e);const n=$("#copy-btn");n.attr("disabled",!1),$("#link").attr("disabled",!1),n.attr("data-l10n-id","copyUrlFormButton");const r=storage.files;if(console.log(r),0===r.length)o();else for(const e in r){const n=r[e].fileId;!function(e,n,r){const a=new XMLHttpRequest;a.onreadystatechange=(()=>{a.readyState===XMLHttpRequest.DONE&&(200===a.status?r&&t(n):404===a.status&&(storage.remove(e),0===storage.numFiles&&o()))}),a.open("get","/exists/"+e,!0),a.send()}(n,r[e],!0)}n.click(()=>{sendEvent("sender","copied",{cd4:"success-screen"});const e=document.createElement("input");e.setAttribute("value",$("#link").attr("value")),document.body.appendChild(e),e.select(),document.execCommand("copy"),document.body.removeChild(e),n.attr("disabled",!0),$("#link").attr("disabled",!0),n.html('<img src="/resources/check-16.svg" class="icon-check"></img>'),window.setTimeout(()=>{n.attr("disabled",!1),$("#link").attr("disabled",!1),n.attr("data-l10n-id","copyUrlFormButton")},3e3)}),$(".upload-window").on("dragover",()=>{$(".upload-window").addClass("ondrag")}),$(".upload-window").on("dragleave",()=>{$(".upload-window").removeClass("ondrag")}),$("#ul-progress").circleProgress({value:0,startAngle:-Math.PI/2,fill:"#3B9DFF",size:158,animation:{duration:300}}),$(".send-new").attr("href",window.location)});
+require("./common");const FileSender=require("./fileSender"),{notify:notify,gcmCompliant:gcmCompliant,findMetric:findMetric,sendEvent:sendEvent,ONE_DAY_IN_MS:ONE_DAY_IN_MS}=require("./utils"),bytes=require("bytes"),Storage=require("./storage"),storage=new Storage(localStorage),$=require("jquery");require("jquery-circle-progress");const Raven=window.Raven;storage.has("referrer")?(window.referrer=storage.referrer,storage.remove("referrer")):window.referrer="external",$(document).ready(function(){function e(o){if(o.preventDefault(),$("#page-one").attr("hidden"))return;storage.totalUploads+=1;let n="";if("drop"===o.type){if(!o.originalEvent.dataTransfer.files[0])return void $(".upload-window").removeClass("ondrag");if(o.originalEvent.dataTransfer.files.length>1||0===o.originalEvent.dataTransfer.files[0].size)return $(".upload-window").removeClass("ondrag"),void document.l10n.formatValue("uploadPageMultipleFilesAlert").then(e=>{alert(e)});n=o.originalEvent.dataTransfer.files[0]}else n=o.target.files[0];if(n.size>MAXFILESIZE)return document.l10n.formatValue("fileTooBig",{size:bytes(MAXFILESIZE)}).then(alert);$("#page-one").attr("hidden",!0),$("#upload-error").attr("hidden",!0),$("#upload-progress").removeAttr("hidden"),document.l10n.formatValue("importingFile").then(e=>{$(".progress-text").text(e)}),$("body").off("drop",e);const r=new FileSender(n);$("#cancel-upload").click(()=>{r.cancel(),location.reload(),document.l10n.formatValue("uploadCancelNotification").then(e=>{notify(e)}),storage.referrer="cancelled-upload",sendEvent("sender","upload-stopped",{cm1:n.size,cm5:storage.totalUploads,cm6:s,cm7:storage.totalDownloads,cd1:"drop"===o.type?"drop":"click",cd2:"cancelled"})}),r.on("progress",e=>{const t=e[0]/e[1];$("#ul-progress").circleProgress("value",t),$("#ul-progress").circleProgress().on("circle-animation-end",function(){$(".percent-number").text(`${Math.floor(100*t)}`)}),$(".progress-text").text(`${n.name} (${bytes(e[0],{decimalPlaces:1,fixedDecimals:!0})} of ${bytes(e[1],{decimalPlaces:1})})`)}),r.on("hashing",e=>{e?document.l10n.formatValue("verifyingFile").then(e=>{$(".progress-text").text(e)}):console.log("Finished hashing")});let a;r.on("encrypting",e=>{e?document.l10n.formatValue("encryptingFile").then(e=>{$(".progress-text").text(e)}):(console.log("Finished encrypting"),a=Date.now())});let d;const l=Date.now(),s=storage.numFiles+1;sendEvent("sender","upload-started",{cm1:n.size,cm5:storage.totalUploads,cm6:s,cm7:storage.totalDownloads,cd1:"drop"===o.type?"drop":"click",cd5:window.referrer}),setTimeout(()=>{r.upload().then(e=>{const r=Date.now(),c=r-l,i=r-a,p=n.size/(i/1e3),u=1e3*EXPIRE_SECONDS;sendEvent("sender","upload-stopped",{cm1:n.size,cm2:c,cm3:p,cm5:storage.totalUploads,cm6:s,cm7:storage.totalDownloads,cd1:"drop"===o.type?"drop":"click",cd2:"completed"});const m={name:n.name,size:n.size,fileId:e.fileId,url:e.url,secretKey:e.secretKey,deleteToken:e.deleteToken,creationDate:new Date,expiry:u,totalTime:c,typeOfUpload:"drop"===o.type?"drop":"click",uploadSpeed:p};storage.addFile(e.fileId,m),$("#upload-filename").attr("data-l10n-id","uploadSuccessConfirmHeader"),d=window.setTimeout(()=>{$("#page-one").attr("hidden",!0),$("#upload-progress").attr("hidden",!0),$("#upload-error").attr("hidden",!0),$("#share-link").removeAttr("hidden")},1e3),t(m),document.l10n.formatValue("notifyUploadDone").then(e=>{notify(e)})}).catch(e=>{0!==e&&(Raven.captureException(e),$("#page-one").attr("hidden",!0),$("#upload-progress").attr("hidden",!0),$("#upload-error").removeAttr("hidden"),window.clearTimeout(d),sendEvent("sender","upload-stopped",{cm1:n.size,cm5:storage.totalUploads,cm6:s,cm7:storage.totalDownloads,cd1:"drop"===o.type?"drop":"click",cd2:"errored",cd6:e}))})},10)}function t(e){function t(){v=h.getTime()-Date.now(),w=Math.floor(v/1e3/60),y=Math.floor(w/60),E=Math.floor(v/1e3%60);let n;y>=1?(l.innerHTML=y+"h "+w%60+"m",n=window.setTimeout(()=>{t()},6e4)):0===y&&(l.innerHTML=w+"m "+E+"s",n=window.setTimeout(()=>{t()},1e3)),v<=0&&(storage.remove(e.fileId),$(l).parents("tr").remove(),window.clearTimeout(n),o())}const n=document.createElement("tr"),r=document.createElement("td"),a=document.createElement("td"),d=$("<img>",{src:"/resources/copy-16.svg",class:"icon-copy","data-l10n-id":"copyUrlHover"}),l=document.createElement("td"),s=document.createElement("td"),c=$("<img>",{src:"/resources/close-16.svg",class:"icon-delete","data-l10n-id":"deleteButtonHover"}),i=document.createElement("div"),p=$("<div>",{class:"popuptext"}),u=document.createTextNode(e.name),m=e.url.trim()+`#${e.secretKey}`.trim();$("#link").attr("value",m),$("#copy-text").attr("data-l10n-args",'{"filename": "'+e.name+'"}'),$("#copy-text").attr("data-l10n-id","copyUrlFormLabelWithName"),p.attr("tabindex","-1"),r.appendChild(u);const g=document.createElement("span");$(g).addClass("icon-cancel-1"),$(g).attr("data-l10n-id","deleteButtonHover"),s.appendChild(g);const f=document.createElement("span");$(f).addClass("icon-docs"),$(f).attr("data-l10n-id","copyUrlHover"),a.appendChild(f),a.style.color="#0A8DFF",d.click(function(){sendEvent("sender","copied",{cd4:"upload-list"});const e=document.createElement("input");e.setAttribute("value",m),document.body.appendChild(e),e.select(),document.execCommand("copy"),document.body.removeChild(e),document.l10n.formatValue("copiedUrl").then(e=>{a.innerHTML=e}),window.setTimeout(()=>{const e=document.createElement("img");$(e).addClass("icon-copy"),$(e).attr("data-l10n-id","copyUrlHover"),$(e).attr("src","/resources/copy-16.svg"),$(a).html(e)},500)}),e.creationDate=new Date(e.creationDate);const h=new Date;h.setTime(e.creationDate.getTime()+e.expiry);let v=0;v=h.getTime()-Date.now();let w=Math.floor(v/1e3/60),y=Math.floor(w/60),E=Math.floor(v/1e3%60);t(),i.classList.add("popup");const D=$("<div>",{class:"popup-message"});D.attr("data-l10n-id","deletePopupText");const T=$("<span>",{class:"popup-yes"});T.attr("data-l10n-id","deletePopupYes");const C=$("<span>",{class:"popup-no"});C.attr("data-l10n-id","deletePopupCancel"),p.html([D,T,C]),n.appendChild(r),$(a).append(d),n.appendChild(a),n.appendChild(l),$(i).append(p),$(s).append(c),s.appendChild(i),n.appendChild(s),$("tbody").append(n);const k=storage.numFiles;p.find(".popup-yes").click(t=>{FileSender.delete(e.fileId,e.deleteToken).then(()=>{$(t.target).parents("tr").remove();const n=ONE_DAY_IN_MS-(Date.now()-e.creationDate.getTime());sendEvent("sender","upload-deleted",{cm1:e.size,cm2:e.totalTime,cm3:e.uploadSpeed,cm4:n,cm5:storage.totalUploads,cm6:k,cm7:storage.totalDownloads,cd1:e.typeOfUpload,cd4:"upload-list"}).then(()=>{storage.remove(e.fileId)}),o()})}),document.getElementById("delete-file").onclick=(()=>{FileSender.delete(e.fileId,e.deleteToken).then(()=>{const t=ONE_DAY_IN_MS-(Date.now()-e.creationDate.getTime());sendEvent("sender","upload-deleted",{cm1:e.size,cm2:e.totalTime,cm3:e.uploadSpeed,cm4:t,cm5:storage.totalUploads,cm6:k,cm7:storage.totalDownloads,cd1:e.typeOfUpload,cd4:"success-screen"}).then(()=>{storage.remove(e.fileId),location.reload()})})}),c.click(function(){p.addClass("show"),p.focus()}),p.find(".popup-no").click(function(e){e.stopPropagation(),p.removeClass("show")}),p.click(function(e){e.stopPropagation()}),p.blur(()=>{p.removeClass("show")}),o()}function o(){1===document.querySelector("tbody").childNodes.length?$("#file-list").attr("hidden",!0):$("#file-list").removeAttr("hidden")}gcmCompliant().catch(e=>{$("#page-one").attr("hidden",!0),sendEvent("sender","unsupported",{cd6:e}).then(()=>{location.replace("/unsupported")})}),$("#file-upload").change(e),$(".legal-links a, .social-links a, #dl-firefox").click(function(e){e.preventDefault();const t=findMetric(e.currentTarget.href);sendEvent("sender","exited",{cd3:t}).then(()=>{location.href=e.currentTarget.href})}),$("#send-new-completed").click(function(e){e.preventDefault(),sendEvent("sender","restarted",{cd2:"completed"}).then(()=>{storage.referrer="completed-upload",location.href=e.currentTarget.href})}),$("#send-new-error").click(function(e){e.preventDefault(),sendEvent("sender","restarted",{cd2:"errored"}).then(()=>{storage.referrer="errored-upload",location.href=e.currentTarget.href})}),$("body").on("dragover",function(e){e.preventDefault()}).on("drop",e);const n=$("#copy-btn");n.attr("disabled",!1),$("#link").attr("disabled",!1),n.attr("data-l10n-id","copyUrlFormButton");const r=storage.files;if(0===r.length)o();else for(const e in r){const n=r[e].fileId;!function(e,n,r){const a=new XMLHttpRequest;a.onreadystatechange=(()=>{a.readyState===XMLHttpRequest.DONE&&(200===a.status?r&&t(n):404===a.status&&(storage.remove(e),0===storage.numFiles&&o()))}),a.open("get","/exists/"+e,!0),a.send()}(n,r[e],!0)}n.click(()=>{sendEvent("sender","copied",{cd4:"success-screen"});const e=document.createElement("input");e.setAttribute("value",$("#link").attr("value")),document.body.appendChild(e),e.select(),document.execCommand("copy"),document.body.removeChild(e),n.attr("disabled",!0),$("#link").attr("disabled",!0),n.html('<img src="/resources/check-16.svg" class="icon-check"></img>'),window.setTimeout(()=>{n.attr("disabled",!1),$("#link").attr("disabled",!1),n.attr("data-l10n-id","copyUrlFormButton")},3e3)}),$(".upload-window").on("dragover",()=>{$(".upload-window").addClass("ondrag")}),$(".upload-window").on("dragleave",()=>{$(".upload-window").removeClass("ondrag")}),$("#ul-progress").circleProgress({value:0,startAngle:-Math.PI/2,fill:"#3B9DFF",size:158,animation:{duration:300}}),$(".send-new").attr("href",window.location)});
 
 },{"./common":1,"./fileSender":2,"./storage":3,"./utils":5,"bytes":6,"jquery":9,"jquery-circle-progress":8}],5:[function(require,module,exports){
 function arrayToHex(t){let e="";for(const n in t)t[n]<16?e+="0"+t[n].toString(16):e+=t[n].toString(16);return e}function hexToArray(t){const e=new Uint8Array(t.length/2);for(let n=0;n<t.length;n+=2)e[n/2]=parseInt(t.charAt(n)+t.charAt(n+1),16);return e}function notify(t){"Notification"in window&&("granted"===Notification.permission?new Notification(t):"denied"!==Notification.permission&&Notification.requestPermission(function(e){"granted"===e&&new Notification(t)}))}function gcmCompliant(){try{return window.crypto.subtle.generateKey({name:"AES-GCM",length:128},!0,["encrypt","decrypt"]).then(t=>window.crypto.subtle.encrypt({name:"AES-GCM",iv:window.crypto.getRandomValues(new Uint8Array(12)),additionalData:window.crypto.getRandomValues(new Uint8Array(6)),tagLength:128},t,new ArrayBuffer(8)).then(()=>Promise.resolve()).catch(t=>Promise.reject())).catch(t=>Promise.reject())}catch(t){return Promise.reject()}}function findMetric(t){switch(t){case"https://www.mozilla.org/":return"mozilla";case"https://www.mozilla.org/about/legal":return"legal";case"https://testpilot.firefox.com/about":return"about";case"https://testpilot.firefox.com/privacy":return"privacy";case"https://testpilot.firefox.com/terms":return"terms";case"https://www.mozilla.org/en-US/privacy/websites/#cookies":return"cookies";case"https://github.com/mozilla/send":return"github";case"https://twitter.com/FxTestPilot":return"twitter";case"https://www.mozilla.org/firefox/new/?scene=2":return"download-firefox";default:return"other"}}function isFile(t){return!["referrer","totalDownloads","totalUploads","testpilot_ga__cid"].includes(t)}function sendEvent(){return window.analytics.sendEvent.apply(window.analytics,arguments).catch(()=>0)}const ONE_DAY_IN_MS=864e5;module.exports={arrayToHex:arrayToHex,hexToArray:hexToArray,notify:notify,gcmCompliant:gcmCompliant,findMetric:findMetric,isFile:isFile,sendEvent:sendEvent,ONE_DAY_IN_MS:864e5};
diff --git a/scripts/version.js b/scripts/version.js
index e39e7a1a..5e112d6e 100755
--- a/scripts/version.js
+++ b/scripts/version.js
@@ -14,7 +14,7 @@ const filename = path.join(__dirname, '..', 'public', 'version.json');
 const filedata = {
   commit,
   source: pkg.homepage,
-  version: pkg.version
+  version: process.env.CIRCLE_TAG || pkg.version
 };
 
 fs.writeFileSync(filename, JSON.stringify(filedata, null, 2) + '\n');
diff --git a/server/config.js b/server/config.js
index b038869e..5c6e540a 100644
--- a/server/config.js
+++ b/server/config.js
@@ -4,12 +4,12 @@ const conf = convict({
   s3_bucket: {
     format: String,
     default: '',
-    env: 'P2P_S3_BUCKET'
+    env: 'S3_BUCKET'
   },
   redis_host: {
     format: String,
     default: 'localhost',
-    env: 'P2P_REDIS_HOST'
+    env: 'REDIS_HOST'
   },
   listen_port: {
     format: 'port',
@@ -25,17 +25,27 @@ const conf = convict({
   sentry_id: {
     format: String,
     default: '',
-    env: 'P2P_SENTRY_CLIENT'
+    env: 'SENTRY_CLIENT'
   },
   sentry_dsn: {
     format: String,
     default: '',
-    env: 'P2P_SENTRY_DSN'
+    env: 'SENTRY_DSN'
   },
   env: {
     format: ['production', 'development', 'test'],
     default: 'development',
     env: 'NODE_ENV'
+  },
+  max_file_size: {
+    format: Number,
+    default: 1024 * 1024 * 1024 * 2,
+    env: 'MAX_FILE_SIZE'
+  },
+  expire_seconds: {
+    format: Number,
+    default: 86400,
+    env: 'EXPIRE_SECONDS'
   }
 });
 
diff --git a/server/server.js b/server/server.js
index 24e1d418..d3c10203 100644
--- a/server/server.js
+++ b/server/server.js
@@ -19,8 +19,6 @@ const mozlog = require('./log.js');
 const log = mozlog('send.server');
 
 const STATIC_PATH = path.join(__dirname, '../public');
-const L20N = path.join(__dirname, '../node_modules/l20n');
-const LOCALES = path.join(__dirname, '../public/locales');
 
 const app = express();
 
@@ -34,10 +32,12 @@ app.engine(
 app.set('view engine', 'handlebars');
 
 app.use(helmet());
-app.use(helmet.hsts({
-  maxAge: 31536000,
-  force: conf.env === 'production'
-}));
+app.use(
+  helmet.hsts({
+    maxAge: 31536000,
+    force: conf.env === 'production'
+  })
+);
 app.use(
   helmet.contentSecurityPolicy({
     directives: {
@@ -45,38 +45,51 @@ app.use(
       connectSrc: [
         "'self'",
         'https://sentry.prod.mozaws.net',
-        'https://www.google-analytics.com',
-        'https://ssl.google-analytics.com'
+        'https://www.google-analytics.com'
       ],
       imgSrc: [
         "'self'",
-        'https://www.google-analytics.com',
-        'https://ssl.google-analytics.com'
+        'https://www.google-analytics.com'
       ],
-      scriptSrc: ["'self'", 'https://ssl.google-analytics.com'],
+      scriptSrc: ["'self'"],
       styleSrc: ["'self'", 'https://code.cdn.mozilla.net'],
       fontSrc: ["'self'", 'https://code.cdn.mozilla.net'],
       formAction: ["'none'"],
       frameAncestors: ["'none'"],
-      objectSrc: ["'none'"]
+      objectSrc: ["'none'"],
+      reportUri: '/__cspreport__'
+    }
+  })
+);
+app.use(
+  busboy({
+    limits: {
+      fileSize: conf.max_file_size
     }
   })
 );
-app.use(busboy());
 app.use(bodyParser.json());
 app.use(express.static(STATIC_PATH));
-app.use('/l20n', express.static(L20N));
-app.use('/locales', express.static(LOCALES));
 
 app.get('/', (req, res) => {
   res.render('index');
 });
 
+app.get('/unsupported', (req, res) => {
+  res.render('unsupported');
+});
+
+app.get('/legal', (req, res) => {
+  res.render('legal');
+});
+
 app.get('/jsconfig.js', (req, res) => {
   res.set('Content-Type', 'application/javascript');
   res.render('jsconfig', {
     trackerId: conf.analytics_id,
     dsn: conf.sentry_id,
+    maxFileSize: conf.max_file_size,
+    expireSeconds: conf.expire_seconds,
     layout: false
   });
 });
@@ -107,15 +120,17 @@ app.get('/download/:id', (req, res) => {
     storage
       .length(id)
       .then(contentLength => {
-        res.render('download', {
-          filename: decodeURIComponent(filename),
-          filesize: bytes(contentLength),
-          trackerId: conf.analytics_id,
-          dsn: conf.sentry_id
+        storage.ttl(id).then(timeToExpiry => {
+          res.render('download', {
+            filename: decodeURIComponent(filename),
+            filesize: bytes(contentLength),
+            sizeInBytes: contentLength,
+            timeToExpiry: timeToExpiry
+          });
         });
       })
       .catch(() => {
-        res.render('download');
+        res.status(404).render('notfound');
       });
   });
 });
@@ -219,15 +234,23 @@ app.post('/upload', (req, res, next) => {
   req.busboy.on('file', (fieldname, file, filename) => {
     log.info('Uploading:', newId);
 
-    storage.set(newId, file, filename, meta).then(() => {
-      const protocol = conf.env === 'production' ? 'https' : req.protocol;
-      const url = `${protocol}://${req.get('host')}/download/${newId}/`;
-      res.json({
-        url,
-        delete: meta.delete,
-        id: newId
-      });
-    });
+    storage.set(newId, file, filename, meta).then(
+      () => {
+        const protocol = conf.env === 'production' ? 'https' : req.protocol;
+        const url = `${protocol}://${req.get('host')}/download/${newId}/`;
+        res.json({
+          url,
+          delete: meta.delete,
+          id: newId
+        });
+      },
+      err => {
+        if (err.message === 'limit') {
+          return res.sendStatus(413);
+        }
+        res.sendStatus(500);
+      }
+    );
   });
 
   req.on('close', err => {
@@ -241,7 +264,7 @@ app.post('/upload', (req, res, next) => {
       .catch(err => {
         log.info('DeleteError:', newId);
       });
-  })
+  });
 });
 
 app.get('/__lbheartbeat__', (req, res) => {
diff --git a/server/storage.js b/server/storage.js
index 68375e47..66a1a192 100644
--- a/server/storage.js
+++ b/server/storage.js
@@ -23,6 +23,7 @@ if (conf.s3_bucket) {
   module.exports = {
     filename: filename,
     exists: exists,
+    ttl: ttl,
     length: awsLength,
     get: awsGet,
     set: awsSet,
@@ -39,6 +40,7 @@ if (conf.s3_bucket) {
   module.exports = {
     filename: filename,
     exists: exists,
+    ttl: ttl,
     length: localLength,
     get: localGet,
     set: localSet,
@@ -73,6 +75,18 @@ function metadata(id) {
   });
 }
 
+function ttl(id) {
+  return new Promise((resolve, reject) => {
+    redis_client.ttl(id, (err, reply) => {
+      if (!err) {
+        resolve(reply * 1000);
+      } else {
+        reject(err);
+      }
+    });
+  });
+}
+
 function filename(id) {
   return new Promise((resolve, reject) => {
     redis_client.hget(id, 'filename', (err, reply) => {
@@ -129,20 +143,24 @@ function localGet(id) {
 
 function localSet(newId, file, filename, meta) {
   return new Promise((resolve, reject) => {
-    const fstream = fs.createWriteStream(
-      path.join(__dirname, '../static', newId)
-    );
+    const filepath = path.join(__dirname, '../static', newId);
+    const fstream = fs.createWriteStream(filepath);
     file.pipe(fstream);
-    fstream.on('close', () => {
+    file.on('limit', () => {
+      file.unpipe(fstream);
+      fstream.destroy(new Error('limit'));
+    });
+    fstream.on('finish', () => {
       redis_client.hmset(newId, meta);
-      redis_client.expire(newId, 86400000);
+      redis_client.expire(newId, conf.expire_seconds);
       log.info('localSet:', 'Upload Finished of ' + newId);
       resolve(meta.delete);
     });
 
-    fstream.on('error', () => {
+    fstream.on('error', err => {
       log.error('localSet:', 'Failed upload of ' + newId);
-      reject();
+      fs.unlinkSync(filepath);
+      reject(err);
     });
   });
 }
@@ -211,21 +229,26 @@ function awsSet(newId, file, filename, meta) {
     Key: newId,
     Body: file
   };
-
-  return new Promise((resolve, reject) => {
-    s3.upload(params, function(err, _data) {
-      if (err) {
-        log.info('awsUploadError:', err.stack); // an error occurred
-        reject();
-      } else {
-        redis_client.hmset(newId, meta);
-
-        redis_client.expire(newId, 86400000);
-        log.info('awsUploadFinish', 'Upload Finished of ' + filename);
-        resolve(meta.delete);
-      }
-    });
+  let hitLimit = false;
+  const upload = s3.upload(params);
+  file.on('limit', () => {
+    hitLimit = true;
+    upload.abort();
   });
+  return upload.promise().then(
+    () => {
+      redis_client.hmset(newId, meta);
+      redis_client.expire(newId, conf.expire_seconds);
+      log.info('awsUploadFinish', 'Upload Finished of ' + filename);
+    },
+    err => {
+      if (hitLimit) {
+        throw new Error('limit');
+      } else {
+        throw err;
+      }
+    }
+  );
 }
 
 function awsDelete(id, delete_token) {
diff --git a/test/.eslintrc.yml b/test/.eslintrc.yml
index b65502fd..dd9f4ba4 100644
--- a/test/.eslintrc.yml
+++ b/test/.eslintrc.yml
@@ -19,3 +19,5 @@ rules:
   mocha/no-pending-tests: error
   mocha/no-return-and-callback: warn
   mocha/no-skipped-tests: error
+
+  no-console: off # ¯\_(ツ)_/¯
diff --git a/test/unit/aws.storage.test.js b/test/unit/aws.storage.test.js
index 0a1778bf..b4507e38 100644
--- a/test/unit/aws.storage.test.js
+++ b/test/unit/aws.storage.test.js
@@ -110,20 +110,20 @@ describe('Testing Set using aws', function() {
   it('Should pass when the file is successfully uploaded', function() {
     const buf = Buffer.alloc(10);
     sinon.stub(crypto, 'randomBytes').returns(buf);
-    s3Stub.upload.callsArgWith(1, null, {});
+    s3Stub.upload.returns({promise: () => Promise.resolve()});
     return storage
-      .set('123', {}, 'Filename.moz', {})
+      .set('123', {on: sinon.stub()}, 'Filename.moz', {})
       .then(() => {
         assert(expire.calledOnce);
-        assert(expire.calledWith('123', 86400000));
+        assert(expire.calledWith('123', 86400));
       })
       .catch(err => assert.fail());
   });
 
   it('Should fail if there was an error during uploading', function() {
-    s3Stub.upload.callsArgWith(1, new Error(), null);
+    s3Stub.upload.returns({promise: () => Promise.reject()});
     return storage
-      .set('123', {}, 'Filename.moz', 'url.com')
+      .set('123', {on: sinon.stub()}, 'Filename.moz', 'url.com')
       .then(_reply => assert.fail())
       .catch(err => assert(1));
   });
diff --git a/test/unit/local.storage.test.js b/test/unit/local.storage.test.js
index d50fb478..48836433 100644
--- a/test/unit/local.storage.test.js
+++ b/test/unit/local.storage.test.js
@@ -117,12 +117,12 @@ describe('Testing Get from local filesystem', function() {
 describe('Testing Set to local filesystem', function() {
   it('Successfully writes the file to the local filesystem', function() {
     const stub = sinon.stub();
-    stub.withArgs('close', sinon.match.any).callsArgWithAsync(1);
+    stub.withArgs('finish', sinon.match.any).callsArgWithAsync(1);
     stub.withArgs('error', sinon.match.any).returns(1);
     fsStub.createWriteStream.returns({ on: stub });
 
     return storage
-      .set('test', { pipe: sinon.stub() }, 'Filename.moz', {})
+      .set('test', { pipe: sinon.stub(), on: sinon.stub() }, 'Filename.moz', {})
       .then(() => {
         assert(1);
       })
diff --git a/views/download.handlebars b/views/download.handlebars
index 94cfeef8..1225bd90 100644
--- a/views/download.handlebars
+++ b/views/download.handlebars
@@ -1,5 +1,5 @@
 <div id="download">
-  {{#if filename}}
+  <script src="/download.js"></script>
   <div id="download-page-one">
     <div class="title">
       <span id="dl-filename"
@@ -7,6 +7,8 @@
             data-l10n-args='{"filename": "{{filename}}"}'></span>
       <span data-l10n-id="downloadFileSize"
             data-l10n-args='{"size": "{{filesize}}"}'></span>
+      <span id="dl-bytelength" hidden="true">{{sizeInBytes}}</span>
+      <span id="dl-ttl" hidden="true">{{timeToExpiry}}</span>
     </div>
     <div class="description" data-l10n-id="downloadMessage"></div>
     <img src="/resources/illustration_download.svg" id="download-img" data-l10n-id="downloadAltText"/>
@@ -34,13 +36,4 @@
   </div>
 
   <a class="send-new" data-l10n-id="sendYourFilesLink"></a>
-  {{else}}
-  <div class="title" data-l10n-id="expiredPageHeader"></div>
-
-  <div class="share-window">
-    <img src="/resources/illustration_expired.svg" id="expired-img" data-l10n-id="linkExpiredAlt"/>
-  </div>
-  <div class="expired-description" data-l10n-id="uploadPageExplainer"></div>
-  <a class="send-new" data-l10n-id="sendYourFilesLink"></a>
-  {{/if}}
 </div>
diff --git a/views/index.handlebars b/views/index.handlebars
index 35516497..527f54c4 100644
--- a/views/index.handlebars
+++ b/views/index.handlebars
@@ -1,4 +1,5 @@
 <div id="page-one">
+  <script src="/upload.js"></script>
   <div class="title" data-l10n-id="uploadPageHeader"></div>
   <div class="description">
     <div data-l10n-id="uploadPageExplainer"></div>
@@ -60,7 +61,7 @@
       <button id="copy-btn" data-l10n-id="copyUrlFormButton"></button>
     </div>
     <button id="delete-file" data-l10n-id="deleteFileButton"></button>
-    <a class="send-new" data-l10n-id="sendAnotherFileLink"></a>
+    <a class="send-new" id="send-new-completed" data-l10n-id="sendAnotherFileLink"></a>
   </div>
 </div>
 
@@ -68,17 +69,5 @@
   <div class="title" data-l10n-id="errorPageHeader"></div>
   <div class="expired-description" data-l10n-id="errorPageMessage"></div>
   <img id="upload-error-img" data-l10n-id="errorAltText" src="/resources/illustration_error.svg"/>
-  <a class="send-new" data-l10n-id="sendAnotherFileLink"></a>
-</div>
-
-<div id="unsupported-browser" hidden="true">
-  <div class="title" data-l10n-id="notSupportedHeader"></div>
-  <div class="description" data-l10n-id="notSupportedDetail"></div>
-  <a id="dl-firefox" href="https://www.mozilla.org/firefox/new/?scene=2" target="_blank">
-    <img src="/resources/firefox_logo-only.svg" id="firefox-logo" alt="Firefox"/>
-    <div id="dl-firefox-text">Firefox<br>
-      <span data-l10n-id="downloadFirefoxButtonSub"></span>
-    </div>
-  </a>
-  <div class="unsupported-description" data-l10n-id="uploadPageExplainer"></div>
+  <a class="send-new" id="send-new-error" data-l10n-id="sendAnotherFileLink"></a>
 </div>
diff --git a/views/jsconfig.handlebars b/views/jsconfig.handlebars
index 1e960e83..c9ddf16d 100644
--- a/views/jsconfig.handlebars
+++ b/views/jsconfig.handlebars
@@ -4,3 +4,5 @@ window.dsn = '{{{dsn}}}';
 {{#if trackerId}}
 window.trackerId = '{{{trackerId}}}';
 {{/if}}
+const MAXFILESIZE = {{{maxFileSize}}};
+const EXPIRE_SECONDS = {{{expireSeconds}}};
diff --git a/views/layouts/main.handlebars b/views/layouts/main.handlebars
index ac886ed5..e9c8b8a8 100644
--- a/views/layouts/main.handlebars
+++ b/views/layouts/main.handlebars
@@ -3,30 +3,37 @@
 <head>
   <title>Firefox Send</title>
   <script src="/jsconfig.js"></script>
-  <script src="/bundle.js"></script>
   <link rel="stylesheet" type="text/css" href="/main.css" />
   <link rel="stylesheet" href="https://code.cdn.mozilla.net/fonts/fira.css">
+  <meta name="viewport" content="width=device-width, initial-scale=1">
 
   <meta name="defaultLanguage" content="en-US">
   <meta name="availableLanguages" content="en-US">
   <link rel="localization" href="/locales/{locale}/send.ftl">
-  <script defer src="/l20n/dist/web/l20n.js"></script>
+  <script defer src="/l20n.min.js"></script>
 </head>
 <body>
-  <div class="send-logo">
-    <img src="/resources/send_logo.svg"/>
-    <img src="/resources/send_logo_type.svg"/>
-  </div>
+  <header class="header">
+    <div class="send-logo">
+      <img src="/resources/send_logo.svg" alt="Send"/>
+      <h1 class="site-title">Send</h1>
+      <div class="site-subtitle">
+        <a href="https://testpilot.firefox.com" target="_blank">Firefox Test Pilot</a>
+        <div data-l10n-id="siteSubtitle">web experiment</div>
+      </div>
+    </div>
+    <a href="https://qsurvey.mozilla.com/s3/txp-firefox-send" rel="noreferrer noopener" class="feedback" target="_blank" data-l10n-id="siteFeedback">Feedback</a>
+  </header>
   <div class="all">
     {{{body}}}
   </div>
   <div class="footer">
     <div class="legal-links">
-      <a  href="https://www.mozilla.org"><img class="mozilla-logo" src="/resources/mozilla-logo.svg"/></a>
-      <a href="https://www.mozilla.org/about/legal/" data-l10n-id="footerLinkLegal"></a>
+      <a href="https://www.mozilla.org"><img class="mozilla-logo" src="/resources/mozilla-logo.svg"/></a>
+      <a href="https://www.mozilla.org/about/legal" data-l10n-id="footerLinkLegal"></a>
       <a href="https://testpilot.firefox.com/about" data-l10n-id="footerLinkAbout"></a>
-      <a href="https://testpilot.firefox.com/privacy" data-l10n-id="footerLinkPrivacy"></a>
-      <a href="https://testpilot.firefox.com/terms" data-l10n-id="footerLinkTerms"></a>
+      <a href="/legal" data-l10n-id="footerLinkPrivacy"></a>
+      <a href="/legal" data-l10n-id="footerLinkTerms"></a>
       <a href="https://www.mozilla.org/en-US/privacy/websites/#cookies" data-l10n-id="footerLinkCookies"></a>
     </div>
     <div class="social-links">
diff --git a/views/legal.handlebars b/views/legal.handlebars
new file mode 100644
index 00000000..023ebf8e
--- /dev/null
+++ b/views/legal.handlebars
@@ -0,0 +1,12 @@
+<div id="legal">
+  <div class="title" data-l10n-id="legalHeader"></div>
+  <div class="description" data-l10n-id="legalNoticeTestPilot">
+    <a href="https://testpilot.firefox.com/terms"></a>
+    <a href="https://testpilot.firefox.com/privacy"></a>
+    <a href="https://testpilot.firefox.com/experiments/send"></a>
+  </div>
+  <div class="description" data-l10n-id="legalNoticeMozilla">
+    <a href="https://www.mozilla.org/privacy/websites/"></a>
+    <a href="https://www.mozilla.org/about/legal/terms/mozilla/"></a>
+  </div>
+</div>
diff --git a/views/notfound.handlebars b/views/notfound.handlebars
new file mode 100644
index 00000000..9f89c95a
--- /dev/null
+++ b/views/notfound.handlebars
@@ -0,0 +1,8 @@
+<div id="download">
+<div class="title" data-l10n-id="expiredPageHeader"></div>
+<div class="share-window">
+  <img src="/resources/illustration_expired.svg" id="expired-img" data-l10n-id="linkExpiredAlt"/>
+</div>
+<div class="expired-description" data-l10n-id="uploadPageExplainer"></div>
+<a class="send-new" href="/" id="expired-send-new" data-l10n-id="sendYourFilesLink"></a>
+</div>
diff --git a/views/unsupported.handlebars b/views/unsupported.handlebars
new file mode 100644
index 00000000..62814e57
--- /dev/null
+++ b/views/unsupported.handlebars
@@ -0,0 +1,11 @@
+<div id="unsupported-browser">
+  <div class="title" data-l10n-id="notSupportedHeader"></div>
+  <div class="description" data-l10n-id="notSupportedDetail"></div>
+  <a id="dl-firefox" href="https://www.mozilla.org/firefox/new/?scene=2" target="_blank">
+    <img src="/resources/firefox_logo-only.svg" id="firefox-logo" alt="Firefox"/>
+    <div id="dl-firefox-text">Firefox<br>
+      <span data-l10n-id="downloadFirefoxButtonSub"></span>
+    </div>
+  </a>
+  <div class="unsupported-description" data-l10n-id="uploadPageExplainer"></div>
+</div>