1
0
mirror of https://gitlab.com/timvisee/send.git synced 2024-11-13 06:32:34 +01:00

Merge pull request #515 from mozilla/refactor-upload

removed jquery from upload.js
This commit is contained in:
Danny Coates 2017-08-14 21:23:05 -07:00 committed by GitHub
commit 856b2cdc60
7 changed files with 440 additions and 511 deletions

View File

@ -2,17 +2,15 @@ import FileSender from './fileSender';
import Storage from './storage'; import Storage from './storage';
import * as metrics from './metrics'; import * as metrics from './metrics';
import { allowedCopy, copyToClipboard, ONE_DAY_IN_MS } from './utils'; import { allowedCopy, copyToClipboard, ONE_DAY_IN_MS } from './utils';
import $ from 'jquery/dist/jquery.slim'; import bel from 'bel';
const HOUR = 1000 * 60 * 60;
const storage = new Storage(); const storage = new Storage();
let fileList = null; let fileList = null;
let $link = null;
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
$link = $('#link');
fileList = document.getElementById('file-list'); fileList = document.getElementById('file-list');
toggleHeader(); toggleHeader();
// eslint-disable-next-line prefer-const
Promise.all( Promise.all(
storage.files.map(file => { storage.files.map(file => {
const id = file.fileId; const id = file.fileId;
@ -33,180 +31,115 @@ function toggleHeader() {
fileList.hidden = storage.files.length === 0; fileList.hidden = storage.files.length === 0;
} }
function timeLeft(milliseconds) {
const minutes = Math.floor(milliseconds / 1000 / 60);
const hours = Math.floor(minutes / 60);
const seconds = Math.floor(milliseconds / 1000 % 60);
if (hours >= 1) {
return `${hours}h ${minutes % 60}m`;
} else if (hours === 0) {
return `${minutes}m ${seconds}s`;
}
return 'Expired';
}
function addFile(file) { function addFile(file) {
if (!file) { if (!file) {
return; 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',
disabled: !allowedCopy()
});
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 popupDiv = document.createElement('div');
const $popupText = $('<div>', { class: 'popuptext' });
const cellText = document.createTextNode(file.name);
const url = file.url.trim() + `#${file.secretKey}`.trim();
$link.attr('value', url);
$('#copy-text')
.attr('data-l10n-args', `{"filename": "${file.name}"}`)
.attr('data-l10n-id', 'copyUrlFormLabelWithName');
$popupText.attr('tabindex', '-1');
name.appendChild(cellText);
// create delete button
const delSpan = document.createElement('span');
$(delSpan)
.addClass('icon-cancel-1')
.attr('data-l10n-id', 'deleteButtonHover');
del.appendChild(delSpan);
const linkSpan = document.createElement('span');
$(linkSpan).addClass('icon-docs').attr('data-l10n-id', 'copyUrlHover');
link.appendChild(linkSpan);
link.style.color = '#0A8DFF';
//copy link to clipboard when icon clicked
$copyIcon.on('click', () => {
// record copied event from upload list
metrics.copiedLink({ location: 'upload-list' });
copyToClipboard(url);
document.l10n.formatValue('copiedUrl').then(translated => {
link.innerHTML = translated;
});
setTimeout(() => {
const linkImg = document.createElement('img');
$(linkImg)
.addClass('icon-copy')
.attr('data-l10n-id', 'copyUrlHover')
.attr('src', '/resources/copy-16.svg');
$(link).html(linkImg);
}, 500);
});
file.creationDate = new Date(file.creationDate); file.creationDate = new Date(file.creationDate);
const url = `${file.url}#${file.secretKey}`;
const future = new Date(); const future = new Date();
future.setTime(file.creationDate.getTime() + file.expiry); future.setTime(file.creationDate.getTime() + file.expiry);
const countdown = future.getTime() - Date.now();
let countdown = 0; const row = bel`
countdown = future.getTime() - Date.now(); <tr>
let minutes = Math.floor(countdown / 1000 / 60); <td>${file.name}</td>
let hours = Math.floor(minutes / 60); <td>
let seconds = Math.floor(countdown / 1000 % 60); <span class="icon-docs" data-l10n-id="copyUrlHover"></span>
<img onclick=${copyClick} src="/resources/copy-16.svg" class="icon-copy" data-l10n-id="copyUrlHover">
const poll = () => { <span data-l10n-id="copiedUrl" class="text-copied" hidden="true"></span>
countdown = future.getTime() - Date.now(); </td>
minutes = Math.floor(countdown / 1000 / 60); <td>${timeLeft(countdown)}</td>
hours = Math.floor(minutes / 60); <td>
seconds = Math.floor(countdown / 1000 % 60); <span class="icon-cancel-1" data-l10n-id="deleteButtonHover" title="Delete"></span>
let t; <img onclick=${showPopup} src="/resources/close-16.svg" class="icon-delete" data-l10n-id="deleteButtonHover" title="Delete">
<div class="popup">
if (hours >= 1) { <div class="popuptext" onclick=${stopProp} onblur=${cancel} tabindex="-1">
expiry.innerHTML = hours + 'h ' + minutes % 60 + 'm'; <div class="popup-message" data-l10n-id="deletePopupText"></div>
t = setTimeout(() => { <div class="popup-action">
poll(); <span class="popup-no" onclick=${cancel} data-l10n-id="deletePopupCancel"></span>
}, 60000); <span class="popup-yes" onclick=${deleteFile} data-l10n-id="deletePopupYes"></span>
} else if (hours === 0) { </div>
expiry.innerHTML = minutes + 'm ' + seconds + 's'; </div>
t = window.setTimeout(() => { </div>
poll(); </td>
}, 1000); </tr>
`;
const popup = row.querySelector('.popuptext');
const timeCol = row.querySelectorAll('td')[2];
if (!allowedCopy()) {
row.querySelector('.icon-copy').disabled = true;
} }
//remove from list when expired
fileList.querySelector('tbody').appendChild(row);
toggleHeader();
poll();
function copyClick(e) {
metrics.copiedLink({ location: 'upload-list' });
copyToClipboard(url);
const icon = e.target;
const text = e.target.nextSibling;
icon.hidden = true;
text.hidden = false;
setTimeout(() => {
icon.hidden = false;
text.hidden = true;
}, 500);
}
function poll() {
const countdown = future.getTime() - Date.now();
if (countdown <= 0) { if (countdown <= 0) {
storage.remove(file.fileId); storage.remove(file.fileId);
$(expiry).parents('tr').remove(); row.parentNode.removeChild(row);
window.clearTimeout(t);
toggleHeader(); toggleHeader();
} }
}; timeCol.textContent = timeLeft(countdown);
setTimeout(poll, countdown >= HOUR ? 60000 : 1000);
}
poll(); function deleteFile() {
FileSender.delete(file.fileId, file.deleteToken);
// create popup
popupDiv.classList.add('popup');
const $popupMessage = $('<div>', { class: 'popup-message' });
$popupMessage.attr('data-l10n-id', 'deletePopupText');
const $popupAction = $('<div>', { class: 'popup-action' });
const $popupNvmSpan = $('<span>', { class: 'popup-no' });
$popupNvmSpan.attr('data-l10n-id', 'deletePopupCancel');
const $popupDelSpan = $('<span>', { class: 'popup-yes' });
$popupDelSpan.attr('data-l10n-id', 'deletePopupYes');
$popupText.html([$popupMessage, $popupAction]);
$popupAction.html([$popupNvmSpan, $popupDelSpan]);
// add data cells to table row
row.appendChild(name);
$(link).append($copyIcon);
row.appendChild(link);
row.appendChild(expiry);
$(popupDiv).append($popupText);
$(del).append($delIcon);
del.appendChild(popupDiv);
row.appendChild(del);
$('tbody').append(row); //add row to table
// delete file
$popupText.find('.popup-yes').on('click', e => {
FileSender.delete(file.fileId, file.deleteToken).then(() => {
$(e.target).parents('tr').remove();
const ttl = ONE_DAY_IN_MS - (Date.now() - file.creationDate.getTime()); const ttl = ONE_DAY_IN_MS - (Date.now() - file.creationDate.getTime());
metrics metrics.deletedUpload({
.deletedUpload({
size: file.size, size: file.size,
time: file.totalTime, time: file.totalTime,
speed: file.uploadSpeed, speed: file.uploadSpeed,
type: file.typeOfUpload, type: file.typeOfUpload,
location: 'upload-list', location: 'upload-list',
ttl ttl
}) });
.then(() => { row.parentNode.removeChild(row);
storage.remove(file.fileId); storage.remove(file.fileId);
});
toggleHeader(); toggleHeader();
}); }
});
// show popup function showPopup() {
$delIcon.on('click', () => { popup.classList.add('show');
$popupText.addClass('show').focus(); popup.focus();
}); }
// hide popup function cancel(e) {
$popupText.find('.popup-no').on('click', e => {
e.stopPropagation(); e.stopPropagation();
$popupText.removeClass('show'); popup.classList.remove('show');
}); }
$popupText.on('click', e => { function stopProp(e) {
e.stopPropagation(); e.stopPropagation();
}); }
//close when popup loses focus
$popupText.on('blur', () => {
$popupText.removeClass('show');
});
toggleHeader();
} }
async function checkExistence(id) { async function checkExistence(id) {

View File

@ -7,6 +7,14 @@ export default class FileSender extends EventEmitter {
this.file = file; this.file = file;
this.iv = window.crypto.getRandomValues(new Uint8Array(12)); this.iv = window.crypto.getRandomValues(new Uint8Array(12));
this.uploadXHR = new XMLHttpRequest(); this.uploadXHR = new XMLHttpRequest();
this.key = window.crypto.subtle.generateKey(
{
name: 'AES-GCM',
length: 128
},
true,
['encrypt']
);
} }
static delete(fileId, token) { static delete(fileId, token) {
@ -32,19 +40,8 @@ export default class FileSender extends EventEmitter {
this.uploadXHR.abort(); this.uploadXHR.abort();
} }
upload() { readFile() {
const self = this; return new Promise((resolve, reject) => {
self.emit('loading');
return Promise.all([
window.crypto.subtle.generateKey(
{
name: 'AES-GCM',
length: 128
},
true,
['encrypt', 'decrypt']
),
new Promise((resolve, reject) => {
const reader = new FileReader(); const reader = new FileReader();
reader.readAsArrayBuffer(this.file); reader.readAsArrayBuffer(this.file);
reader.onload = function(event) { reader.onload = function(event) {
@ -54,24 +51,10 @@ export default class FileSender extends EventEmitter {
reader.onerror = function(err) { reader.onerror = function(err) {
reject(err); reject(err);
}; };
}) });
]) }
.then(([secretKey, plaintext]) => {
self.emit('encrypting'); uploadFile(encrypted, keydata) {
return Promise.all([
window.crypto.subtle.encrypt(
{
name: 'AES-GCM',
iv: this.iv,
tagLength: 128
},
secretKey,
plaintext
),
window.crypto.subtle.exportKey('jwk', secretKey)
]);
})
.then(([encrypted, keydata]) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const file = this.file; const file = this.file;
const fileId = arrayToHex(this.iv); const fileId = arrayToHex(this.iv);
@ -80,11 +63,11 @@ export default class FileSender extends EventEmitter {
const fd = new FormData(); const fd = new FormData();
fd.append('data', blob, file.name); fd.append('data', blob, file.name);
const xhr = self.uploadXHR; const xhr = this.uploadXHR;
xhr.upload.addEventListener('progress', e => { xhr.upload.addEventListener('progress', e => {
if (e.lengthComputable) { if (e.lengthComputable) {
self.emit('progress', [e.loaded, e.total]); this.emit('progress', [e.loaded, e.total]);
} }
}); });
@ -113,6 +96,23 @@ export default class FileSender extends EventEmitter {
); );
xhr.send(fd); xhr.send(fd);
}); });
}); }
async upload() {
this.emit('loading');
const key = await this.key;
const plaintext = await this.readFile();
this.emit('encrypting');
const encrypted = await window.crypto.subtle.encrypt(
{
name: 'AES-GCM',
iv: this.iv,
tagLength: 128
},
key,
plaintext
);
const keydata = await window.crypto.subtle.exportKey('jwk', key);
return this.uploadFile(encrypted, keydata);
} }
} }

View File

@ -310,6 +310,10 @@ tbody {
opacity: 0.3; opacity: 0.3;
} }
.text-copied {
color: #0a8dff;
}
/* Popup container */ /* Popup container */
.popup { .popup {
position: absolute; position: absolute;

View File

@ -13,69 +13,20 @@ import Storage from './storage';
import * as metrics from './metrics'; import * as metrics from './metrics';
import * as progress from './progress'; import * as progress from './progress';
import * as fileList from './fileList'; import * as fileList from './fileList';
import $ from 'jquery/dist/jquery.slim';
const storage = new Storage(); const storage = new Storage();
$(() => { async function upload(event) {
gcmCompliant()
.then(function() {
const $pageOne = $('#page-one');
const $copyBtn = $('#copy-btn');
const $link = $('#link');
const $uploadWindow = $('.upload-window');
const $uploadError = $('#upload-error');
const $uploadProgress = $('#upload-progress');
$pageOne.removeAttr('hidden');
$('#file-upload').on('change', onUpload);
$(document.body).on('dragover', allowDrop).on('drop', onUpload);
// reset copy button
$copyBtn.attr({
disabled: !allowedCopy(),
'data-l10n-id': 'copyUrlFormButton'
});
$link.attr('disabled', false);
// copy link to clipboard
$copyBtn.on('click', () => {
if (allowedCopy() && copyToClipboard($link.attr('value'))) {
metrics.copiedLink({ location: 'success-screen' });
//disable button for 3s
$copyBtn.attr('disabled', true);
$link.attr('disabled', true);
$copyBtn.html(
'<img src="/resources/check-16.svg" class="icon-check"></img>'
);
setTimeout(() => {
$copyBtn.attr({
disabled: false,
'data-l10n-id': 'copyUrlFormButton'
});
$link.attr('disabled', false);
}, 3000);
}
});
$uploadWindow
.on('dragover', () => {
$uploadWindow.addClass('ondrag');
})
.on('dragleave', () => {
$uploadWindow.removeClass('ondrag');
});
// on file upload by browse or drag & drop
function onUpload(event) {
event.preventDefault(); event.preventDefault();
const pageOne = document.getElementById('page-one');
const link = document.getElementById('link');
const uploadWindow = document.querySelector('.upload-window');
const uploadError = document.getElementById('upload-error');
const uploadProgress = document.getElementById('upload-progress');
const clickOrDrop = event.type === 'drop' ? 'drop' : 'click'; const clickOrDrop = event.type === 'drop' ? 'drop' : 'click';
// don't allow upload if not on upload page // don't allow upload if not on upload page
if ($pageOne.attr('hidden')) { if (pageOne.hidden) {
return; return;
} }
@ -84,17 +35,15 @@ $(() => {
let file = ''; let file = '';
if (clickOrDrop === 'drop') { if (clickOrDrop === 'drop') {
if (!event.originalEvent.dataTransfer.files[0]) { if (!event.originalEvent.dataTransfer.files[0]) {
$uploadWindow.removeClass('ondrag'); uploadWindow.classList.remove('ondrag');
return; return;
} }
if ( if (
event.originalEvent.dataTransfer.files.length > 1 || event.originalEvent.dataTransfer.files.length > 1 ||
event.originalEvent.dataTransfer.files[0].size === 0 event.originalEvent.dataTransfer.files[0].size === 0
) { ) {
$uploadWindow.removeClass('ondrag'); uploadWindow.classList.remove('ondrag');
document.l10n document.l10n.formatValue('uploadPageMultipleFilesAlert').then(str => {
.formatValue('uploadPageMultipleFilesAlert')
.then(str => {
alert(str); alert(str);
}); });
return; return;
@ -110,23 +59,23 @@ $(() => {
.then(alert); .then(alert);
} }
$pageOne.attr('hidden', true); pageOne.hidden = true;
$uploadError.attr('hidden', true); uploadError.hidden = true;
$uploadProgress.removeAttr('hidden'); uploadProgress.hidden = false;
document.l10n document.l10n
.formatValue('uploadingPageProgress', { .formatValue('uploadingPageProgress', {
size: bytes(file.size), size: bytes(file.size),
filename: file.name filename: file.name
}) })
.then(str => { .then(str => {
$('#upload-filename').text(str); document.getElementById('upload-filename').textContent = str;
}); });
document.l10n.formatValue('importingFile').then(progress.setText); document.l10n.formatValue('importingFile').then(progress.setText);
//don't allow drag and drop when not on page-one //don't allow drag and drop when not on page-one
$(document.body).off('drop', onUpload); document.body.removeEventListener('drop', upload);
const fileSender = new FileSender(file); const fileSender = new FileSender(file);
$('#cancel-upload').on('click', () => { document.getElementById('cancel-upload').addEventListener('click', () => {
fileSender.cancel(); fileSender.cancel();
metrics.cancelledUpload({ metrics.cancelledUpload({
size: file.size, size: file.size,
@ -166,6 +115,8 @@ $(() => {
const speed = file.size / (uploadTime / 1000); const speed = file.size / (uploadTime / 1000);
const expiration = EXPIRE_SECONDS * 1000; const expiration = EXPIRE_SECONDS * 1000;
link.setAttribute('value', `${info.url}#${info.secretKey}`);
metrics.completedUpload({ metrics.completedUpload({
size: file.size, size: file.size,
time, time,
@ -187,14 +138,10 @@ $(() => {
uploadSpeed: speed uploadSpeed: speed
}; };
$('#delete-file').on('click', () => { document.getElementById('delete-file').addEventListener('click', () => {
FileSender.delete( FileSender.delete(fileData.fileId, fileData.deleteToken).then(() => {
fileData.fileId,
fileData.deleteToken
).then(() => {
const ttl = const ttl =
ONE_DAY_IN_MS - ONE_DAY_IN_MS - (Date.now() - fileData.creationDate.getTime());
(Date.now() - fileData.creationDate.getTime());
metrics metrics
.deletedUpload({ .deletedUpload({
size: fileData.size, size: fileData.size,
@ -213,10 +160,10 @@ $(() => {
storage.addFile(info.fileId, fileData); storage.addFile(info.fileId, fileData);
$pageOne.attr('hidden', true); pageOne.hidden = true;
$uploadProgress.attr('hidden', true); uploadProgress.hidden = true;
$uploadError.attr('hidden', true); uploadError.hidden = true;
$('#share-link').removeAttr('hidden'); document.getElementById('share-link').hidden = false;
fileList.addFile(fileData); fileList.addFile(fileData);
document.l10n.formatValue('notifyUploadDone').then(str => { document.l10n.formatValue('notifyUploadDone').then(str => {
@ -230,9 +177,9 @@ $(() => {
} }
// only show error page when the error is anything other than user cancelling the upload // only show error page when the error is anything other than user cancelling the upload
Raven.captureException(err); Raven.captureException(err);
$pageOne.attr('hidden', true); pageOne.hidden = true;
$uploadProgress.attr('hidden', true); uploadProgress.hidden = true;
$uploadError.removeAttr('hidden'); uploadError.hidden = false;
window.clearTimeout(t); window.clearTimeout(t);
metrics.stoppedUpload({ metrics.stoppedUpload({
@ -244,6 +191,53 @@ $(() => {
}, 10); }, 10);
} }
document.addEventListener('DOMContentLoaded', function() {
gcmCompliant()
.then(function() {
const pageOne = document.getElementById('page-one');
const copyBtn = document.getElementById('copy-btn');
const link = document.getElementById('link');
const uploadWindow = document.querySelector('.upload-window');
pageOne.hidden = false;
document.getElementById('file-upload').addEventListener('change', upload);
document.body.addEventListener('dragover', allowDrop);
document.body.addEventListener('drop', upload);
// reset copy button
copyBtn.disabled = !allowedCopy();
copyBtn.setAttribute('data-l10n-id', 'copyUrlFormButton');
link.disabled = false;
// copy link to clipboard
copyBtn.addEventListener('click', () => {
if (allowedCopy() && copyToClipboard(link.getAttribute('value'))) {
metrics.copiedLink({ location: 'success-screen' });
//disable button for 3s
copyBtn.disabled = true;
link.disabled = true;
copyBtn.innerHtml =
'<img src="/resources/check-16.svg" class="icon-check"></img>';
setTimeout(() => {
copyBtn.disabled = !allowedCopy();
copyBtn.setAttribute('data-l10n-id', 'copyUrlFormButton');
link.disabled = false;
}, 3000);
}
});
uploadWindow.addEventListener('dragover', () =>
uploadWindow.classList.add('ondrag')
);
uploadWindow.addEventListener('dragleave', () =>
uploadWindow.classList.remove('ondrag')
);
// on file upload by browse or drag & drop
function allowDrop(ev) { function allowDrop(ev) {
ev.preventDefault(); ev.preventDefault();
} }

118
package-lock.json generated
View File

@ -291,9 +291,9 @@
} }
}, },
"aws-sdk": { "aws-sdk": {
"version": "2.95.0", "version": "2.98.0",
"resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.95.0.tgz", "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.98.0.tgz",
"integrity": "sha1-JuIdsUlEOx8GOUnch5hPDRdwDmo=", "integrity": "sha1-kK0CPXM4ndFex736+TLsq2VEVxE=",
"requires": { "requires": {
"buffer": "4.9.1", "buffer": "4.9.1",
"crypto-browserify": "1.0.9", "crypto-browserify": "1.0.9",
@ -1086,6 +1086,17 @@
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz",
"integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw==" "integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw=="
}, },
"bel": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/bel/-/bel-5.0.3.tgz",
"integrity": "sha512-bMLvUOrKBM2zWp3Ab6UugjCjFmsZtIeKH3oMNWaUr9RA94sNeicajzptZHQWU3K8KNIL8o6JwAmKG1W3mUiwXw==",
"dev": true,
"requires": {
"hyperx": "2.3.0",
"is-electron": "2.1.0",
"pelo": "0.0.3"
}
},
"big.js": { "big.js": {
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/big.js/-/big.js-3.1.3.tgz", "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.1.3.tgz",
@ -2330,7 +2341,7 @@
"integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=",
"dev": true, "dev": true,
"requires": { "requires": {
"es5-ext": "0.10.26" "es5-ext": "0.10.27"
} }
}, },
"dasherize": { "dasherize": {
@ -2640,12 +2651,6 @@
} }
} }
}, },
"dom-walk": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz",
"integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg=",
"dev": true
},
"domain-browser": { "domain-browser": {
"version": "1.1.7", "version": "1.1.7",
"resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz", "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz",
@ -2801,9 +2806,9 @@
} }
}, },
"es5-ext": { "es5-ext": {
"version": "0.10.26", "version": "0.10.27",
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.26.tgz", "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.27.tgz",
"integrity": "sha1-UbISilMbcMT2dkCTpzy+u4IYY3I=", "integrity": "sha512-3KXJRYzKXTd7xfFy5uZsJCXue55fAYQ035PRjyYk2PicllxIwcW9l3AbM/eGaw3vgVAUW4tl4xg9AXDEI6yw0w==",
"dev": true, "dev": true,
"requires": { "requires": {
"es6-iterator": "2.0.1", "es6-iterator": "2.0.1",
@ -2817,7 +2822,7 @@
"dev": true, "dev": true,
"requires": { "requires": {
"d": "1.0.0", "d": "1.0.0",
"es5-ext": "0.10.26", "es5-ext": "0.10.27",
"es6-symbol": "3.1.1" "es6-symbol": "3.1.1"
} }
}, },
@ -2828,7 +2833,7 @@
"dev": true, "dev": true,
"requires": { "requires": {
"d": "1.0.0", "d": "1.0.0",
"es5-ext": "0.10.26", "es5-ext": "0.10.27",
"es6-iterator": "2.0.1", "es6-iterator": "2.0.1",
"es6-set": "0.1.5", "es6-set": "0.1.5",
"es6-symbol": "3.1.1", "es6-symbol": "3.1.1",
@ -2848,7 +2853,7 @@
"dev": true, "dev": true,
"requires": { "requires": {
"d": "1.0.0", "d": "1.0.0",
"es5-ext": "0.10.26", "es5-ext": "0.10.27",
"es6-iterator": "2.0.1", "es6-iterator": "2.0.1",
"es6-symbol": "3.1.1", "es6-symbol": "3.1.1",
"event-emitter": "0.3.5" "event-emitter": "0.3.5"
@ -2861,7 +2866,7 @@
"dev": true, "dev": true,
"requires": { "requires": {
"d": "1.0.0", "d": "1.0.0",
"es5-ext": "0.10.26" "es5-ext": "0.10.27"
} }
}, },
"es6-weak-map": { "es6-weak-map": {
@ -2871,7 +2876,7 @@
"dev": true, "dev": true,
"requires": { "requires": {
"d": "1.0.0", "d": "1.0.0",
"es5-ext": "0.10.26", "es5-ext": "0.10.27",
"es6-iterator": "2.0.1", "es6-iterator": "2.0.1",
"es6-symbol": "3.1.1" "es6-symbol": "3.1.1"
} }
@ -3142,7 +3147,7 @@
"dev": true, "dev": true,
"requires": { "requires": {
"d": "1.0.0", "d": "1.0.0",
"es5-ext": "0.10.26" "es5-ext": "0.10.27"
} }
}, },
"event-stream": { "event-stream": {
@ -4520,24 +4525,6 @@
"is-glob": "2.0.1" "is-glob": "2.0.1"
} }
}, },
"global": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz",
"integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=",
"dev": true,
"requires": {
"min-document": "2.19.0",
"process": "0.5.2"
},
"dependencies": {
"process": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz",
"integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=",
"dev": true
}
}
},
"globals": { "globals": {
"version": "9.18.0", "version": "9.18.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
@ -4784,6 +4771,21 @@
} }
} }
}, },
"hyperscript-attribute-to-property": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/hyperscript-attribute-to-property/-/hyperscript-attribute-to-property-1.0.0.tgz",
"integrity": "sha1-glMI1Ju44pV5I/cxmBvMgRytev8=",
"dev": true
},
"hyperx": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/hyperx/-/hyperx-2.3.0.tgz",
"integrity": "sha1-cPRz1m1K1VDd0cg+S+JlEna78eI=",
"dev": true,
"requires": {
"hyperscript-attribute-to-property": "1.0.0"
}
},
"iconv-lite": { "iconv-lite": {
"version": "0.4.15", "version": "0.4.15",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.15.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.15.tgz",
@ -5064,6 +5066,12 @@
"integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=",
"dev": true "dev": true
}, },
"is-electron": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-electron/-/is-electron-2.1.0.tgz",
"integrity": "sha512-dkg5xT383+M6zIbbXW/z7n2nz4SFUi2OSyhntnFYkRdtV+HVEfdjEK+5AWisfYgkpe3WYjTIuh7toaKmSfFVWw==",
"dev": true
},
"is-equal-shallow": { "is-equal-shallow": {
"version": "0.1.3", "version": "0.1.3",
"resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz",
@ -5260,12 +5268,6 @@
"resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz",
"integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc="
}, },
"jquery": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.2.1.tgz",
"integrity": "sha1-XE2d5lKvbNCncBVKYxu6ErAVx4c=",
"dev": true
},
"js-base64": { "js-base64": {
"version": "2.1.9", "version": "2.1.9",
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz",
@ -6324,15 +6326,6 @@
"integrity": "sha1-5md4PZLonb00KBi1IwudYqZyrRg=", "integrity": "sha1-5md4PZLonb00KBi1IwudYqZyrRg=",
"dev": true "dev": true
}, },
"min-document": {
"version": "2.19.0",
"resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz",
"integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=",
"dev": true,
"requires": {
"dom-walk": "0.1.1"
}
},
"minimalistic-assert": { "minimalistic-assert": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz",
@ -6574,7 +6567,7 @@
"stream-browserify": "2.0.1", "stream-browserify": "2.0.1",
"stream-http": "2.7.2", "stream-http": "2.7.2",
"string_decoder": "0.10.31", "string_decoder": "0.10.31",
"timers-browserify": "2.0.3", "timers-browserify": "2.0.4",
"tty-browserify": "0.0.0", "tty-browserify": "0.0.0",
"url": "0.11.0", "url": "0.11.0",
"util": "0.10.3", "util": "0.10.3",
@ -6638,12 +6631,11 @@
} }
}, },
"timers-browserify": { "timers-browserify": {
"version": "2.0.3", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.3.tgz", "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.4.tgz",
"integrity": "sha512-+JAqyNgg+M8+gXIrq2EeUr4kZqRz47Ysco7X5QKRGScRE9HIHckyHD1asozSFGeqx2nmPCgA8T5tIGVO0ML7/w==", "integrity": "sha512-uZYhyU3EX8O7HQP+J9fTVYwsq90Vr68xPEFo7yrVImIxYvHgukBEgOB/SgGoorWVTzGM/3Z+wUNnboA4M8jWrg==",
"dev": true, "dev": true,
"requires": { "requires": {
"global": "4.3.2",
"setimmediate": "1.0.5" "setimmediate": "1.0.5"
} }
}, },
@ -7046,6 +7038,12 @@
"sha.js": "2.4.8" "sha.js": "2.4.8"
} }
}, },
"pelo": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/pelo/-/pelo-0.0.3.tgz",
"integrity": "sha1-+2smsGEgNtsCuRj+qrPowMTLWXw=",
"dev": true
},
"pify": { "pify": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
@ -10560,9 +10558,9 @@
} }
}, },
"webpack": { "webpack": {
"version": "3.5.2", "version": "3.5.4",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-3.5.2.tgz", "resolved": "https://registry.npmjs.org/webpack/-/webpack-3.5.4.tgz",
"integrity": "sha1-qWAQZuI688gPO/l1j9eUypd48lE=", "integrity": "sha1-VYPrJj7Se3i1vRe/37DrGxzRv4E=",
"dev": true, "dev": true,
"requires": { "requires": {
"acorn": "5.1.1", "acorn": "5.1.1",

View File

@ -4,7 +4,7 @@
"version": "1.1.0", "version": "1.1.0",
"author": "Mozilla (https://mozilla.org)", "author": "Mozilla (https://mozilla.org)",
"dependencies": { "dependencies": {
"aws-sdk": "^2.95.0", "aws-sdk": "^2.98.0",
"body-parser": "^1.17.2", "body-parser": "^1.17.2",
"connect-busboy": "0.0.2", "connect-busboy": "0.0.2",
"convict": "^3.0.0", "convict": "^3.0.0",
@ -23,6 +23,7 @@
"babel-polyfill": "^6.23.0", "babel-polyfill": "^6.23.0",
"babel-preset-es2015": "^6.24.1", "babel-preset-es2015": "^6.24.1",
"babel-preset-stage-2": "^6.24.1", "babel-preset-stage-2": "^6.24.1",
"bel": "^5.0.3",
"browserify": "^14.4.0", "browserify": "^14.4.0",
"cross-env": "^5.0.5", "cross-env": "^5.0.5",
"css-mqpacker": "^6.0.1", "css-mqpacker": "^6.0.1",
@ -33,7 +34,6 @@
"eslint-plugin-security": "^1.4.0", "eslint-plugin-security": "^1.4.0",
"git-rev-sync": "^1.9.1", "git-rev-sync": "^1.9.1",
"husky": "^0.14.3", "husky": "^0.14.3",
"jquery": "^3.2.1",
"l20n": "^5.0.0", "l20n": "^5.0.0",
"lint-staged": "^4.0.3", "lint-staged": "^4.0.3",
"mocha": "^3.4.2", "mocha": "^3.4.2",
@ -50,7 +50,7 @@
"supertest": "^3.0.0", "supertest": "^3.0.0",
"testpilot-ga": "^0.3.0", "testpilot-ga": "^0.3.0",
"webcrypto-liner": "^0.1.25", "webcrypto-liner": "^0.1.25",
"webpack": "^3.5.2", "webpack": "^3.5.4",
"webpack-dev-middleware": "^1.12.0" "webpack-dev-middleware": "^1.12.0"
}, },
"engines": { "engines": {

View File

@ -11,15 +11,15 @@ module.exports = {
publicPath: '/' publicPath: '/'
}, },
module: { module: {
loaders: [ rules: [
{ {
test: /\.js$/, test: /\.js$/,
loaders: 'babel-loader', loader: 'babel-loader',
include: [ include: [
path.resolve(__dirname, 'frontend'), path.resolve(__dirname, 'frontend'),
path.resolve(__dirname, 'node_modules/testpilot-ga/src') path.resolve(__dirname, 'node_modules/testpilot-ga/src')
], ],
query: { options: {
babelrc: false, babelrc: false,
presets: [['es2015', { modules: false }], 'stage-2'] presets: [['es2015', { modules: false }], 'stage-2']
} }