Merge pull request #515 from mozilla/refactor-upload
removed jquery from upload.js
This commit is contained in:
commit
856b2cdc60
@ -2,17 +2,15 @@ import FileSender from './fileSender';
|
||||
import Storage from './storage';
|
||||
import * as metrics from './metrics';
|
||||
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();
|
||||
let fileList = null;
|
||||
let $link = null;
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
$link = $('#link');
|
||||
fileList = document.getElementById('file-list');
|
||||
toggleHeader();
|
||||
// eslint-disable-next-line prefer-const
|
||||
Promise.all(
|
||||
storage.files.map(file => {
|
||||
const id = file.fileId;
|
||||
@ -33,180 +31,115 @@ function toggleHeader() {
|
||||
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) {
|
||||
if (!file) {
|
||||
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);
|
||||
|
||||
const url = `${file.url}#${file.secretKey}`;
|
||||
const future = new Date();
|
||||
future.setTime(file.creationDate.getTime() + file.expiry);
|
||||
const countdown = future.getTime() - Date.now();
|
||||
|
||||
let countdown = 0;
|
||||
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);
|
||||
|
||||
const poll = () => {
|
||||
countdown = future.getTime() - Date.now();
|
||||
minutes = Math.floor(countdown / 1000 / 60);
|
||||
hours = Math.floor(minutes / 60);
|
||||
seconds = Math.floor(countdown / 1000 % 60);
|
||||
let t;
|
||||
|
||||
if (hours >= 1) {
|
||||
expiry.innerHTML = hours + 'h ' + minutes % 60 + 'm';
|
||||
t = setTimeout(() => {
|
||||
poll();
|
||||
}, 60000);
|
||||
} else if (hours === 0) {
|
||||
expiry.innerHTML = minutes + 'm ' + seconds + 's';
|
||||
t = window.setTimeout(() => {
|
||||
poll();
|
||||
}, 1000);
|
||||
}
|
||||
//remove from list when expired
|
||||
if (countdown <= 0) {
|
||||
storage.remove(file.fileId);
|
||||
$(expiry).parents('tr').remove();
|
||||
window.clearTimeout(t);
|
||||
toggleHeader();
|
||||
}
|
||||
};
|
||||
const row = bel`
|
||||
<tr>
|
||||
<td>${file.name}</td>
|
||||
<td>
|
||||
<span class="icon-docs" data-l10n-id="copyUrlHover"></span>
|
||||
<img onclick=${copyClick} src="/resources/copy-16.svg" class="icon-copy" data-l10n-id="copyUrlHover">
|
||||
<span data-l10n-id="copiedUrl" class="text-copied" hidden="true"></span>
|
||||
</td>
|
||||
<td>${timeLeft(countdown)}</td>
|
||||
<td>
|
||||
<span class="icon-cancel-1" data-l10n-id="deleteButtonHover" title="Delete"></span>
|
||||
<img onclick=${showPopup} src="/resources/close-16.svg" class="icon-delete" data-l10n-id="deleteButtonHover" title="Delete">
|
||||
<div class="popup">
|
||||
<div class="popuptext" onclick=${stopProp} onblur=${cancel} tabindex="-1">
|
||||
<div class="popup-message" data-l10n-id="deletePopupText"></div>
|
||||
<div class="popup-action">
|
||||
<span class="popup-no" onclick=${cancel} data-l10n-id="deletePopupCancel"></span>
|
||||
<span class="popup-yes" onclick=${deleteFile} data-l10n-id="deletePopupYes"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
const popup = row.querySelector('.popuptext');
|
||||
const timeCol = row.querySelectorAll('td')[2];
|
||||
if (!allowedCopy()) {
|
||||
row.querySelector('.icon-copy').disabled = true;
|
||||
}
|
||||
|
||||
fileList.querySelector('tbody').appendChild(row);
|
||||
toggleHeader();
|
||||
poll();
|
||||
|
||||
// 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');
|
||||
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);
|
||||
}
|
||||
|
||||
$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());
|
||||
metrics
|
||||
.deletedUpload({
|
||||
size: file.size,
|
||||
time: file.totalTime,
|
||||
speed: file.uploadSpeed,
|
||||
type: file.typeOfUpload,
|
||||
location: 'upload-list',
|
||||
ttl
|
||||
})
|
||||
.then(() => {
|
||||
storage.remove(file.fileId);
|
||||
});
|
||||
function poll() {
|
||||
const countdown = future.getTime() - Date.now();
|
||||
if (countdown <= 0) {
|
||||
storage.remove(file.fileId);
|
||||
row.parentNode.removeChild(row);
|
||||
toggleHeader();
|
||||
}
|
||||
timeCol.textContent = timeLeft(countdown);
|
||||
setTimeout(poll, countdown >= HOUR ? 60000 : 1000);
|
||||
}
|
||||
|
||||
function deleteFile() {
|
||||
FileSender.delete(file.fileId, file.deleteToken);
|
||||
const ttl = ONE_DAY_IN_MS - (Date.now() - file.creationDate.getTime());
|
||||
metrics.deletedUpload({
|
||||
size: file.size,
|
||||
time: file.totalTime,
|
||||
speed: file.uploadSpeed,
|
||||
type: file.typeOfUpload,
|
||||
location: 'upload-list',
|
||||
ttl
|
||||
});
|
||||
});
|
||||
row.parentNode.removeChild(row);
|
||||
storage.remove(file.fileId);
|
||||
toggleHeader();
|
||||
}
|
||||
|
||||
// show popup
|
||||
$delIcon.on('click', () => {
|
||||
$popupText.addClass('show').focus();
|
||||
});
|
||||
function showPopup() {
|
||||
popup.classList.add('show');
|
||||
popup.focus();
|
||||
}
|
||||
|
||||
// hide popup
|
||||
$popupText.find('.popup-no').on('click', e => {
|
||||
function cancel(e) {
|
||||
e.stopPropagation();
|
||||
$popupText.removeClass('show');
|
||||
});
|
||||
popup.classList.remove('show');
|
||||
}
|
||||
|
||||
$popupText.on('click', e => {
|
||||
function stopProp(e) {
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
//close when popup loses focus
|
||||
$popupText.on('blur', () => {
|
||||
$popupText.removeClass('show');
|
||||
});
|
||||
|
||||
toggleHeader();
|
||||
}
|
||||
}
|
||||
|
||||
async function checkExistence(id) {
|
||||
|
@ -7,6 +7,14 @@ export default class FileSender extends EventEmitter {
|
||||
this.file = file;
|
||||
this.iv = window.crypto.getRandomValues(new Uint8Array(12));
|
||||
this.uploadXHR = new XMLHttpRequest();
|
||||
this.key = window.crypto.subtle.generateKey(
|
||||
{
|
||||
name: 'AES-GCM',
|
||||
length: 128
|
||||
},
|
||||
true,
|
||||
['encrypt']
|
||||
);
|
||||
}
|
||||
|
||||
static delete(fileId, token) {
|
||||
@ -32,87 +40,79 @@ export default class FileSender extends EventEmitter {
|
||||
this.uploadXHR.abort();
|
||||
}
|
||||
|
||||
upload() {
|
||||
const self = this;
|
||||
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();
|
||||
reader.readAsArrayBuffer(this.file);
|
||||
reader.onload = function(event) {
|
||||
const plaintext = new Uint8Array(this.result);
|
||||
resolve(plaintext);
|
||||
};
|
||||
reader.onerror = function(err) {
|
||||
reject(err);
|
||||
};
|
||||
})
|
||||
])
|
||||
.then(([secretKey, plaintext]) => {
|
||||
self.emit('encrypting');
|
||||
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) => {
|
||||
const file = this.file;
|
||||
const fileId = arrayToHex(this.iv);
|
||||
const dataView = new DataView(encrypted);
|
||||
const blob = new Blob([dataView], { type: file.type });
|
||||
const fd = new FormData();
|
||||
fd.append('data', blob, file.name);
|
||||
readFile() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.readAsArrayBuffer(this.file);
|
||||
reader.onload = function(event) {
|
||||
const plaintext = new Uint8Array(this.result);
|
||||
resolve(plaintext);
|
||||
};
|
||||
reader.onerror = function(err) {
|
||||
reject(err);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
const xhr = self.uploadXHR;
|
||||
uploadFile(encrypted, keydata) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const file = this.file;
|
||||
const fileId = arrayToHex(this.iv);
|
||||
const dataView = new DataView(encrypted);
|
||||
const blob = new Blob([dataView], { type: file.type });
|
||||
const fd = new FormData();
|
||||
fd.append('data', blob, file.name);
|
||||
|
||||
xhr.upload.addEventListener('progress', e => {
|
||||
if (e.lengthComputable) {
|
||||
self.emit('progress', [e.loaded, e.total]);
|
||||
}
|
||||
});
|
||||
const xhr = this.uploadXHR;
|
||||
|
||||
xhr.onreadystatechange = () => {
|
||||
if (xhr.readyState === XMLHttpRequest.DONE) {
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
xhr.open('post', '/upload', true);
|
||||
xhr.setRequestHeader(
|
||||
'X-File-Metadata',
|
||||
JSON.stringify({
|
||||
id: fileId,
|
||||
filename: encodeURIComponent(file.name)
|
||||
})
|
||||
);
|
||||
xhr.send(fd);
|
||||
});
|
||||
xhr.upload.addEventListener('progress', e => {
|
||||
if (e.lengthComputable) {
|
||||
this.emit('progress', [e.loaded, e.total]);
|
||||
}
|
||||
});
|
||||
|
||||
xhr.onreadystatechange = () => {
|
||||
if (xhr.readyState === XMLHttpRequest.DONE) {
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
xhr.open('post', '/upload', true);
|
||||
xhr.setRequestHeader(
|
||||
'X-File-Metadata',
|
||||
JSON.stringify({
|
||||
id: fileId,
|
||||
filename: encodeURIComponent(file.name)
|
||||
})
|
||||
);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -310,6 +310,10 @@ tbody {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.text-copied {
|
||||
color: #0a8dff;
|
||||
}
|
||||
|
||||
/* Popup container */
|
||||
.popup {
|
||||
position: absolute;
|
||||
|
@ -13,236 +13,230 @@ import Storage from './storage';
|
||||
import * as metrics from './metrics';
|
||||
import * as progress from './progress';
|
||||
import * as fileList from './fileList';
|
||||
import $ from 'jquery/dist/jquery.slim';
|
||||
|
||||
const storage = new Storage();
|
||||
|
||||
$(() => {
|
||||
async function upload(event) {
|
||||
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';
|
||||
|
||||
// don't allow upload if not on upload page
|
||||
if (pageOne.hidden) {
|
||||
return;
|
||||
}
|
||||
|
||||
storage.totalUploads += 1;
|
||||
|
||||
let file = '';
|
||||
if (clickOrDrop === 'drop') {
|
||||
if (!event.originalEvent.dataTransfer.files[0]) {
|
||||
uploadWindow.classList.remove('ondrag');
|
||||
return;
|
||||
}
|
||||
if (
|
||||
event.originalEvent.dataTransfer.files.length > 1 ||
|
||||
event.originalEvent.dataTransfer.files[0].size === 0
|
||||
) {
|
||||
uploadWindow.classList.remove('ondrag');
|
||||
document.l10n.formatValue('uploadPageMultipleFilesAlert').then(str => {
|
||||
alert(str);
|
||||
});
|
||||
return;
|
||||
}
|
||||
file = event.originalEvent.dataTransfer.files[0];
|
||||
} else {
|
||||
file = event.target.files[0];
|
||||
}
|
||||
|
||||
if (file.size > MAXFILESIZE) {
|
||||
return document.l10n
|
||||
.formatValue('fileTooBig', { size: bytes(MAXFILESIZE) })
|
||||
.then(alert);
|
||||
}
|
||||
|
||||
pageOne.hidden = true;
|
||||
uploadError.hidden = true;
|
||||
uploadProgress.hidden = false;
|
||||
document.l10n
|
||||
.formatValue('uploadingPageProgress', {
|
||||
size: bytes(file.size),
|
||||
filename: file.name
|
||||
})
|
||||
.then(str => {
|
||||
document.getElementById('upload-filename').textContent = str;
|
||||
});
|
||||
document.l10n.formatValue('importingFile').then(progress.setText);
|
||||
//don't allow drag and drop when not on page-one
|
||||
document.body.removeEventListener('drop', upload);
|
||||
|
||||
const fileSender = new FileSender(file);
|
||||
document.getElementById('cancel-upload').addEventListener('click', () => {
|
||||
fileSender.cancel();
|
||||
metrics.cancelledUpload({
|
||||
size: file.size,
|
||||
type: clickOrDrop
|
||||
});
|
||||
location.reload();
|
||||
});
|
||||
|
||||
let uploadStart;
|
||||
fileSender.on('progress', data => {
|
||||
uploadStart = uploadStart || Date.now();
|
||||
progress.setProgress({
|
||||
complete: data[0],
|
||||
total: data[1]
|
||||
});
|
||||
});
|
||||
|
||||
fileSender.on('encrypting', () => {
|
||||
document.l10n.formatValue('encryptingFile').then(progress.setText);
|
||||
});
|
||||
|
||||
let t;
|
||||
const startTime = Date.now();
|
||||
metrics.startedUpload({
|
||||
size: file.size,
|
||||
type: clickOrDrop
|
||||
});
|
||||
// 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 time = endTime - startTime;
|
||||
const uploadTime = endTime - uploadStart;
|
||||
const speed = file.size / (uploadTime / 1000);
|
||||
const expiration = EXPIRE_SECONDS * 1000;
|
||||
|
||||
link.setAttribute('value', `${info.url}#${info.secretKey}`);
|
||||
|
||||
metrics.completedUpload({
|
||||
size: file.size,
|
||||
time,
|
||||
speed,
|
||||
type: clickOrDrop
|
||||
});
|
||||
|
||||
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: time,
|
||||
typeOfUpload: clickOrDrop,
|
||||
uploadSpeed: speed
|
||||
};
|
||||
|
||||
document.getElementById('delete-file').addEventListener('click', () => {
|
||||
FileSender.delete(fileData.fileId, fileData.deleteToken).then(() => {
|
||||
const ttl =
|
||||
ONE_DAY_IN_MS - (Date.now() - fileData.creationDate.getTime());
|
||||
metrics
|
||||
.deletedUpload({
|
||||
size: fileData.size,
|
||||
time: fileData.totalTime,
|
||||
speed: fileData.uploadSpeed,
|
||||
type: fileData.typeOfUpload,
|
||||
location: 'success-screen',
|
||||
ttl
|
||||
})
|
||||
.then(() => {
|
||||
storage.remove(fileData.fileId);
|
||||
location.reload();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
storage.addFile(info.fileId, fileData);
|
||||
|
||||
pageOne.hidden = true;
|
||||
uploadProgress.hidden = true;
|
||||
uploadError.hidden = true;
|
||||
document.getElementById('share-link').hidden = false;
|
||||
|
||||
fileList.addFile(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);
|
||||
pageOne.hidden = true;
|
||||
uploadProgress.hidden = true;
|
||||
uploadError.hidden = false;
|
||||
window.clearTimeout(t);
|
||||
|
||||
metrics.stoppedUpload({
|
||||
size: file.size,
|
||||
type: clickOrDrop,
|
||||
err
|
||||
});
|
||||
});
|
||||
}, 10);
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
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');
|
||||
const pageOne = document.getElementById('page-one');
|
||||
const copyBtn = document.getElementById('copy-btn');
|
||||
const link = document.getElementById('link');
|
||||
const uploadWindow = document.querySelector('.upload-window');
|
||||
|
||||
$pageOne.removeAttr('hidden');
|
||||
$('#file-upload').on('change', onUpload);
|
||||
pageOne.hidden = false;
|
||||
document.getElementById('file-upload').addEventListener('change', upload);
|
||||
|
||||
$(document.body).on('dragover', allowDrop).on('drop', onUpload);
|
||||
document.body.addEventListener('dragover', allowDrop);
|
||||
document.body.addEventListener('drop', upload);
|
||||
|
||||
// reset copy button
|
||||
$copyBtn.attr({
|
||||
disabled: !allowedCopy(),
|
||||
'data-l10n-id': 'copyUrlFormButton'
|
||||
});
|
||||
copyBtn.disabled = !allowedCopy();
|
||||
copyBtn.setAttribute('data-l10n-id', 'copyUrlFormButton');
|
||||
|
||||
$link.attr('disabled', false);
|
||||
link.disabled = false;
|
||||
|
||||
// copy link to clipboard
|
||||
$copyBtn.on('click', () => {
|
||||
if (allowedCopy() && copyToClipboard($link.attr('value'))) {
|
||||
copyBtn.addEventListener('click', () => {
|
||||
if (allowedCopy() && copyToClipboard(link.getAttribute('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>'
|
||||
);
|
||||
copyBtn.disabled = true;
|
||||
link.disabled = true;
|
||||
copyBtn.innerHtml =
|
||||
'<img src="/resources/check-16.svg" class="icon-check"></img>';
|
||||
setTimeout(() => {
|
||||
$copyBtn.attr({
|
||||
disabled: false,
|
||||
'data-l10n-id': 'copyUrlFormButton'
|
||||
});
|
||||
$link.attr('disabled', false);
|
||||
copyBtn.disabled = !allowedCopy();
|
||||
copyBtn.setAttribute('data-l10n-id', 'copyUrlFormButton');
|
||||
link.disabled = false;
|
||||
}, 3000);
|
||||
}
|
||||
});
|
||||
|
||||
$uploadWindow
|
||||
.on('dragover', () => {
|
||||
$uploadWindow.addClass('ondrag');
|
||||
})
|
||||
.on('dragleave', () => {
|
||||
$uploadWindow.removeClass('ondrag');
|
||||
});
|
||||
uploadWindow.addEventListener('dragover', () =>
|
||||
uploadWindow.classList.add('ondrag')
|
||||
);
|
||||
uploadWindow.addEventListener('dragleave', () =>
|
||||
uploadWindow.classList.remove('ondrag')
|
||||
);
|
||||
|
||||
// on file upload by browse or drag & drop
|
||||
function onUpload(event) {
|
||||
event.preventDefault();
|
||||
const clickOrDrop = event.type === 'drop' ? 'drop' : 'click';
|
||||
|
||||
// don't allow upload if not on upload page
|
||||
if ($pageOne.attr('hidden')) {
|
||||
return;
|
||||
}
|
||||
|
||||
storage.totalUploads += 1;
|
||||
|
||||
let file = '';
|
||||
if (clickOrDrop === 'drop') {
|
||||
if (!event.originalEvent.dataTransfer.files[0]) {
|
||||
$uploadWindow.removeClass('ondrag');
|
||||
return;
|
||||
}
|
||||
if (
|
||||
event.originalEvent.dataTransfer.files.length > 1 ||
|
||||
event.originalEvent.dataTransfer.files[0].size === 0
|
||||
) {
|
||||
$uploadWindow.removeClass('ondrag');
|
||||
document.l10n
|
||||
.formatValue('uploadPageMultipleFilesAlert')
|
||||
.then(str => {
|
||||
alert(str);
|
||||
});
|
||||
return;
|
||||
}
|
||||
file = event.originalEvent.dataTransfer.files[0];
|
||||
} else {
|
||||
file = event.target.files[0];
|
||||
}
|
||||
|
||||
if (file.size > MAXFILESIZE) {
|
||||
return document.l10n
|
||||
.formatValue('fileTooBig', { size: bytes(MAXFILESIZE) })
|
||||
.then(alert);
|
||||
}
|
||||
|
||||
$pageOne.attr('hidden', true);
|
||||
$uploadError.attr('hidden', true);
|
||||
$uploadProgress.removeAttr('hidden');
|
||||
document.l10n
|
||||
.formatValue('uploadingPageProgress', {
|
||||
size: bytes(file.size),
|
||||
filename: file.name
|
||||
})
|
||||
.then(str => {
|
||||
$('#upload-filename').text(str);
|
||||
});
|
||||
document.l10n.formatValue('importingFile').then(progress.setText);
|
||||
//don't allow drag and drop when not on page-one
|
||||
$(document.body).off('drop', onUpload);
|
||||
|
||||
const fileSender = new FileSender(file);
|
||||
$('#cancel-upload').on('click', () => {
|
||||
fileSender.cancel();
|
||||
metrics.cancelledUpload({
|
||||
size: file.size,
|
||||
type: clickOrDrop
|
||||
});
|
||||
location.reload();
|
||||
});
|
||||
|
||||
let uploadStart;
|
||||
fileSender.on('progress', data => {
|
||||
uploadStart = uploadStart || Date.now();
|
||||
progress.setProgress({
|
||||
complete: data[0],
|
||||
total: data[1]
|
||||
});
|
||||
});
|
||||
|
||||
fileSender.on('encrypting', () => {
|
||||
document.l10n.formatValue('encryptingFile').then(progress.setText);
|
||||
});
|
||||
|
||||
let t;
|
||||
const startTime = Date.now();
|
||||
metrics.startedUpload({
|
||||
size: file.size,
|
||||
type: clickOrDrop
|
||||
});
|
||||
// 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 time = endTime - startTime;
|
||||
const uploadTime = endTime - uploadStart;
|
||||
const speed = file.size / (uploadTime / 1000);
|
||||
const expiration = EXPIRE_SECONDS * 1000;
|
||||
|
||||
metrics.completedUpload({
|
||||
size: file.size,
|
||||
time,
|
||||
speed,
|
||||
type: clickOrDrop
|
||||
});
|
||||
|
||||
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: time,
|
||||
typeOfUpload: clickOrDrop,
|
||||
uploadSpeed: speed
|
||||
};
|
||||
|
||||
$('#delete-file').on('click', () => {
|
||||
FileSender.delete(
|
||||
fileData.fileId,
|
||||
fileData.deleteToken
|
||||
).then(() => {
|
||||
const ttl =
|
||||
ONE_DAY_IN_MS -
|
||||
(Date.now() - fileData.creationDate.getTime());
|
||||
metrics
|
||||
.deletedUpload({
|
||||
size: fileData.size,
|
||||
time: fileData.totalTime,
|
||||
speed: fileData.uploadSpeed,
|
||||
type: fileData.typeOfUpload,
|
||||
location: 'success-screen',
|
||||
ttl
|
||||
})
|
||||
.then(() => {
|
||||
storage.remove(fileData.fileId);
|
||||
location.reload();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
storage.addFile(info.fileId, fileData);
|
||||
|
||||
$pageOne.attr('hidden', true);
|
||||
$uploadProgress.attr('hidden', true);
|
||||
$uploadError.attr('hidden', true);
|
||||
$('#share-link').removeAttr('hidden');
|
||||
|
||||
fileList.addFile(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);
|
||||
$pageOne.attr('hidden', true);
|
||||
$uploadProgress.attr('hidden', true);
|
||||
$uploadError.removeAttr('hidden');
|
||||
window.clearTimeout(t);
|
||||
|
||||
metrics.stoppedUpload({
|
||||
size: file.size,
|
||||
type: clickOrDrop,
|
||||
err
|
||||
});
|
||||
});
|
||||
}, 10);
|
||||
}
|
||||
|
||||
function allowDrop(ev) {
|
||||
ev.preventDefault();
|
||||
|
118
package-lock.json
generated
118
package-lock.json
generated
@ -291,9 +291,9 @@
|
||||
}
|
||||
},
|
||||
"aws-sdk": {
|
||||
"version": "2.95.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.95.0.tgz",
|
||||
"integrity": "sha1-JuIdsUlEOx8GOUnch5hPDRdwDmo=",
|
||||
"version": "2.98.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.98.0.tgz",
|
||||
"integrity": "sha1-kK0CPXM4ndFex736+TLsq2VEVxE=",
|
||||
"requires": {
|
||||
"buffer": "4.9.1",
|
||||
"crypto-browserify": "1.0.9",
|
||||
@ -1086,6 +1086,17 @@
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz",
|
||||
"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": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/big.js/-/big.js-3.1.3.tgz",
|
||||
@ -2330,7 +2341,7 @@
|
||||
"integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"es5-ext": "0.10.26"
|
||||
"es5-ext": "0.10.27"
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
"version": "1.1.7",
|
||||
"resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz",
|
||||
@ -2801,9 +2806,9 @@
|
||||
}
|
||||
},
|
||||
"es5-ext": {
|
||||
"version": "0.10.26",
|
||||
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.26.tgz",
|
||||
"integrity": "sha1-UbISilMbcMT2dkCTpzy+u4IYY3I=",
|
||||
"version": "0.10.27",
|
||||
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.27.tgz",
|
||||
"integrity": "sha512-3KXJRYzKXTd7xfFy5uZsJCXue55fAYQ035PRjyYk2PicllxIwcW9l3AbM/eGaw3vgVAUW4tl4xg9AXDEI6yw0w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"es6-iterator": "2.0.1",
|
||||
@ -2817,7 +2822,7 @@
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"d": "1.0.0",
|
||||
"es5-ext": "0.10.26",
|
||||
"es5-ext": "0.10.27",
|
||||
"es6-symbol": "3.1.1"
|
||||
}
|
||||
},
|
||||
@ -2828,7 +2833,7 @@
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"d": "1.0.0",
|
||||
"es5-ext": "0.10.26",
|
||||
"es5-ext": "0.10.27",
|
||||
"es6-iterator": "2.0.1",
|
||||
"es6-set": "0.1.5",
|
||||
"es6-symbol": "3.1.1",
|
||||
@ -2848,7 +2853,7 @@
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"d": "1.0.0",
|
||||
"es5-ext": "0.10.26",
|
||||
"es5-ext": "0.10.27",
|
||||
"es6-iterator": "2.0.1",
|
||||
"es6-symbol": "3.1.1",
|
||||
"event-emitter": "0.3.5"
|
||||
@ -2861,7 +2866,7 @@
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"d": "1.0.0",
|
||||
"es5-ext": "0.10.26"
|
||||
"es5-ext": "0.10.27"
|
||||
}
|
||||
},
|
||||
"es6-weak-map": {
|
||||
@ -2871,7 +2876,7 @@
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"d": "1.0.0",
|
||||
"es5-ext": "0.10.26",
|
||||
"es5-ext": "0.10.27",
|
||||
"es6-iterator": "2.0.1",
|
||||
"es6-symbol": "3.1.1"
|
||||
}
|
||||
@ -3142,7 +3147,7 @@
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"d": "1.0.0",
|
||||
"es5-ext": "0.10.26"
|
||||
"es5-ext": "0.10.27"
|
||||
}
|
||||
},
|
||||
"event-stream": {
|
||||
@ -4520,24 +4525,6 @@
|
||||
"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": {
|
||||
"version": "9.18.0",
|
||||
"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": {
|
||||
"version": "0.4.15",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.15.tgz",
|
||||
@ -5064,6 +5066,12 @@
|
||||
"integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=",
|
||||
"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": {
|
||||
"version": "0.1.3",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "2.1.9",
|
||||
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz",
|
||||
@ -6324,15 +6326,6 @@
|
||||
"integrity": "sha1-5md4PZLonb00KBi1IwudYqZyrRg=",
|
||||
"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": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz",
|
||||
@ -6574,7 +6567,7 @@
|
||||
"stream-browserify": "2.0.1",
|
||||
"stream-http": "2.7.2",
|
||||
"string_decoder": "0.10.31",
|
||||
"timers-browserify": "2.0.3",
|
||||
"timers-browserify": "2.0.4",
|
||||
"tty-browserify": "0.0.0",
|
||||
"url": "0.11.0",
|
||||
"util": "0.10.3",
|
||||
@ -6638,12 +6631,11 @@
|
||||
}
|
||||
},
|
||||
"timers-browserify": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.3.tgz",
|
||||
"integrity": "sha512-+JAqyNgg+M8+gXIrq2EeUr4kZqRz47Ysco7X5QKRGScRE9HIHckyHD1asozSFGeqx2nmPCgA8T5tIGVO0ML7/w==",
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.4.tgz",
|
||||
"integrity": "sha512-uZYhyU3EX8O7HQP+J9fTVYwsq90Vr68xPEFo7yrVImIxYvHgukBEgOB/SgGoorWVTzGM/3Z+wUNnboA4M8jWrg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"global": "4.3.2",
|
||||
"setimmediate": "1.0.5"
|
||||
}
|
||||
},
|
||||
@ -7046,6 +7038,12 @@
|
||||
"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": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
||||
@ -10560,9 +10558,9 @@
|
||||
}
|
||||
},
|
||||
"webpack": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-3.5.2.tgz",
|
||||
"integrity": "sha1-qWAQZuI688gPO/l1j9eUypd48lE=",
|
||||
"version": "3.5.4",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-3.5.4.tgz",
|
||||
"integrity": "sha1-VYPrJj7Se3i1vRe/37DrGxzRv4E=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"acorn": "5.1.1",
|
||||
|
@ -4,7 +4,7 @@
|
||||
"version": "1.1.0",
|
||||
"author": "Mozilla (https://mozilla.org)",
|
||||
"dependencies": {
|
||||
"aws-sdk": "^2.95.0",
|
||||
"aws-sdk": "^2.98.0",
|
||||
"body-parser": "^1.17.2",
|
||||
"connect-busboy": "0.0.2",
|
||||
"convict": "^3.0.0",
|
||||
@ -23,6 +23,7 @@
|
||||
"babel-polyfill": "^6.23.0",
|
||||
"babel-preset-es2015": "^6.24.1",
|
||||
"babel-preset-stage-2": "^6.24.1",
|
||||
"bel": "^5.0.3",
|
||||
"browserify": "^14.4.0",
|
||||
"cross-env": "^5.0.5",
|
||||
"css-mqpacker": "^6.0.1",
|
||||
@ -33,7 +34,6 @@
|
||||
"eslint-plugin-security": "^1.4.0",
|
||||
"git-rev-sync": "^1.9.1",
|
||||
"husky": "^0.14.3",
|
||||
"jquery": "^3.2.1",
|
||||
"l20n": "^5.0.0",
|
||||
"lint-staged": "^4.0.3",
|
||||
"mocha": "^3.4.2",
|
||||
@ -50,7 +50,7 @@
|
||||
"supertest": "^3.0.0",
|
||||
"testpilot-ga": "^0.3.0",
|
||||
"webcrypto-liner": "^0.1.25",
|
||||
"webpack": "^3.5.2",
|
||||
"webpack": "^3.5.4",
|
||||
"webpack-dev-middleware": "^1.12.0"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -11,15 +11,15 @@ module.exports = {
|
||||
publicPath: '/'
|
||||
},
|
||||
module: {
|
||||
loaders: [
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
loaders: 'babel-loader',
|
||||
loader: 'babel-loader',
|
||||
include: [
|
||||
path.resolve(__dirname, 'frontend'),
|
||||
path.resolve(__dirname, 'node_modules/testpilot-ga/src')
|
||||
],
|
||||
query: {
|
||||
options: {
|
||||
babelrc: false,
|
||||
presets: [['es2015', { modules: false }], 'stage-2']
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user