mirror of
https://gitlab.com/timvisee/send.git
synced 2025-12-06 22:20:55 +03:00
Compare commits
106 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87a8cfba40 | ||
|
|
f929bb2aec | ||
|
|
aaec16cf52 | ||
|
|
5da43f0da7 | ||
|
|
dffb26349b | ||
|
|
c4751842fe | ||
|
|
08ec9d98b8 | ||
|
|
5200928d5c | ||
|
|
4c3e37f4b5 | ||
|
|
60eda64c8d | ||
|
|
4db7fc6db3 | ||
|
|
5844a9a03c | ||
|
|
9829e46270 | ||
|
|
9a150ddb22 | ||
|
|
e7b90ea1b9 | ||
|
|
07bcfa2d36 | ||
|
|
dce134744d | ||
|
|
288bc50484 | ||
|
|
09d6c64a9b | ||
|
|
d7a542f2c2 | ||
|
|
aca82dd880 | ||
|
|
aa72a4c72c | ||
|
|
a70dcfc905 | ||
|
|
49731ab8ba | ||
|
|
3d9e01f8e5 | ||
|
|
441fe86186 | ||
|
|
f9283f5f6a | ||
|
|
c8e0b696a6 | ||
|
|
cf5c64a3f8 | ||
|
|
097ff19c5f | ||
|
|
8767b9b6b0 | ||
|
|
4ac7ac2b24 | ||
|
|
4cdce6c841 | ||
|
|
763414c848 | ||
|
|
9172af48fd | ||
|
|
14a4297db8 | ||
|
|
a78475aff4 | ||
|
|
d2b57039bf | ||
|
|
dd5be246d4 | ||
|
|
779005845e | ||
|
|
f3499326bd | ||
|
|
fe10b131fc | ||
|
|
9b6b7aa998 | ||
|
|
e9d5d8ec11 | ||
|
|
73e39ad453 | ||
|
|
c91d24cd86 | ||
|
|
1fc83aa902 | ||
|
|
5ac013ca40 | ||
|
|
6d17b86d28 | ||
|
|
bdd7044808 | ||
|
|
96bea84a73 | ||
|
|
9aef7df82c | ||
|
|
b0d36529a1 | ||
|
|
2a7099a7a2 | ||
|
|
4c069949f2 | ||
|
|
699c97a9c0 | ||
|
|
3c640061f0 | ||
|
|
bbd74efc2c | ||
|
|
2dfbee090a | ||
|
|
a03b1b7a9a | ||
|
|
594b584500 | ||
|
|
d74d339e4b | ||
|
|
859554ce21 | ||
|
|
703325f223 | ||
|
|
9a416e3e78 | ||
|
|
8821403a9b | ||
|
|
4a841bf563 | ||
|
|
4d6995536a | ||
|
|
6ef4e86029 | ||
|
|
2408c766ce | ||
|
|
882e13bac6 | ||
|
|
e48c2cf75b | ||
|
|
c8f7e60259 | ||
|
|
432a39d313 | ||
|
|
59ed64698d | ||
|
|
6edfe5146c | ||
|
|
44ea0756ea | ||
|
|
02fc4d74db | ||
|
|
0f77b6d86b | ||
|
|
ae2eb14fda | ||
|
|
455c4f5472 | ||
|
|
7335232680 | ||
|
|
2f9372e8e0 | ||
|
|
80db158ee3 | ||
|
|
6c18f4fb2c | ||
|
|
f28444f4c6 | ||
|
|
93ac742a5e | ||
|
|
6dcbc19315 | ||
|
|
65df0fa9cf | ||
|
|
387e88907c | ||
|
|
9253695f8d | ||
|
|
67635b9151 | ||
|
|
e0abfb5cf7 | ||
|
|
f1de6a14da | ||
|
|
38052c1b85 | ||
|
|
5847faf4e2 | ||
|
|
0c1c8178f5 | ||
|
|
86fbd2a4b5 | ||
|
|
8cfb45944c | ||
|
|
0289409c9c | ||
|
|
b93a33eba4 | ||
|
|
fb41a40128 | ||
|
|
e18fda91fe | ||
|
|
b077635427 | ||
|
|
4d4098b7c9 | ||
|
|
9e4838d121 |
78
CONTRIBUTORS
Normal file
78
CONTRIBUTORS
Normal file
@@ -0,0 +1,78 @@
|
||||
Abhinav Adduri
|
||||
Alexander Slovesnik
|
||||
Amin Mahmudian
|
||||
Andreas Pettersson
|
||||
Balázs Meskó
|
||||
Bjørn I
|
||||
Boopesh Mahendran
|
||||
Chuck Harmston
|
||||
Cynthia Pereira
|
||||
Daniel Thorn
|
||||
Daniela Arcese
|
||||
Danny Coates
|
||||
Emin Mastizada
|
||||
Erica
|
||||
Erica Wright
|
||||
Fjoerfoks
|
||||
Francesco Lodolo
|
||||
Francesco Lodolo [:flod]
|
||||
Gautam krishna.R
|
||||
Håvar Henriksen
|
||||
Jim Spentzos
|
||||
Johann-S
|
||||
John Gruen
|
||||
Jordi Serratosa
|
||||
Juraj Cigáň
|
||||
Kohei Yoshino
|
||||
Lan Glad
|
||||
Luna Jernberg
|
||||
Marcelo Poli
|
||||
Marco Aurélio
|
||||
Mark Liang
|
||||
Matjaž Horvat
|
||||
Maykon Chagas
|
||||
Michael Köhler
|
||||
Michael Wolf
|
||||
Michal Stanke
|
||||
Michal Vašíček
|
||||
Moḥend Belqasem
|
||||
Nicholas Skinsacos
|
||||
Peter deHaan
|
||||
Pierre Neter
|
||||
Pin-guang Chen
|
||||
Rhoslyn Prys
|
||||
Rizky Ariestiyansyah
|
||||
Roberto Alvarado
|
||||
Rodrigo
|
||||
Rok Žerdin
|
||||
Sahithi
|
||||
Sairam Raavi
|
||||
Sandro
|
||||
Selim Şumlu
|
||||
Slimane Amiri
|
||||
Théo Chevalier
|
||||
Tomáš Zelina
|
||||
Ton
|
||||
Tymur Faradzhev
|
||||
Victor Bychek
|
||||
Weihang Lo
|
||||
Wil Clouser
|
||||
YFdyh000
|
||||
You-Wen Liang (Mark)
|
||||
alex_mayorga
|
||||
ariestiyansyah
|
||||
avelper
|
||||
dgadelha
|
||||
ehuggett
|
||||
eljuno
|
||||
erdem cobanoglu
|
||||
gautamkrishnar
|
||||
goofy
|
||||
hi
|
||||
kenrick95
|
||||
manxmensch
|
||||
ravmn
|
||||
siparon
|
||||
xcffl
|
||||
Μιχάλης
|
||||
Марко Костић (Marko Kostić)
|
||||
@@ -1,35 +1,22 @@
|
||||
window.Raven = require('raven-js');
|
||||
const Raven = require('raven-js');
|
||||
const { unsupported } = require('./metrics');
|
||||
|
||||
if (navigator.doNotTrack !== '1' && window.RAVEN_CONFIG) {
|
||||
window.Raven.config(window.SENTRY_ID, window.RAVEN_CONFIG).install();
|
||||
Raven.config(window.SENTRY_ID, window.RAVEN_CONFIG).install();
|
||||
}
|
||||
|
||||
const testPilotGA = require('testpilot-ga');
|
||||
const { gcmCompliant, sendEvent } = require('./utils');
|
||||
window.analytics = new testPilotGA({
|
||||
an: 'Firefox Send',
|
||||
ds: 'web',
|
||||
tid: window.GOOGLE_ANALYTICS_ID
|
||||
});
|
||||
|
||||
const isSender = !location.pathname.includes('/download');
|
||||
const ec = isSender ? 'sender' : 'recipient';
|
||||
const ua = navigator.userAgent.toLowerCase();
|
||||
|
||||
gcmCompliant().catch(err => {
|
||||
sendEvent(ec, 'unsupported', {
|
||||
cd6: err
|
||||
}).then(() => {
|
||||
location.replace('/unsupported/gcm');
|
||||
});
|
||||
});
|
||||
|
||||
if (
|
||||
ua.indexOf('firefox') > -1 &&
|
||||
parseInt(ua.match(/firefox\/*([^\n\r]*)\./)[1], 10) <= 49
|
||||
) {
|
||||
sendEvent(ec, 'unsupported', {
|
||||
cd6: new Error('Firefox is outdated.')
|
||||
unsupported({
|
||||
err: new Error('Firefox is outdated.')
|
||||
}).then(() => {
|
||||
location.replace('/unsupported/outdated');
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Raven
|
||||
};
|
||||
|
||||
@@ -1,181 +1,118 @@
|
||||
require('./common');
|
||||
const { Raven } = require('./common');
|
||||
const FileReceiver = require('./fileReceiver');
|
||||
const { notify, findMetric, sendEvent } = require('./utils');
|
||||
const bytes = require('bytes');
|
||||
const { bytes, notify, gcmCompliant } = require('./utils');
|
||||
const Storage = require('./storage');
|
||||
const storage = new Storage(localStorage);
|
||||
|
||||
const links = require('./links');
|
||||
const metrics = require('./metrics');
|
||||
const progress = require('./progress');
|
||||
const $ = require('jquery');
|
||||
require('jquery-circle-progress');
|
||||
|
||||
const Raven = window.Raven;
|
||||
function onUnload(size) {
|
||||
metrics.cancelledDownload({ size });
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
//link back to homepage
|
||||
$('.send-new').attr('href', window.location.origin);
|
||||
function download() {
|
||||
const $downloadBtn = $('#download-btn');
|
||||
const $title = $('.title');
|
||||
const $file = $('#dl-file');
|
||||
const size = Number($file.attr('data-size'));
|
||||
const ttl = Number($file.attr('data-ttl'));
|
||||
const unloadHandler = onUnload.bind(null, size);
|
||||
const startTime = Date.now();
|
||||
const fileReceiver = new FileReceiver();
|
||||
|
||||
$('.send-new').click(function() {
|
||||
sendEvent('recipient', 'restarted', {
|
||||
cd2: 'completed'
|
||||
});
|
||||
$downloadBtn.attr('disabled', 'disabled');
|
||||
$('#download-page-one').attr('hidden', true);
|
||||
$('#download-progress').removeAttr('hidden');
|
||||
metrics.startedDownload({ size, ttl });
|
||||
links.setOpenInNewTab(true);
|
||||
window.addEventListener('unload', unloadHandler);
|
||||
|
||||
fileReceiver.on('progress', data => {
|
||||
progress.setProgress({ complete: data[0], total: data[1] });
|
||||
});
|
||||
|
||||
$('.legal-links a, .social-links a, #dl-firefox').click(function(target) {
|
||||
const metric = findMetric(target.currentTarget.href);
|
||||
// record exited event by recipient
|
||||
sendEvent('recipient', 'exited', {
|
||||
cd3: metric
|
||||
});
|
||||
let downloadEnd;
|
||||
fileReceiver.on('decrypting', () => {
|
||||
downloadEnd = Date.now();
|
||||
window.removeEventListener('unload', unloadHandler);
|
||||
fileReceiver.removeAllListeners('progress');
|
||||
document.l10n.formatValue('decryptingFile').then(progress.setText);
|
||||
});
|
||||
|
||||
const filename = $('#dl-filename').text();
|
||||
const bytelength = Number($('#dl-bytelength').text());
|
||||
const timeToExpiry = Number($('#dl-ttl').text());
|
||||
fileReceiver
|
||||
.download()
|
||||
.catch(err => {
|
||||
metrics.stoppedDownload({ size, err });
|
||||
|
||||
//initiate progress bar
|
||||
$('#dl-progress').circleProgress({
|
||||
value: 0.0,
|
||||
startAngle: -Math.PI / 2,
|
||||
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').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) {
|
||||
fileReceiver.removeAllListeners('progress');
|
||||
window.onunload = null;
|
||||
document.l10n.formatValue('decryptingFile').then(decryptingFile => {
|
||||
$('.progress-text').text(decryptingFile);
|
||||
});
|
||||
if (err.message === 'notfound') {
|
||||
location.reload();
|
||||
} else {
|
||||
downloadEnd = Date.now();
|
||||
document.l10n.formatValue('errorPageHeader').then(translated => {
|
||||
$title.text(translated);
|
||||
});
|
||||
$downloadBtn.attr('hidden', true);
|
||||
$('#expired-img').removeAttr('hidden');
|
||||
}
|
||||
});
|
||||
|
||||
fileReceiver.on('hashing', isStillHashing => {
|
||||
// The file is being hashed to make sure a malicious user hasn't tampered with it
|
||||
if (isStillHashing) {
|
||||
document.l10n.formatValue('verifyingFile').then(verifyingFile => {
|
||||
$('.progress-text').text(verifyingFile);
|
||||
throw err;
|
||||
})
|
||||
.then(([decrypted, fname]) => {
|
||||
const endTime = Date.now();
|
||||
const time = endTime - startTime;
|
||||
const downloadTime = endTime - downloadEnd;
|
||||
const speed = size / (downloadTime / 1000);
|
||||
storage.totalDownloads += 1;
|
||||
metrics.completedDownload({ size, time, speed });
|
||||
progress.setText(' ');
|
||||
document.l10n
|
||||
.formatValues('downloadNotification', 'downloadFinish')
|
||||
.then(translated => {
|
||||
notify(translated[0]);
|
||||
$title.text(translated[1]);
|
||||
});
|
||||
} else {
|
||||
$('.progress-text').text(' ');
|
||||
document.l10n
|
||||
.formatValues('downloadNotification', 'downloadFinish')
|
||||
.then(translated => {
|
||||
notify(translated[0]);
|
||||
$('.title').text(translated[1]);
|
||||
});
|
||||
|
||||
const dataView = new DataView(decrypted);
|
||||
const blob = new Blob([dataView]);
|
||||
const downloadUrl = URL.createObjectURL(blob);
|
||||
|
||||
const a = document.createElement('a');
|
||||
a.href = downloadUrl;
|
||||
if (window.navigator.msSaveBlob) {
|
||||
// if we are in microsoft edge or IE
|
||||
window.navigator.msSaveBlob(blob, fname);
|
||||
return;
|
||||
}
|
||||
});
|
||||
a.download = fname;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
})
|
||||
.catch(err => {
|
||||
Raven.captureException(err);
|
||||
return Promise.reject(err);
|
||||
})
|
||||
.then(() => links.setOpenInNewTab(false));
|
||||
}
|
||||
|
||||
const startTime = Date.now();
|
||||
$(() => {
|
||||
const $file = $('#dl-file');
|
||||
const filename = $file.attr('data-filename');
|
||||
const b = Number($file.attr('data-size'));
|
||||
const size = bytes(b);
|
||||
document.l10n
|
||||
.formatValue('downloadFileSize', { size })
|
||||
.then(str => $('#dl-filesize').text(str));
|
||||
document.l10n
|
||||
.formatValue('downloadingPageProgress', { filename, size })
|
||||
.then(str => $('#dl-title').text(str));
|
||||
|
||||
// record download-started by recipient
|
||||
sendEvent('recipient', 'download-started', {
|
||||
cm1: bytelength,
|
||||
cm4: timeToExpiry,
|
||||
cm5: storage.totalUploads,
|
||||
cm6: unexpiredFiles,
|
||||
cm7: storage.totalDownloads
|
||||
});
|
||||
|
||||
fileReceiver
|
||||
.download()
|
||||
.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
|
||||
});
|
||||
|
||||
if (err.message === 'notfound') {
|
||||
location.reload();
|
||||
} else {
|
||||
document.l10n.formatValue('errorPageHeader').then(translated => {
|
||||
$('.title').text(translated);
|
||||
});
|
||||
$('#download-btn').attr('hidden', true);
|
||||
$('#expired-img').removeAttr('hidden');
|
||||
}
|
||||
throw err;
|
||||
})
|
||||
.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);
|
||||
|
||||
const a = document.createElement('a');
|
||||
a.href = downloadUrl;
|
||||
if (window.navigator.msSaveBlob) {
|
||||
// if we are in microsoft edge or IE
|
||||
window.navigator.msSaveBlob(blob, fname);
|
||||
return;
|
||||
}
|
||||
a.download = fname;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
})
|
||||
.catch(err => {
|
||||
Raven.captureException(err);
|
||||
return Promise.reject(err);
|
||||
gcmCompliant()
|
||||
.then(() => {
|
||||
$('#download-btn').on('click', download);
|
||||
})
|
||||
.catch(err => {
|
||||
metrics.unsupported({ err }).then(() => {
|
||||
location.replace('/unsupported/gcm');
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -45,7 +45,6 @@ class FileReceiver extends EventEmitter {
|
||||
resolve([
|
||||
{
|
||||
data: this.result,
|
||||
aad: meta.aad,
|
||||
filename: meta.filename,
|
||||
iv: meta.id
|
||||
},
|
||||
@@ -62,44 +61,23 @@ class FileReceiver extends EventEmitter {
|
||||
});
|
||||
})
|
||||
.then(([fdata, key]) => {
|
||||
this.emit('decrypting', true);
|
||||
this.emit('decrypting');
|
||||
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)
|
||||
decodeURIComponent(fdata.filename)
|
||||
]);
|
||||
})
|
||||
.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)]);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ class FileSender extends EventEmitter {
|
||||
|
||||
upload() {
|
||||
const self = this;
|
||||
self.emit('loading', true);
|
||||
self.emit('loading');
|
||||
return Promise.all([
|
||||
window.crypto.subtle.generateKey(
|
||||
{
|
||||
@@ -48,46 +48,30 @@ class FileSender extends EventEmitter {
|
||||
const reader = new FileReader();
|
||||
reader.readAsArrayBuffer(this.file);
|
||||
reader.onload = function(event) {
|
||||
self.emit('loading', false);
|
||||
self.emit('hashing', true);
|
||||
const plaintext = new Uint8Array(this.result);
|
||||
window.crypto.subtle.digest('SHA-256', plaintext).then(hash => {
|
||||
self.emit('hashing', false);
|
||||
self.emit('encrypting', true);
|
||||
resolve({ plaintext: plaintext, hash: new Uint8Array(hash) });
|
||||
});
|
||||
resolve(plaintext);
|
||||
};
|
||||
reader.onerror = function(err) {
|
||||
reject(err);
|
||||
};
|
||||
})
|
||||
])
|
||||
.then(([secretKey, file]) => {
|
||||
.then(([secretKey, plaintext]) => {
|
||||
self.emit('encrypting');
|
||||
return Promise.all([
|
||||
window.crypto.subtle
|
||||
.encrypt(
|
||||
{
|
||||
name: 'AES-GCM',
|
||||
iv: this.iv,
|
||||
additionalData: file.hash,
|
||||
tagLength: 128
|
||||
},
|
||||
secretKey,
|
||||
file.plaintext
|
||||
)
|
||||
.then(encrypted => {
|
||||
self.emit('encrypting', false);
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve(encrypted);
|
||||
});
|
||||
}),
|
||||
window.crypto.subtle.exportKey('jwk', secretKey),
|
||||
new Promise((resolve, reject) => {
|
||||
resolve(file.hash);
|
||||
})
|
||||
window.crypto.subtle.encrypt(
|
||||
{
|
||||
name: 'AES-GCM',
|
||||
iv: this.iv,
|
||||
tagLength: 128
|
||||
},
|
||||
secretKey,
|
||||
plaintext
|
||||
),
|
||||
window.crypto.subtle.exportKey('jwk', secretKey)
|
||||
]);
|
||||
})
|
||||
.then(([encrypted, keydata, hash]) => {
|
||||
.then(([encrypted, keydata]) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const file = this.file;
|
||||
const fileId = arrayToHex(this.iv);
|
||||
@@ -123,7 +107,6 @@ class FileSender extends EventEmitter {
|
||||
xhr.setRequestHeader(
|
||||
'X-File-Metadata',
|
||||
JSON.stringify({
|
||||
aad: arrayToHex(hash),
|
||||
id: fileId,
|
||||
filename: encodeURIComponent(file.name)
|
||||
})
|
||||
|
||||
23
frontend/src/links.js
Normal file
23
frontend/src/links.js
Normal file
@@ -0,0 +1,23 @@
|
||||
let links = [];
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
links = Array.from(document.querySelectorAll('a:not([target])'));
|
||||
});
|
||||
|
||||
function setOpenInNewTab(bool) {
|
||||
if (bool === false) {
|
||||
links.forEach(l => {
|
||||
l.removeAttribute('target');
|
||||
l.removeAttribute('rel');
|
||||
});
|
||||
} else {
|
||||
links.forEach(l => {
|
||||
l.setAttribute('target', '_blank');
|
||||
l.setAttribute('rel', 'noopener noreferrer');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
setOpenInNewTab
|
||||
};
|
||||
234
frontend/src/metrics.js
Normal file
234
frontend/src/metrics.js
Normal file
@@ -0,0 +1,234 @@
|
||||
const testPilotGA = require('testpilot-ga/src/TestPilotGA');
|
||||
const Storage = require('./storage');
|
||||
const storage = new Storage(localStorage);
|
||||
|
||||
const analytics = new testPilotGA({
|
||||
an: 'Firefox Send',
|
||||
ds: 'web',
|
||||
tid: window.GOOGLE_ANALYTICS_ID
|
||||
});
|
||||
|
||||
const category = location.pathname.includes('/download')
|
||||
? 'recipient'
|
||||
: 'sender';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
addExitHandlers();
|
||||
addRestartHandlers();
|
||||
});
|
||||
|
||||
function sendEvent() {
|
||||
return analytics.sendEvent.apply(analytics, arguments).catch(() => 0);
|
||||
}
|
||||
|
||||
function urlToMetric(url) {
|
||||
switch (url) {
|
||||
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/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 setReferrer(state) {
|
||||
if (category === 'sender') {
|
||||
if (state) {
|
||||
storage.referrer = `${state}-upload`;
|
||||
}
|
||||
} else if (category === 'recipient') {
|
||||
if (state) {
|
||||
storage.referrer = `${state}-download`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function externalReferrer() {
|
||||
if (/^https:\/\/testpilot\.firefox\.com/.test(document.referrer)) {
|
||||
return 'testpilot';
|
||||
}
|
||||
return 'external';
|
||||
}
|
||||
|
||||
function takeReferrer() {
|
||||
const referrer = storage.referrer || externalReferrer();
|
||||
storage.referrer = null;
|
||||
return referrer;
|
||||
}
|
||||
|
||||
function startedUpload(params) {
|
||||
return sendEvent(category, 'upload-started', {
|
||||
cm1: params.size,
|
||||
cm5: storage.totalUploads,
|
||||
cm6: storage.numFiles + 1,
|
||||
cm7: storage.totalDownloads,
|
||||
cd1: params.type,
|
||||
cd5: takeReferrer()
|
||||
});
|
||||
}
|
||||
|
||||
function cancelledUpload(params) {
|
||||
setReferrer('cancelled');
|
||||
return sendEvent(category, 'upload-stopped', {
|
||||
cm1: params.size,
|
||||
cm5: storage.totalUploads,
|
||||
cm6: storage.numFiles,
|
||||
cm7: storage.totalDownloads,
|
||||
cd1: params.type,
|
||||
cd2: 'cancelled'
|
||||
});
|
||||
}
|
||||
|
||||
function completedUpload(params) {
|
||||
return sendEvent(category, 'upload-stopped', {
|
||||
cm1: params.size,
|
||||
cm2: params.time,
|
||||
cm3: params.speed,
|
||||
cm5: storage.totalUploads,
|
||||
cm6: storage.numFiles,
|
||||
cm7: storage.totalDownloads,
|
||||
cd1: params.type,
|
||||
cd2: 'completed'
|
||||
});
|
||||
}
|
||||
|
||||
function startedDownload(params) {
|
||||
return sendEvent(category, 'download-started', {
|
||||
cm1: params.size,
|
||||
cm4: params.ttl,
|
||||
cm5: storage.totalUploads,
|
||||
cm6: storage.numFiles,
|
||||
cm7: storage.totalDownloads
|
||||
});
|
||||
}
|
||||
|
||||
function stoppedDownload(params) {
|
||||
return sendEvent(category, 'download-stopped', {
|
||||
cm1: params.size,
|
||||
cm5: storage.totalUploads,
|
||||
cm6: storage.numFiles,
|
||||
cm7: storage.totalDownloads,
|
||||
cd2: 'errored',
|
||||
cd6: params.err
|
||||
});
|
||||
}
|
||||
|
||||
function cancelledDownload(params) {
|
||||
setReferrer('cancelled');
|
||||
return sendEvent(category, 'download-stopped', {
|
||||
cm1: params.size,
|
||||
cm5: storage.totalUploads,
|
||||
cm6: storage.numFiles,
|
||||
cm7: storage.totalDownloads,
|
||||
cd2: 'cancelled'
|
||||
});
|
||||
}
|
||||
|
||||
function stoppedUpload(params) {
|
||||
return sendEvent(category, 'upload-stopped', {
|
||||
cm1: params.size,
|
||||
cm5: storage.totalUploads,
|
||||
cm6: storage.numFiles,
|
||||
cm7: storage.totalDownloads,
|
||||
cd1: params.type,
|
||||
cd2: 'errored',
|
||||
cd6: params.err
|
||||
});
|
||||
}
|
||||
|
||||
function completedDownload(params) {
|
||||
return sendEvent(category, 'download-stopped', {
|
||||
cm1: params.size,
|
||||
cm2: params.time,
|
||||
cm3: params.speed,
|
||||
cm5: storage.totalUploads,
|
||||
cm6: storage.numFiles,
|
||||
cm7: storage.totalDownloads,
|
||||
cd2: 'completed'
|
||||
});
|
||||
}
|
||||
|
||||
function deletedUpload(params) {
|
||||
return sendEvent(category, 'upload-deleted', {
|
||||
cm1: params.size,
|
||||
cm2: params.time,
|
||||
cm3: params.speed,
|
||||
cm4: params.ttl,
|
||||
cm5: storage.totalUploads,
|
||||
cm6: storage.numFiles,
|
||||
cm7: storage.totalDownloads,
|
||||
cd1: params.type,
|
||||
cd4: params.location
|
||||
});
|
||||
}
|
||||
|
||||
function unsupported(params) {
|
||||
return sendEvent(category, 'unsupported', {
|
||||
cd6: params.err
|
||||
});
|
||||
}
|
||||
|
||||
function copiedLink(params) {
|
||||
return sendEvent(category, 'copied', {
|
||||
cd4: params.location
|
||||
});
|
||||
}
|
||||
|
||||
function exitEvent(target) {
|
||||
return sendEvent(category, 'exited', {
|
||||
cd3: urlToMetric(target.currentTarget.href)
|
||||
});
|
||||
}
|
||||
|
||||
function addExitHandlers() {
|
||||
const links = Array.from(document.querySelectorAll('a'));
|
||||
links.forEach(l => {
|
||||
if (/^http/.test(l.href)) {
|
||||
l.addEventListener('click', exitEvent);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function restartEvent(state) {
|
||||
setReferrer(state);
|
||||
return sendEvent(category, 'restarted', {
|
||||
cd2: state
|
||||
});
|
||||
}
|
||||
|
||||
function addRestartHandlers() {
|
||||
const elements = Array.from(document.querySelectorAll('.send-new'));
|
||||
elements.forEach(el => {
|
||||
const state = el.getAttribute('data-state');
|
||||
el.addEventListener('click', restartEvent.bind(null, state));
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
copiedLink,
|
||||
startedUpload,
|
||||
cancelledUpload,
|
||||
stoppedUpload,
|
||||
completedUpload,
|
||||
deletedUpload,
|
||||
startedDownload,
|
||||
cancelledDownload,
|
||||
stoppedDownload,
|
||||
completedDownload,
|
||||
unsupported
|
||||
};
|
||||
41
frontend/src/progress.js
Normal file
41
frontend/src/progress.js
Normal file
@@ -0,0 +1,41 @@
|
||||
const { bytes } = require('./utils');
|
||||
const $ = require('jquery');
|
||||
require('jquery-circle-progress');
|
||||
|
||||
let $progress = null;
|
||||
let $percent = null;
|
||||
let $text = null;
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
$percent = $('.percent-number');
|
||||
$text = $('.progress-text');
|
||||
$progress = $('.progress-bar');
|
||||
$progress.circleProgress({
|
||||
value: 0.0,
|
||||
startAngle: -Math.PI / 2,
|
||||
fill: '#3B9DFF',
|
||||
size: 158,
|
||||
animation: { duration: 300 }
|
||||
});
|
||||
});
|
||||
|
||||
function setProgress(params) {
|
||||
const percent = params.complete / params.total;
|
||||
$progress.circleProgress('value', percent);
|
||||
$percent.text(`${Math.floor(percent * 100)}`);
|
||||
document.l10n
|
||||
.formatValue('fileSizeProgress', {
|
||||
partialSize: bytes(params.complete),
|
||||
totalSize: bytes(params.total)
|
||||
})
|
||||
.then(setText);
|
||||
}
|
||||
|
||||
function setText(str) {
|
||||
$text.text(str);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
setProgress,
|
||||
setText
|
||||
};
|
||||
@@ -1,537 +1,476 @@
|
||||
/* global MAXFILESIZE EXPIRE_SECONDS */
|
||||
require('./common');
|
||||
const { Raven } = require('./common');
|
||||
const FileSender = require('./fileSender');
|
||||
const { notify, findMetric, sendEvent, ONE_DAY_IN_MS } = require('./utils');
|
||||
const bytes = require('bytes');
|
||||
const {
|
||||
bytes,
|
||||
copyToClipboard,
|
||||
notify,
|
||||
gcmCompliant,
|
||||
ONE_DAY_IN_MS
|
||||
} = require('./utils');
|
||||
const Storage = require('./storage');
|
||||
const storage = new Storage(localStorage);
|
||||
const metrics = require('./metrics');
|
||||
const progress = require('./progress');
|
||||
|
||||
const $ = require('jquery');
|
||||
require('jquery-circle-progress');
|
||||
|
||||
const Raven = window.Raven;
|
||||
const allowedCopy = () => {
|
||||
const support = !!document.queryCommandSupported;
|
||||
return support ? document.queryCommandSupported('copy') : false;
|
||||
};
|
||||
|
||||
if (storage.has('referrer')) {
|
||||
window.referrer = storage.referrer;
|
||||
storage.remove('referrer');
|
||||
} else {
|
||||
window.referrer = 'external';
|
||||
}
|
||||
$(() => {
|
||||
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 $fileList = $('#file-list');
|
||||
|
||||
$(document).ready(function() {
|
||||
$('#page-one').removeAttr('hidden');
|
||||
$('#file-upload').change(onUpload);
|
||||
$pageOne.removeAttr('hidden');
|
||||
$('#file-upload').on('change', onUpload);
|
||||
|
||||
$('.legal-links a, .social-links a, #dl-firefox').click(function(target) {
|
||||
const metric = findMetric(target.currentTarget.href);
|
||||
// record exited event by recipient
|
||||
sendEvent('sender', 'exited', {
|
||||
cd3: metric
|
||||
});
|
||||
});
|
||||
$(document.body).on('dragover', allowDrop).on('drop', onUpload);
|
||||
|
||||
$('#send-new-completed').click(function() {
|
||||
// record restarted event
|
||||
storage.referrer = 'errored-upload';
|
||||
sendEvent('sender', 'restarted', {
|
||||
cd2: 'completed'
|
||||
});
|
||||
});
|
||||
|
||||
$('#send-new-error').click(function() {
|
||||
// record restarted event
|
||||
storage.referrer = 'errored-upload';
|
||||
sendEvent('sender', 'restarted', {
|
||||
cd2: 'errored'
|
||||
});
|
||||
});
|
||||
|
||||
$('body').on('dragover', allowDrop).on('drop', onUpload);
|
||||
// reset copy button
|
||||
const $copyBtn = $('#copy-btn');
|
||||
$copyBtn.attr('disabled', false);
|
||||
$('#link').attr('disabled', false);
|
||||
$copyBtn.attr('data-l10n-id', 'copyUrlFormButton');
|
||||
|
||||
const files = storage.files;
|
||||
if (files.length === 0) {
|
||||
toggleHeader();
|
||||
} else {
|
||||
// eslint-disable-next-line prefer-const
|
||||
for (let 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);
|
||||
aux.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(aux);
|
||||
//disable button for 3s
|
||||
$copyBtn.attr('disabled', true);
|
||||
$('#link').attr('disabled', true);
|
||||
$copyBtn.html(
|
||||
'<img src="/resources/check-16.svg" class="icon-check"></img>'
|
||||
);
|
||||
window.setTimeout(() => {
|
||||
$copyBtn.attr('disabled', false);
|
||||
$('#link').attr('disabled', false);
|
||||
$copyBtn.attr('data-l10n-id', 'copyUrlFormButton');
|
||||
}, 3000);
|
||||
});
|
||||
|
||||
$('.upload-window').on('dragover', () => {
|
||||
$('.upload-window').addClass('ondrag');
|
||||
});
|
||||
$('.upload-window').on('dragleave', () => {
|
||||
$('.upload-window').removeClass('ondrag');
|
||||
});
|
||||
//initiate progress bar
|
||||
$('#ul-progress').circleProgress({
|
||||
value: 0.0,
|
||||
startAngle: -Math.PI / 2,
|
||||
fill: '#3B9DFF',
|
||||
size: 158,
|
||||
animation: { duration: 300 }
|
||||
});
|
||||
|
||||
//link back to homepage
|
||||
$('.send-new').attr('href', window.location);
|
||||
|
||||
// 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[0]) {
|
||||
$('.upload-window').removeClass('ondrag');
|
||||
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];
|
||||
} else {
|
||||
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 fileSender = new FileSender(file);
|
||||
$('#cancel-upload').click(() => {
|
||||
fileSender.cancel();
|
||||
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'
|
||||
// reset copy button
|
||||
$copyBtn.attr({
|
||||
disabled: !allowedCopy(),
|
||||
'data-l10n-id': 'copyUrlFormButton'
|
||||
});
|
||||
location.reload();
|
||||
});
|
||||
|
||||
fileSender.on('progress', progress => {
|
||||
const percent = progress[0] / progress[1];
|
||||
// update progress bar
|
||||
$('#ul-progress').circleProgress('value', percent);
|
||||
$('#ul-progress').circleProgress().on('circle-animation-end', function() {
|
||||
$('.percent-number').text(`${Math.floor(percent * 100)}`);
|
||||
});
|
||||
$('.progress-text').text(
|
||||
`${file.name} (${bytes(progress[0], {
|
||||
decimalPlaces: 1,
|
||||
fixedDecimals: true
|
||||
})} of ${bytes(progress[1], { decimalPlaces: 1 })})`
|
||||
);
|
||||
});
|
||||
$link.attr('disabled', false);
|
||||
|
||||
fileSender.on('hashing', isStillHashing => {
|
||||
// The file is being hashed
|
||||
if (isStillHashing) {
|
||||
document.l10n.formatValue('verifyingFile').then(verifyingFile => {
|
||||
$('.progress-text').text(verifyingFile);
|
||||
});
|
||||
}
|
||||
});
|
||||
const toggleHeader = () => {
|
||||
//hide table header if empty list
|
||||
if (document.querySelector('tbody').childNodes.length === 1) {
|
||||
$fileList.attr('hidden', true);
|
||||
} else {
|
||||
$fileList.removeAttr('hidden');
|
||||
}
|
||||
};
|
||||
|
||||
let uploadStart;
|
||||
fileSender.on('encrypting', isStillEncrypting => {
|
||||
// The file is being encrypted
|
||||
if (isStillEncrypting) {
|
||||
document.l10n.formatValue('encryptingFile').then(encryptingFile => {
|
||||
$('.progress-text').text(encryptingFile);
|
||||
});
|
||||
const files = storage.files;
|
||||
if (files.length === 0) {
|
||||
toggleHeader();
|
||||
} else {
|
||||
uploadStart = Date.now();
|
||||
}
|
||||
});
|
||||
|
||||
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').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, file, populate) {
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.onreadystatechange = () => {
|
||||
if (xhr.readyState === XMLHttpRequest.DONE) {
|
||||
if (xhr.status === 200) {
|
||||
if (populate) {
|
||||
populateFileList(file);
|
||||
}
|
||||
} else if (xhr.status === 404) {
|
||||
storage.remove(id);
|
||||
if (storage.numFiles === 0) {
|
||||
toggleHeader();
|
||||
}
|
||||
// eslint-disable-next-line prefer-const
|
||||
for (let index in files) {
|
||||
const id = files[index].fileId;
|
||||
//check if file still exists before adding to list
|
||||
checkExistence(id, files[index], true);
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.open('get', '/exists/' + id, true);
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
//update file table with current files in storage
|
||||
function populateFileList(file) {
|
||||
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 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);
|
||||
// copy link to clipboard
|
||||
$copyBtn.on('click', () => {
|
||||
if (allowedCopy() && copyToClipboard($link.attr('value'))) {
|
||||
metrics.copiedLink({ location: 'success-screen' });
|
||||
|
||||
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');
|
||||
$popupText.attr('tabindex', '-1');
|
||||
|
||||
name.appendChild(cellText);
|
||||
|
||||
// create delete button
|
||||
|
||||
const delSpan = document.createElement('span');
|
||||
$(delSpan).addClass('icon-cancel-1');
|
||||
$(delSpan).attr('data-l10n-id', 'deleteButtonHover');
|
||||
del.appendChild(delSpan);
|
||||
|
||||
const linkSpan = document.createElement('span');
|
||||
$(linkSpan).addClass('icon-docs');
|
||||
$(linkSpan).attr('data-l10n-id', 'copyUrlHover');
|
||||
link.appendChild(linkSpan);
|
||||
|
||||
link.style.color = '#0A8DFF';
|
||||
|
||||
//copy link to clipboard when icon clicked
|
||||
$copyIcon.click(function() {
|
||||
// record copied event from upload list
|
||||
sendEvent('sender', 'copied', {
|
||||
cd4: 'upload-list'
|
||||
//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);
|
||||
}
|
||||
});
|
||||
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;
|
||||
});
|
||||
window.setTimeout(() => {
|
||||
const linkImg = document.createElement('img');
|
||||
$(linkImg).addClass('icon-copy');
|
||||
$(linkImg).attr('data-l10n-id', 'copyUrlHover');
|
||||
$(linkImg).attr('src', '/resources/copy-16.svg');
|
||||
$(link).html(linkImg);
|
||||
}, 500);
|
||||
});
|
||||
|
||||
file.creationDate = new Date(file.creationDate);
|
||||
|
||||
const future = new Date();
|
||||
future.setTime(file.creationDate.getTime() + file.expiry);
|
||||
|
||||
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);
|
||||
|
||||
poll();
|
||||
|
||||
function 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 = window.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();
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
const unexpiredFiles = storage.numFiles;
|
||||
|
||||
// delete file
|
||||
$popupText.find('.popup-yes').click(e => {
|
||||
FileSender.delete(file.fileId, file.deleteToken).then(() => {
|
||||
$(e.target).parents('tr').remove();
|
||||
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);
|
||||
$uploadWindow
|
||||
.on('dragover', () => {
|
||||
$uploadWindow.addClass('ondrag');
|
||||
})
|
||||
.on('dragleave', () => {
|
||||
$uploadWindow.removeClass('ondrag');
|
||||
});
|
||||
toggleHeader();
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById('delete-file').onclick = () => {
|
||||
FileSender.delete(file.fileId, file.deleteToken).then(() => {
|
||||
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);
|
||||
// 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();
|
||||
});
|
||||
});
|
||||
};
|
||||
// show popup
|
||||
$delIcon.click(function() {
|
||||
$popupText.addClass('show');
|
||||
$popupText.focus();
|
||||
});
|
||||
// hide popup
|
||||
$popupText.find('.popup-no').click(function(e) {
|
||||
e.stopPropagation();
|
||||
$popupText.removeClass('show');
|
||||
});
|
||||
$popupText.click(function(e) {
|
||||
e.stopPropagation();
|
||||
});
|
||||
//close when popup loses focus
|
||||
$popupText.blur(() => {
|
||||
$popupText.removeClass('show');
|
||||
});
|
||||
|
||||
toggleHeader();
|
||||
}
|
||||
function toggleHeader() {
|
||||
//hide table header if empty list
|
||||
if (document.querySelector('tbody').childNodes.length === 1) {
|
||||
$('#file-list').attr('hidden', true);
|
||||
} else {
|
||||
$('#file-list').removeAttr('hidden');
|
||||
}
|
||||
}
|
||||
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');
|
||||
|
||||
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);
|
||||
$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();
|
||||
}
|
||||
|
||||
function checkExistence(id, file, populate) {
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.onreadystatechange = () => {
|
||||
if (xhr.readyState === XMLHttpRequest.DONE) {
|
||||
if (xhr.status === 200) {
|
||||
if (populate) {
|
||||
populateFileList(file);
|
||||
}
|
||||
} else if (xhr.status === 404) {
|
||||
storage.remove(id);
|
||||
if (storage.numFiles === 0) {
|
||||
toggleHeader();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.open('get', '/exists/' + id, true);
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
//update file table with current files in storage
|
||||
const populateFileList = file => {
|
||||
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', JSON.stringify({ 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 future = new Date();
|
||||
future.setTime(file.creationDate.getTime() + file.expiry);
|
||||
|
||||
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();
|
||||
}
|
||||
};
|
||||
|
||||
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');
|
||||
|
||||
$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);
|
||||
});
|
||||
toggleHeader();
|
||||
});
|
||||
});
|
||||
|
||||
// show popup
|
||||
$delIcon.on('click', () => {
|
||||
$popupText.addClass('show').focus();
|
||||
});
|
||||
|
||||
// hide popup
|
||||
$popupText.find('.popup-no').on('click', e => {
|
||||
e.stopPropagation();
|
||||
$popupText.removeClass('show');
|
||||
});
|
||||
|
||||
$popupText.on('click', e => {
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
//close when popup loses focus
|
||||
$popupText.on('blur', () => {
|
||||
$popupText.removeClass('show');
|
||||
});
|
||||
|
||||
toggleHeader();
|
||||
};
|
||||
})
|
||||
.catch(err => {
|
||||
metrics.unsupported({ err }).then(() => {
|
||||
location.replace('/unsupported/gcm');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -60,41 +60,22 @@ function gcmCompliant() {
|
||||
)
|
||||
.then(() => {
|
||||
return Promise.resolve();
|
||||
})
|
||||
.catch(err => {
|
||||
return Promise.reject();
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
return Promise.reject();
|
||||
return loadShim();
|
||||
});
|
||||
} catch (err) {
|
||||
return Promise.reject();
|
||||
return loadShim();
|
||||
}
|
||||
}
|
||||
|
||||
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/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 loadShim() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const shim = document.createElement('script');
|
||||
shim.src = '/cryptofill.js';
|
||||
shim.addEventListener('load', resolve);
|
||||
shim.addEventListener('error', reject);
|
||||
document.head.appendChild(shim);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,21 +83,55 @@ function isFile(id) {
|
||||
return /^[0-9a-fA-F]{10}$/.test(id);
|
||||
}
|
||||
|
||||
function sendEvent() {
|
||||
return window.analytics.sendEvent
|
||||
.apply(window.analytics, arguments)
|
||||
.catch(() => 0);
|
||||
function copyToClipboard(str) {
|
||||
const aux = document.createElement('input');
|
||||
aux.setAttribute('value', str);
|
||||
aux.contentEditable = true;
|
||||
aux.readOnly = true;
|
||||
document.body.appendChild(aux);
|
||||
if (navigator.userAgent.match(/iphone|ipad|ipod/i)) {
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(aux);
|
||||
const sel = window.getSelection();
|
||||
sel.removeAllRanges();
|
||||
sel.addRange(range);
|
||||
aux.setSelectionRange(0, str.length);
|
||||
} else {
|
||||
aux.select();
|
||||
}
|
||||
const result = document.execCommand('copy');
|
||||
document.body.removeChild(aux);
|
||||
return result;
|
||||
}
|
||||
|
||||
const LOCALIZE_NUMBERS = !!(
|
||||
typeof Intl === 'object' &&
|
||||
Intl &&
|
||||
typeof Intl.NumberFormat === 'function'
|
||||
);
|
||||
|
||||
const UNITS = ['B', 'kB', 'MB', 'GB'];
|
||||
function bytes(num) {
|
||||
const exponent = Math.min(Math.floor(Math.log10(num) / 3), UNITS.length - 1);
|
||||
const n = Number(num / Math.pow(1000, exponent));
|
||||
const nStr = LOCALIZE_NUMBERS
|
||||
? n.toLocaleString(navigator.languages, {
|
||||
minimumFractionDigits: 1,
|
||||
maximumFractionDigits: 1
|
||||
})
|
||||
: n.toFixed(1);
|
||||
return `${nStr}${UNITS[exponent]}`;
|
||||
}
|
||||
|
||||
const ONE_DAY_IN_MS = 86400000;
|
||||
|
||||
module.exports = {
|
||||
bytes,
|
||||
copyToClipboard,
|
||||
arrayToHex,
|
||||
hexToArray,
|
||||
notify,
|
||||
gcmCompliant,
|
||||
findMetric,
|
||||
isFile,
|
||||
sendEvent,
|
||||
ONE_DAY_IN_MS
|
||||
};
|
||||
|
||||
5188
package-lock.json
generated
5188
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
24
package.json
24
package.json
@@ -1,12 +1,11 @@
|
||||
{
|
||||
"name": "firefox-send",
|
||||
"description": "File Sharing Experiment",
|
||||
"version": "1.0.4",
|
||||
"version": "1.1.0",
|
||||
"author": "Mozilla (https://mozilla.org)",
|
||||
"dependencies": {
|
||||
"aws-sdk": "^2.89.0",
|
||||
"body-parser": "^1.17.2",
|
||||
"bytes": "^2.5.0",
|
||||
"connect-busboy": "0.0.2",
|
||||
"convict": "^3.0.0",
|
||||
"express": "^4.15.3",
|
||||
@@ -17,7 +16,13 @@
|
||||
"redis": "^2.7.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"asmcrypto.js": "0.0.11",
|
||||
"babel-core": "^6.25.0",
|
||||
"babel-loader": "^7.1.1",
|
||||
"babel-plugin-add-module-exports": "^0.2.1",
|
||||
"babel-polyfill": "^6.23.0",
|
||||
"babel-preset-es2015": "^6.24.1",
|
||||
"babel-preset-stage-2": "^6.24.1",
|
||||
"browserify": "^14.4.0",
|
||||
"eslint": "^4.3.0",
|
||||
"eslint-plugin-mocha": "^4.11.0",
|
||||
@@ -38,10 +43,11 @@
|
||||
"stylelint-config-standard": "^17.0.0",
|
||||
"supertest": "^3.0.0",
|
||||
"testpilot-ga": "^0.3.0",
|
||||
"uglifyify": "^4.0.3"
|
||||
"webcrypto-liner": "^0.1.25",
|
||||
"webpack": "^3.4.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
"node": ">=8.2.0"
|
||||
},
|
||||
"homepage": "https://github.com/mozilla/send/",
|
||||
"license": "MPL-2.0",
|
||||
@@ -53,7 +59,9 @@
|
||||
"cy",
|
||||
"de",
|
||||
"dsb",
|
||||
"el",
|
||||
"en-US",
|
||||
"es-CL",
|
||||
"es-ES",
|
||||
"es-MX",
|
||||
"fr",
|
||||
@@ -64,6 +72,7 @@
|
||||
"it",
|
||||
"ja",
|
||||
"kab",
|
||||
"ko",
|
||||
"ms",
|
||||
"nb-NO",
|
||||
"nl",
|
||||
@@ -75,15 +84,18 @@
|
||||
"sl",
|
||||
"sr",
|
||||
"sv-SE",
|
||||
"tr",
|
||||
"uk",
|
||||
"vi",
|
||||
"zh-CN",
|
||||
"zh-TW"
|
||||
],
|
||||
"scripts": {
|
||||
"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:js": "webpack -p",
|
||||
"build:version": "node scripts/version",
|
||||
"build:vendor": "cp node_modules/l20n/dist/web/l20n.min.js node_modules/babel-polyfill/dist/polyfill.min.js public",
|
||||
"contributors": "git shortlog -s | awk -F\\t '{print $2}' > CONTRIBUTORS",
|
||||
"dev": "npm run build && npm start",
|
||||
"format": "prettier '{frontend/src/,scripts/,server/,test/**/!(bundle)}*.js' 'public/*.css' --single-quote --write",
|
||||
"get-prod-locales": "node scripts/get-prod-locales",
|
||||
|
||||
22
public/cryptofill.js
Normal file
22
public/cryptofill.js
Normal file
File diff suppressed because one or more lines are too long
@@ -67,6 +67,7 @@ expiredPageHeader = Keçidin vaxtı çıxıb və ya heç vaxt olmayıb!
|
||||
notSupportedHeader = Səyyahınız dəstəklənmir.
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
notSupportedDetail = Heyf ki, bu səyyah Firefox Send-ə güc verən web texnologiyalarını dəstəkləmir. Fərqli bir səyyah yoxlamalısınız. Biz Firefox məsləhət görürük!
|
||||
notSupportedLink = Səyyahım niyə dəstəklənmir?
|
||||
notSupportedOutdatedDetail = Heyf ki, Firefox səyyahının bu versiyası Firefox Send-ə güc verən web texnologiyalarını dəstəkləmir. Səyyahınızı yeniləməlisiniz.
|
||||
updateFirefox = Firefox-u Yenilə
|
||||
downloadFirefoxButtonSub = Pulsuz Endir
|
||||
|
||||
@@ -26,7 +26,7 @@ uploadingFileNotification = Upozornit, až bude nahrávání dokončeno.
|
||||
uploadSuccessConfirmHeader = Připraveno k odeslání
|
||||
uploadSvgAlt
|
||||
.alt = Nahrát
|
||||
uploadSuccessTimingHeader = Platnost odkazu na váš souboru vyprší po jeho prvním stažení, nebo po 24 hodinách.
|
||||
uploadSuccessTimingHeader = Platnost odkazu na váš soubor vyprší po jeho prvním stažení, nebo po 24 hodinách.
|
||||
copyUrlFormLabelWithName = Zkopírujte a sdílejte odkaz na váš soubor: { $filename }
|
||||
// Note: Title text for button should be the same.
|
||||
copyUrlFormButton = Zkopírovat do schránky
|
||||
|
||||
@@ -11,7 +11,7 @@ uploadPageBrowseButton = Wählen Sie eine Datei auf Ihrem Computer aus
|
||||
.title = Wählen Sie eine Datei auf Ihrem Computer aus
|
||||
uploadPageMultipleFilesAlert = Hochladen mehrerer Dateien oder eines Ordners wird derzeit nicht unterstützt.
|
||||
uploadPageBrowseButtonTitle = Datei hochladen
|
||||
uploadingPageHeader = Ihre Datei wird hochgeladen
|
||||
uploadingPageProgress = { $filename } ({ $size }) wird hochgeladen
|
||||
importingFile = Wird importiert…
|
||||
verifyingFile = Wird überprüft…
|
||||
encryptingFile = Wird verschlüsselt…
|
||||
@@ -50,6 +50,8 @@ downloadButtonLabel = Herunterladen
|
||||
.title = Herunterladen
|
||||
downloadNotification = Der Download wurde abgeschlossen.
|
||||
downloadFinish = Download abgeschlossen
|
||||
// This message is displayed when uploading or downloading a file, e.g. "(1,3 MB of 10 MB)".
|
||||
fileSizeProgress = ({ $partialSize } von { $totalSize })
|
||||
// Firefox Send is a brand name and should not be localized. Title text for button should be the same.
|
||||
sendYourFilesLink = Firefox Send ausprobieren
|
||||
.title = Firefox Send ausprobieren
|
||||
|
||||
@@ -11,7 +11,7 @@ uploadPageBrowseButton = Wubjeŕśo dataju na swójom licadle
|
||||
.title = Wubjeŕśo dataju na swójom licadle
|
||||
uploadPageMultipleFilesAlert = Nagrawanje někotarych datajow abo zarědnika se tuchylu njepódpěra.
|
||||
uploadPageBrowseButtonTitle = Dataju nagraś
|
||||
uploadingPageHeader = Waša dataja se nagrawa
|
||||
uploadingPageProgress = { $filename } ({ $size }) se nagrawa
|
||||
importingFile = Importěrujo se...
|
||||
verifyingFile = Pśespytujo se...
|
||||
encryptingFile = Koděrujo se...
|
||||
@@ -50,6 +50,8 @@ downloadButtonLabel = Ześěgnuś
|
||||
.title = Ześěgnuś
|
||||
downloadNotification = Wašo ześěgnjenje jo dokóńcone.
|
||||
downloadFinish = Ześěgnjenje dokóńcone
|
||||
// This message is displayed when uploading or downloading a file, e.g. "(1,3 MB of 10 MB)".
|
||||
fileSizeProgress = ({ $partialSize } z { $totalSize })
|
||||
// Firefox Send is a brand name and should not be localized. Title text for button should be the same.
|
||||
sendYourFilesLink = Firefox Send wopytaś
|
||||
.title = Firefox Send wopytaś
|
||||
@@ -67,6 +69,7 @@ expiredPageHeader = Toś ten wótkaz jo spadnjony abo njejo nigda eksistěrował
|
||||
notSupportedHeader = Waš wobglědowak se njepódpěra.
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
notSupportedDetail = Bóžko toś ten wobglědowak webtechnologiju njepódpěra, na kótarejž Firefox Send bazěrujo. Musyśo drugi wobglědowak wužywaś. My Firefox dopórucujomy!
|
||||
notSupportedLink = Cogodla se mój wobglědowak njepódpěra?
|
||||
notSupportedOutdatedDetail = Bóžko toś ta wersija Firefox webtechnologiju njepódpěra, na kótarejž Firefox Send bazěrujo. Musyśo swój wobglědowak aktualizěrowaś.
|
||||
updateFirefox = Firefox aktualizěrowaś
|
||||
downloadFirefoxButtonSub = Dermotne ześěgnjenje
|
||||
|
||||
@@ -2,8 +2,11 @@
|
||||
title = Firefox Send
|
||||
siteSubtitle = πείραμα διαδικτύου
|
||||
siteFeedback = Σχόλια
|
||||
uploadPageHeader = Ιδιωτική, κρυπτογραφημένη κοινή χρήση αρχείων
|
||||
uploadPageExplainer = Στείλτε αρχεία μέσω ασφαλούς, ιδιωτικού και κρυπτογραφημένου συνδέσμου που λήγει αυτόματα ώστε να διασφαλίσετε ότι τα περιεχόμενά σας δεν θα παραμείνουν στο διαδίκτυο για πάντα.
|
||||
uploadPageLearnMore = Μάθετε περισσότερα
|
||||
uploadPageDropMessage = Εναποθέστε το αρχείο σας εδώ για έναρξη μεταφόρτωσης
|
||||
uploadPageSizeMessage = Για περισσότερο αξιόπιστη λειτουργία, προτείνεται να διατηρήσετε το αρχείο κάτω από 1GB
|
||||
uploadPageBrowseButton = Επιλέξτε αρχείο από τον υπολογιστή σας
|
||||
.title = Επιλέξτε αρχείο από τον υπολογιστή σας
|
||||
uploadPageMultipleFilesAlert = Η μεταφόρτωση πολλαπλών αρχείων ή φακέλου δεν υποστηρίζεται αυτή τη στιγμή.
|
||||
@@ -14,6 +17,7 @@ verifyingFile = Επαλήθευση...
|
||||
encryptingFile = Κρυπτογράφηση…
|
||||
decryptingFile = Αποκρυπτογράφηση…
|
||||
notifyUploadDone = Η μεταφόρτωσή σας ολοκληρώθηκε.
|
||||
uploadingPageMessage = Αφού μεταφορτωθούν τα αρχεία σας, θα μπορείτε να ορίσετε επιλογές λήξης.
|
||||
uploadingPageCancel = Ακύρωση μεταφόρτωσης
|
||||
.title = Ακύρωση μεταφόρτωσης
|
||||
uploadCancelNotification = Η μεταφόρτωσή σας ακυρώθηκε.
|
||||
@@ -22,6 +26,8 @@ uploadingFileNotification = Ειδοποίηση όταν ολοκληρωθεί
|
||||
uploadSuccessConfirmHeader = Έτοιμο για αποστολή
|
||||
uploadSvgAlt
|
||||
.alt = Μεταφόρτωση
|
||||
uploadSuccessTimingHeader = Ο σύνδεσμος του αρχείου σας θα λήξει έπειτα από 1 λήψη ή 24 ώρες.
|
||||
copyUrlFormLabelWithName = Αντιγράψτε και μοιραστείτε τον σύνδεσμο για αποστολή του αρχείου σας : { $filename }
|
||||
// Note: Title text for button should be the same.
|
||||
copyUrlFormButton = Αντιγραφή στο πρόχειρο
|
||||
.title = Αντιγραφή στο πρόχειρο
|
||||
@@ -37,6 +43,8 @@ downloadAltText
|
||||
.alt = Λήψη
|
||||
downloadFileName = Λήψη του { $filename }
|
||||
downloadFileSize = ({ $size })
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
downloadMessage = Ο/Η φίλος/-η σας, σάς στέλνει ένα αρχείο με τη βοήθεια του Firefox Send, μιας υπηρεσίας που επιτρέπει τον διαμοιρασμό αρχείων μέσω ενός ασφαλούς, ιδιωτικού και κρυπτογραφημένου συνδέσμου που λήγει αυτόματα, ώστε να είστε σίγουροι ότι τα αρχεία σας δεν θα παραμείνουν στο διαδίκτυο για πάντα.
|
||||
// Text and title used on the download link/button (indicates an action).
|
||||
downloadButtonLabel = Λήψη
|
||||
.title = Λήψη
|
||||
@@ -46,14 +54,22 @@ downloadFinish = Η λήψη ολοκληρώθηκε
|
||||
sendYourFilesLink = Δοκιμάστε το Firefox Send
|
||||
.title = Δοκιμάστε το Firefox Send
|
||||
downloadingPageProgress = Γίνεται λήψη του { $filename } ({ $size })
|
||||
downloadingPageMessage = Παρακαλώ αφήστε ανοικτή αυτή την καρτέλα όσο λαμβάνουμε και αποκρυπτογραφούμε το αρχείο σας.
|
||||
errorAltText
|
||||
.alt = Σφάλμα μεταφόρτωσης
|
||||
errorPageHeader = Κάτι πήγε στραβά!
|
||||
errorPageMessage = Παρουσιάστηκε σφάλμα κατά τη μεταφόρτωση του αρχείου.
|
||||
errorPageLink = Αποστολή άλλου αρχείου
|
||||
fileTooBig = Αυτό το αρχείο είναι πολύ μεγάλο για μεταφόρτωση. Πρέπει να είναι μικρότερο από { $size }.
|
||||
linkExpiredAlt
|
||||
.alt = Ο σύνδεσμος έληξε
|
||||
expiredPageHeader = Αυτός ο σύνδεσμος έχει λήξει ή δεν υπήρξε ποτέ!
|
||||
notSupportedHeader = Το πρόγραμμα περιήγησής σας δεν υποστηρίζεται.
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
notSupportedDetail = Δυστυχώς, αυτό το πρόγραμμα περιήγησης δεν υποστηρίζει την τεχνολογία ιστού στην οποία βασίζεται το Firefox Send. Θα πρέπει να δοκιμάσετε ένα άλλο πρόγραμμα περιήγησης. Προτείνουμε το Firefox!
|
||||
notSupportedLink = Γιατί δεν υποστηρίζεται το πρόγραμμα περιήγησής μου;
|
||||
notSupportedOutdatedDetail = Δυστυχώς, αυτή η έκδοση του Firefox δεν υποστηρίζει την τεχνολογία ιστού στην οποία βασίζεται το Firefox Send. Πρέπει να ενημερώσετε το πρόγραμμα περιήγησής σας.
|
||||
updateFirefox = Ενημέρωση Firefox
|
||||
downloadFirefoxButtonSub = Δωρεάν λήψη
|
||||
uploadedFile = Αρχείο
|
||||
copyFileList = Αντιγραφή URL
|
||||
@@ -62,6 +78,8 @@ expiryFileList = Λήγει σε
|
||||
deleteFileList = Διαγραφή
|
||||
nevermindButton = Μην ανησυχείτε
|
||||
legalHeader = Όροι & απόρρητο
|
||||
legalNoticeTestPilot = Το Firefox Send αποτελεί προς το παρόν ένα πείραμα Test Pilot και υπόκειται στους <a>όρους υπηρεσίας</a> και την <a>πολιτική απορρήτου</a> του Test Pilot. Μπορείτε να μάθετε περισσότερα γι' αυτό το πείραμα και τη συλλογή δεδομένων <a>εδώ</a>.
|
||||
legalNoticeMozilla = Η χρήση της ιστοσελίδας Firefox Send υπόκειται επίσης στην <a>πολιτική απορρήτου ιστοσελίδων</a> και τους <a>όρους χρήσης ιστοσελίδων</a> της Mozilla.
|
||||
deletePopupText = Διαγραφή αρχείου;
|
||||
deletePopupYes = Ναι
|
||||
deletePopupCancel = Ακύρωση
|
||||
|
||||
@@ -11,7 +11,7 @@ uploadPageBrowseButton = Select a file on your computer
|
||||
.title = Select a file on your computer
|
||||
uploadPageMultipleFilesAlert = Uploading multiple files or a folder is currently not supported.
|
||||
uploadPageBrowseButtonTitle = Upload file
|
||||
uploadingPageHeader = Uploading Your File
|
||||
uploadingPageProgress = Uploading { $filename } ({ $size })
|
||||
importingFile = Importing…
|
||||
verifyingFile = Verifying…
|
||||
encryptingFile = Encrypting…
|
||||
@@ -50,6 +50,8 @@ downloadButtonLabel = Download
|
||||
.title = Download
|
||||
downloadNotification = Your download has completed.
|
||||
downloadFinish = Download Complete
|
||||
// This message is displayed when uploading or downloading a file, e.g. "(1,3 MB of 10 MB)".
|
||||
fileSizeProgress = ({ $partialSize } of { $totalSize })
|
||||
// Firefox Send is a brand name and should not be localized. Title text for button should be the same.
|
||||
sendYourFilesLink = Try Firefox Send
|
||||
.title = Try Firefox Send
|
||||
|
||||
@@ -11,7 +11,7 @@ uploadPageBrowseButton = Seleccioná un archivo en tu computadora
|
||||
.title = Seleccioná un archivo en tu computadora
|
||||
uploadPageMultipleFilesAlert = Cargar múltiples archivos o una carpeta todavía no está soportado.
|
||||
uploadPageBrowseButtonTitle = Subir archivo
|
||||
uploadingPageHeader = Subiendo el archivo
|
||||
uploadingPageProgress = Subiendo { $filename } ({ $size })
|
||||
importingFile = Importando…
|
||||
verifyingFile = Verificando…
|
||||
encryptingFile = Cifrando…
|
||||
@@ -50,6 +50,8 @@ downloadButtonLabel = Descargar
|
||||
.title = Descargar
|
||||
downloadNotification = La descarga se completó.
|
||||
downloadFinish = Descarga completa
|
||||
// This message is displayed when uploading or downloading a file, e.g. "(1,3 MB of 10 MB)".
|
||||
fileSizeProgress = ({ $partialSize } de { $totalSize })
|
||||
// Firefox Send is a brand name and should not be localized. Title text for button should be the same.
|
||||
sendYourFilesLink = Probá Firefox Send
|
||||
.title = Probá Firefox Send
|
||||
@@ -67,6 +69,7 @@ expiredPageHeader = ¡Este enlace ha expirado o nunca existió en primer lugar!
|
||||
notSupportedHeader = El navegador no está soportado.
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
notSupportedDetail = Desafortunadamente este navegador no soporta la tecnología web que necesita Firefox Send. Deberías probar otro navegador. ¡Te recomendamos Firefox!
|
||||
notSupportedLink = ¿Por qué mi navegador no está soportado?
|
||||
notSupportedOutdatedDetail = Desafortunadamente esta versión de Firefox no soporta la tecnología web que necesita Firefox Send. Necesitás actualizar el navegador.
|
||||
updateFirefox = Actualizar Firefox
|
||||
downloadFirefoxButtonSub = Descarga gratuita
|
||||
|
||||
@@ -8,6 +8,7 @@ uploadPageLearnMore = Aprender más
|
||||
uploadPageDropMessage = Suelta tu archivo aquí para empezar a subirlo
|
||||
uploadPageSizeMessage = Para una operación más confiable, es mejor mantener el tamaño del archivo bajo 1 GB
|
||||
uploadPageBrowseButton = Selecciona un archivo en tu computador
|
||||
.title = Selecciona un archivo en tu computador
|
||||
uploadPageMultipleFilesAlert = Subir múltiples archivos o una carpeta actualmente no es posible.
|
||||
uploadPageBrowseButtonTitle = Subir archivo
|
||||
uploadingPageHeader = Subiendo tu archivo
|
||||
@@ -18,6 +19,7 @@ decryptingFile = Descifrando…
|
||||
notifyUploadDone = Tu subida ha terminado.
|
||||
uploadingPageMessage = Una vez que tu archivo sea subido podrás ajustar las opciones de expiración.
|
||||
uploadingPageCancel = Cancelar subida
|
||||
.title = Cancelar subida
|
||||
uploadCancelNotification = Tu subida fue cancelada.
|
||||
uploadingPageLargeFileMessage = Este archivo es grande y puede tardar un rato en subir. ¡Aprovecha de hacer algo mientras!
|
||||
uploadingFileNotification = Notificarme cuando la subida sea completada.
|
||||
@@ -28,11 +30,14 @@ uploadSuccessTimingHeader = El enlace a tu archivo expirará tras 1 descarga o e
|
||||
copyUrlFormLabelWithName = Copia y comparte el enlace para enviar tu archivo: { $filename }
|
||||
// Note: Title text for button should be the same.
|
||||
copyUrlFormButton = Copiar al portapapeles
|
||||
.title = Copiar al portapapeles
|
||||
copiedUrl = ¡Copiado!
|
||||
// Note: Title text for button should be the same.
|
||||
deleteFileButton = Eliminar archivo
|
||||
.title = Eliminar archivo
|
||||
// Note: Title text for button should be the same.
|
||||
sendAnotherFileLink = Enviar otro archivo
|
||||
.title = Enviar otro archivo
|
||||
// Alternative text used on the download link/button (indicates an action).
|
||||
downloadAltText
|
||||
.alt = Descargar
|
||||
@@ -42,13 +47,16 @@ downloadFileSize = ({ $size })
|
||||
downloadMessage = Tu amigo te está enviando un archivo con Firefox Send, un servicio que te permite compartir archivos con un enlace seguro, privado y cifrado que expira automáticamente para asegurar que tus cosas no queden en línea de por vida.
|
||||
// Text and title used on the download link/button (indicates an action).
|
||||
downloadButtonLabel = Descargar
|
||||
.title = Descargar
|
||||
downloadNotification = Tu descarga se completó.
|
||||
downloadFinish = Descarga completa
|
||||
// Firefox Send is a brand name and should not be localized. Title text for button should be the same.
|
||||
sendYourFilesLink = Probar Firefox Send
|
||||
.title = Probar Firefox Send
|
||||
downloadingPageProgress = Descargando { $filename } ({ $size })
|
||||
downloadingPageMessage = Por favor, deja esta pestaña abierta mientras recibimos tu archivo y lo desciframos.
|
||||
errorAltText = Error de subida
|
||||
errorAltText
|
||||
.alt = Error de subida
|
||||
errorPageHeader = ¡Algo se fue a las pailas!
|
||||
errorPageMessage = Hubo un error al subir el archivo.
|
||||
errorPageLink = Enviar otro archivo
|
||||
@@ -59,6 +67,7 @@ expiredPageHeader = ¡Este enlace ha expirado o quizá jamás existió!
|
||||
notSupportedHeader = Tu navegador no está soportado.
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
notSupportedDetail = Lamentablemente este navegador no soporta la tecnología web que potencia a Firefox Send. Deberás probar en otro navegador. ¡Recomendamos Firefox!
|
||||
notSupportedLink = ¿Por qué mi navegador no es soportado?
|
||||
notSupportedOutdatedDetail = Lamentablemente esta versión de Firefox no soporta la tecnología web que potencia a Firefox Send. Deberás actualizar tu navegador.
|
||||
updateFirefox = Actualizar Firefox
|
||||
downloadFirefoxButtonSub = Descarga gratuita
|
||||
@@ -69,10 +78,7 @@ expiryFileList = Expira en
|
||||
deleteFileList = Eliminar
|
||||
nevermindButton = Da lo mismo
|
||||
legalHeader = Términos y privacidad
|
||||
legalNoticeTestPilot =
|
||||
Firefox Send es actualmente un experimento de Test Pilot, y está sujeto a los <a>Términos del servicio</a> y la <a>Política de privacidad</a> de Test Pilot. Puedes aprender más sobre este experimento y su recolección de datos <a>aquí</a>.
|
||||
|
||||
|
||||
legalNoticeTestPilot = Firefox Send es actualmente un experimento de Test Pilot, y está sujeto a los <a>Términos del servicio</a> y la <a>Política de privacidad</a> de Test Pilot. Puedes aprender más sobre este experimento y su recolección de datos <a>aquí</a>.
|
||||
legalNoticeMozilla = El uso del sitio web de Firefox Send también está sujeto a la <a>Política de privacidad de sitios web</a> y los <a>Términos de uso de sitios web</a> de Mozilla.
|
||||
deletePopupText = ¿Eliminar este archivo?
|
||||
deletePopupYes = Sí
|
||||
|
||||
@@ -67,6 +67,7 @@ expiredPageHeader = ¡El enlace ha caducado o nunca existió!
|
||||
notSupportedHeader = Tu navegador no está admitido.
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
notSupportedDetail = Lamentablemente, este navegador no admite la tecnología web que necesita Firefox Send. Tendrás que probar otro navegador. ¡Te recomendamos Firefox!
|
||||
notSupportedLink = ¿Por qué no se admite mi navegador?
|
||||
notSupportedOutdatedDetail = Lamentablemente, esta versión de Firefox no admite la tecnología web que impulsa Firefox Send. Tendrás que actualizar tu navegador.
|
||||
updateFirefox = Actualizar Firefox
|
||||
downloadFirefoxButtonSub = Descarga gratuita
|
||||
|
||||
@@ -67,6 +67,7 @@ expiredPageHeader = ¡Este enlace ha caducado o nunca existió en primer lugar!
|
||||
notSupportedHeader = Tu navegador no está soportado.
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
notSupportedDetail = Lamentablemente, este navegador no admite la tecnología web que necesita Firefox Send. Tendrás que probar otro navegador. ¡Te recomendamos Firefox!
|
||||
notSupportedLink = ¿Por qué mi navegador no tiene soporte?
|
||||
notSupportedOutdatedDetail = Lamentablemente esta versión de Firefox no soporta la tecnología web que potencia a Firefox Send. Deberás actualizar tu navegador.
|
||||
updateFirefox = Actualizar Firefox
|
||||
downloadFirefoxButtonSub = Descarga gratuita
|
||||
|
||||
25
public/locales/fa/send.ftl
Normal file
25
public/locales/fa/send.ftl
Normal file
@@ -0,0 +1,25 @@
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
title = Firefox Send
|
||||
siteSubtitle = آزمایش وب
|
||||
siteFeedback = بازخورد
|
||||
downloadFirefoxButtonSub = دریافت رایگان
|
||||
uploadedFile = پرونده
|
||||
copyFileList = رونوشت از نشانی
|
||||
// expiryFileList is used as a column header
|
||||
expiryFileList = زمان انقضا
|
||||
deleteFileList = حذف
|
||||
nevermindButton = بیخیال
|
||||
legalHeader = شرایط و حریمخصوصی
|
||||
deletePopupText = حذف این پرونده؟
|
||||
deletePopupYes = بله
|
||||
deletePopupCancel = انصراف
|
||||
deleteButtonHover
|
||||
.title = حذف
|
||||
copyUrlHover
|
||||
.title = رونوشت از نشانی
|
||||
footerLinkLegal = ملاحظات حقوقی
|
||||
// Test Pilot is a proper name and should not be localized.
|
||||
footerLinkAbout = درباره Test Pilot
|
||||
footerLinkPrivacy = حریمخصوصی
|
||||
footerLinkTerms = شرایط
|
||||
footerLinkCookies = کوکیها
|
||||
@@ -11,7 +11,7 @@ uploadPageBrowseButton = Sélectionner un fichier sur l’ordinateur
|
||||
.title = Sélectionner un fichier sur l’ordinateur
|
||||
uploadPageMultipleFilesAlert = L’envoi de plusieurs fichiers ou de dossiers n’est pas pris en charge pour le moment.
|
||||
uploadPageBrowseButtonTitle = Envoyer le fichier
|
||||
uploadingPageHeader = Envoi du fichier en cours
|
||||
uploadingPageProgress = Envoi en cours de { $filename } ({ $size })
|
||||
importingFile = Importation…
|
||||
verifyingFile = Vérification…
|
||||
encryptingFile = Chiffrement…
|
||||
@@ -50,6 +50,8 @@ downloadButtonLabel = Télécharger
|
||||
.title = Télécharger
|
||||
downloadNotification = Le téléchargement est terminé.
|
||||
downloadFinish = Téléchargement terminé
|
||||
// This message is displayed when uploading or downloading a file, e.g. "(1,3 MB of 10 MB)".
|
||||
fileSizeProgress = ({ $partialSize } sur { $totalSize })
|
||||
// Firefox Send is a brand name and should not be localized. Title text for button should be the same.
|
||||
sendYourFilesLink = Essayer Firefox Send
|
||||
.title = Essayer Firefox Send
|
||||
|
||||
@@ -67,6 +67,7 @@ expiredPageHeader = Dizze keppeling is ferrûn of hat nea bestien!
|
||||
notSupportedHeader = Jo browser wurdt net stipe.
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
notSupportedDetail = Spitigernôch stipet dizze browser de webtechnology dy't Firefox Send mooglik makket net. Jo moatte in oare browser probearje. Wy rekommandearje Firefox!
|
||||
notSupportedLink = Wêrom wurdt myn browser net stipe?
|
||||
notSupportedOutdatedDetail = Spitigernôch stipet dizze ferzje fan Firefox de webtechnology dy't Firefox Send mooflik makket net. Jo moatte jo browser fernije.
|
||||
updateFirefox = Firefox fernije
|
||||
downloadFirefoxButtonSub = Fergese download
|
||||
|
||||
@@ -11,7 +11,7 @@ uploadPageBrowseButton = Wubjerće dataju na swojim ličaku
|
||||
.title = Wubjerće dataju na swojim ličaku
|
||||
uploadPageMultipleFilesAlert = Nahrawanje wjacorych datajow abo rjadowaka so tuchwilu njepodpěruje.
|
||||
uploadPageBrowseButtonTitle = Dataju nahrać
|
||||
uploadingPageHeader = Waša dataja so nahrawa
|
||||
uploadingPageProgress = { $filename } ({ $size }) so nahrawa
|
||||
importingFile = Importuje so...
|
||||
verifyingFile = Přepruwuje so...
|
||||
encryptingFile = Zaklučuje so...
|
||||
@@ -50,6 +50,8 @@ downloadButtonLabel = Sćahnyć
|
||||
.title = Sćahnyć
|
||||
downloadNotification = Waše sćehnjenje je dokónčene.
|
||||
downloadFinish = Sćehnjenje dokónčene
|
||||
// This message is displayed when uploading or downloading a file, e.g. "(1,3 MB of 10 MB)".
|
||||
fileSizeProgress = ({ $partialSize } z { $totalSize })
|
||||
// Firefox Send is a brand name and should not be localized. Title text for button should be the same.
|
||||
sendYourFilesLink = Firefox Send wupruwować
|
||||
.title = Firefox Send wupruwować
|
||||
@@ -67,6 +69,7 @@ expiredPageHeader = Tutón wotkaz je spadnjeny abo njeje ženje eksistował!
|
||||
notSupportedHeader = Waš wobhladowak so njepodpěruje.
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
notSupportedDetail = Bohužel tutón wobhladowak webtechnologiju njepodpěruje, na kotrejž Firefox Send bazuje. Dyrbiće druhi wobhladowak wužiwać. My Firefox doporučemy!
|
||||
notSupportedLink = Čehodla so mój wobhladowak njepodpěruje?
|
||||
notSupportedOutdatedDetail = Bohužel tuta wersija Firefox webtechnologiju njepodpěruje, na kotrejž Firefox Send bazuje. Dyrbiće swój wobhladowak aktualizować.
|
||||
updateFirefox = Firefox aktualizować
|
||||
downloadFirefoxButtonSub = Darmotne sćehnjenje
|
||||
|
||||
@@ -11,7 +11,7 @@ uploadPageBrowseButton = Válasszon egy fájlt a számítógépén
|
||||
.title = Válasszon egy fájlt a számítógépén
|
||||
uploadPageMultipleFilesAlert = Több fájl vagy mappa feltöltése pillanatnyilag nem támogatott.
|
||||
uploadPageBrowseButtonTitle = Fájl feltöltése
|
||||
uploadingPageHeader = A fájlja feltöltése
|
||||
uploadingPageProgress = { $filename } ({ $size }) feltöltése
|
||||
importingFile = Importálás…
|
||||
verifyingFile = Ellenőrzés…
|
||||
encryptingFile = Titkosítás…
|
||||
@@ -50,6 +50,8 @@ downloadButtonLabel = Letöltés
|
||||
.title = Letöltés
|
||||
downloadNotification = A letöltés befejeződött.
|
||||
downloadFinish = A letöltés befejeződött
|
||||
// This message is displayed when uploading or downloading a file, e.g. "(1,3 MB of 10 MB)".
|
||||
fileSizeProgress = ({ $partialSize } / { $totalSize })
|
||||
// Firefox Send is a brand name and should not be localized. Title text for button should be the same.
|
||||
sendYourFilesLink = Próbálja ki a Firefox Sendet
|
||||
.title = Próbálja ki a Firefox Sendet
|
||||
@@ -67,6 +69,7 @@ expiredPageHeader = Ez a hivatkozás lejárt, vagy sosem létezett!
|
||||
notSupportedHeader = A böngésző nem támogatott.
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
notSupportedDetail = Sajnos ez a böngésző nem támogatja a Firefox Send alapját képező webes technológiát. Egy másik böngészőben kell megpróbálnia. Mi a Firefoxot javasoljuk!
|
||||
notSupportedLink = Miért nem támogatott a böngészőm?
|
||||
notSupportedOutdatedDetail = Sajnos a Firefox ezen verziója nem támogatja a Firefox Send alapját képező technológiát. Frissítenie kell a böngészőjét.
|
||||
updateFirefox = Firefox frissítése
|
||||
downloadFirefoxButtonSub = Ingyenes letöltés
|
||||
|
||||
@@ -45,6 +45,9 @@ downloadFileName = Unduh { $filename }
|
||||
downloadFileSize = ({ $size })
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
downloadMessage = Teman Anda mengirimkan berkas dengan Firefox Send, layanan yang memungkinkan Anda berbagi berkas dengan tautan yang aman, pribadi, dan terenkripsi yang secara otomatis berakhir untuk memastikan berkas Anda tidak daring selamanya.
|
||||
// Text and title used on the download link/button (indicates an action).
|
||||
downloadButtonLabel = Unduh
|
||||
.title = Unduh
|
||||
downloadNotification = Unduhan Anda telah selesai.
|
||||
downloadFinish = Unduhan Selesai
|
||||
// Firefox Send is a brand name and should not be localized. Title text for button should be the same.
|
||||
@@ -64,6 +67,7 @@ expiredPageHeader = Tautan ini telah kedaluwarsa atau tidak pernah ada!
|
||||
notSupportedHeader = Peramban Anda tidak mendukung.
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
notSupportedDetail = Sayangnya peramban ini tidak mendukung teknologi web yang menggerakkan Firefox Send. Anda perlu mencoba peramban lain. Kami merekomendasikan Firefox!
|
||||
notSupportedLink = Mengapa peramban saya tidak didukung?
|
||||
notSupportedOutdatedDetail = Sayangnya Firefox versi ini tidak mendukung teknologi web yang menggerakkan Firefox Send. Anda perlu memperbarui peramban Anda.
|
||||
updateFirefox = Perbarui Firefox
|
||||
downloadFirefoxButtonSub = Unduh Gratis
|
||||
|
||||
@@ -11,7 +11,7 @@ uploadPageBrowseButton = Seleziona un file sul computer
|
||||
.title = Seleziona un file sul computer
|
||||
uploadPageMultipleFilesAlert = Il caricamento di più file o cartelle non è attualmente supportato.
|
||||
uploadPageBrowseButtonTitle = Carica file
|
||||
uploadingPageHeader = Caricamento file
|
||||
uploadingPageProgress = Caricamento { $filename } ({ $size })
|
||||
importingFile = Importazione in corso…
|
||||
verifyingFile = Verifica in corso…
|
||||
encryptingFile = Crittazione in corso…
|
||||
@@ -50,6 +50,8 @@ downloadButtonLabel = Scarica
|
||||
.title = Scarica
|
||||
downloadNotification = Download completato.
|
||||
downloadFinish = Download completato
|
||||
// This message is displayed when uploading or downloading a file, e.g. "(1,3 MB of 10 MB)".
|
||||
fileSizeProgress = ({ $partialSize } di { $totalSize })
|
||||
// Firefox Send is a brand name and should not be localized. Title text for button should be the same.
|
||||
sendYourFilesLink = Prova Firefox Send
|
||||
.title = Prova Firefox Send
|
||||
@@ -67,6 +69,7 @@ expiredPageHeader = Questo link è scaduto oppure non è mai esistito.
|
||||
notSupportedHeader = Il browser in uso non è supportato.
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
notSupportedDetail = Purtroppo questo browser non supporta le tecnologie web alla base di Firefox Send. Devi utilizzare un altro browser. Ti consigliamo Firefox!
|
||||
notSupportedLink = Perché questo browser non risulta supportato?
|
||||
notSupportedOutdatedDetail = Purtroppo questa versione di Firefox non supporta le tecnologie web alla base di Firefox Send. È necessario aggiornare il browser.
|
||||
updateFirefox = Aggiorna Firefox
|
||||
downloadFirefoxButtonSub = Download gratuito
|
||||
|
||||
@@ -11,7 +11,7 @@ uploadPageBrowseButton = コンピューター上のファイルを選択
|
||||
.title = コンピューター上のファイルを選択
|
||||
uploadPageMultipleFilesAlert = 今のところ複数ファイルやフォルダーのアップロードには対応していません。
|
||||
uploadPageBrowseButtonTitle = ファイルをアップロード
|
||||
uploadingPageHeader = ファイルをアップロードしています
|
||||
uploadingPageProgress = { $filename } ({ $size }) をアップロード中
|
||||
importingFile = インポート中...
|
||||
verifyingFile = 検証中...
|
||||
encryptingFile = 暗号化中...
|
||||
@@ -50,11 +50,13 @@ downloadButtonLabel = ダウンロード
|
||||
.title = ダウンロード
|
||||
downloadNotification = ダウンロードが完了しました。
|
||||
downloadFinish = ダウンロード完了
|
||||
// This message is displayed when uploading or downloading a file, e.g. "(1,3 MB of 10 MB)".
|
||||
fileSizeProgress = ({ $partialSize } / { $totalSize })
|
||||
// Firefox Send is a brand name and should not be localized. Title text for button should be the same.
|
||||
sendYourFilesLink = Firefox Send を試す
|
||||
.title = Firefox Send を試す
|
||||
downloadingPageProgress = { $filename } ({ $size }) をダウンロードしています
|
||||
downloadingPageMessage = ファイルの取得と暗号化が完了するまでこのタブを開いたままにしておいてください。
|
||||
downloadingPageMessage = ファイルの取得と復号化が完了するまでこのタブを開いたままにしておいてください。
|
||||
errorAltText
|
||||
.alt = アップロードエラー
|
||||
errorPageHeader = 何か問題が発生しました。
|
||||
@@ -67,6 +69,7 @@ expiredPageHeader = このリンクは期限切れとなったか元々存在し
|
||||
notSupportedHeader = お使いのブラウザーには対応していません。
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
notSupportedDetail = 残念ながらお使いのブラウザーは Firefox Send が活用しているウェブ技術に対応していません。他のブラウザーで試してください。私たちは Firefox をお勧めします!
|
||||
notSupportedLink = なぜ私のブラウザには対応していないのでしょうか?
|
||||
notSupportedOutdatedDetail = 残念ながらお使いのバージョンの Firefox は Firefox Send が活用しているウェブ技術に対応していません。ブラウザーを更新する必要があります。
|
||||
updateFirefox = Firefox を更新
|
||||
downloadFirefoxButtonSub = 無料ダウンロード
|
||||
|
||||
@@ -67,6 +67,7 @@ expiredPageHeader = Aseɣwen-agi yemmut neɣ wurǧin yella seg tazwara!
|
||||
notSupportedHeader = Iminig-ik ur ittusefrak ara
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
notSupportedDetail = Ad nesḥissef imi iminig-ik ur isefrak ara tatiknulujit web iseqdac Firefox Send. Yessefk ad tesqedceḍ iminig-nniḍen. Seqdec Firefox!
|
||||
notSupportedLink = Ayγer iminig inu ur yettwasefrek ara?
|
||||
notSupportedOutdatedDetail = Ad nesḥissef imilqem-agi n Firefox Firefox ur isefrak ara titiknulujiyin web yettwaseqdacen di Firefox Send. Yessefk ad tleqmeḍ iminig-ik.
|
||||
updateFirefox = Leqqem Firefox
|
||||
downloadFirefoxButtonSub = Asider ilelli
|
||||
|
||||
97
public/locales/ko/send.ftl
Normal file
97
public/locales/ko/send.ftl
Normal file
@@ -0,0 +1,97 @@
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
title = Firefox Send
|
||||
siteSubtitle = 웹 실험
|
||||
siteFeedback = 사용자 의견
|
||||
uploadPageHeader = 개인적이고, 암호화된 파일 공유
|
||||
uploadPageExplainer = 안전하고, 개인적이며, 암호화된 링크를 통해 파일을 공유하세요. 사용자의 파일이 더 이상 온라인 상에 남지 않도록 링크는 자동적으로 만료됩니다.
|
||||
uploadPageLearnMore = 더 알아보기
|
||||
uploadPageDropMessage = 파일을 끌어 놓아 업로드 시작
|
||||
uploadPageSizeMessage = 확실한 작동을 위해서, 파일의 크기가 1GB보다 작은 것이 좋음
|
||||
uploadPageBrowseButton = 컴퓨터의 파일을 선택
|
||||
.title = 컴퓨터의 파일을 선택
|
||||
uploadPageMultipleFilesAlert = 여러 개의 파일 또는 폴더를 업로드하는 것은 현재로선 지원되지 않습니다.
|
||||
uploadPageBrowseButtonTitle = 파일 업로드
|
||||
uploadingPageProgress = { $filename } ({ $size }) 업로드 중
|
||||
importingFile = 가져오는 중…
|
||||
verifyingFile = 확인하는 중…
|
||||
encryptingFile = 암호화 중…
|
||||
decryptingFile = 복호화 중…
|
||||
notifyUploadDone = 업로드가 완료되었습니다.
|
||||
uploadingPageMessage = 파일이 업로드 되고나서 만료 옵션을 설정할 수 있습니다.
|
||||
uploadingPageCancel = 업로드 취소
|
||||
.title = 업로드 취소
|
||||
uploadCancelNotification = 업로드가 취소되었습니다.
|
||||
uploadingPageLargeFileMessage = 이 파일은 크기가 커서 시간이 다소 걸릴 수 있습니다. 잠시만 기다려주세요!
|
||||
uploadingFileNotification = 업로드가 완료되면 알림을 표시해 주세요.
|
||||
uploadSuccessConfirmHeader = 보낼 준비 완료
|
||||
uploadSvgAlt
|
||||
.alt = 업로드
|
||||
uploadSuccessTimingHeader = 이 파일의 링크는 한 번의 다운로드 후 또는 24시간이 지난 뒤에 만료됩니다.
|
||||
copyUrlFormLabelWithName = 파일을 보내기 위해 이 링크를 복사하고 공유하세요: { $filename }
|
||||
// Note: Title text for button should be the same.
|
||||
copyUrlFormButton = 클립보드에 복사
|
||||
.title = 클립보드에 복사
|
||||
copiedUrl = 복사 완료!
|
||||
// Note: Title text for button should be the same.
|
||||
deleteFileButton = 파일 삭제
|
||||
.title = 파일 삭제
|
||||
// Note: Title text for button should be the same.
|
||||
sendAnotherFileLink = 다른 파일 보내기
|
||||
.title = 다른 파일 보내기
|
||||
// Alternative text used on the download link/button (indicates an action).
|
||||
downloadAltText
|
||||
.alt = 다운로드
|
||||
downloadFileName = { $filename } 다운로드
|
||||
downloadFileSize = ({ $size })
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
downloadMessage = 당신의 친구가 Firefox Send를 통해 파일을 보내고 있습니다. 이 서비스는 안전하고, 개인적이며, 암호화된 링크를 통해 파일을 공유하는 서비스입니다. 사용자의 파일이 더 이상 온라인 상에 남지 않도록 링크는 자동적으로 만료됩니다.
|
||||
// Text and title used on the download link/button (indicates an action).
|
||||
downloadButtonLabel = 다운로드
|
||||
.title = 다운로드
|
||||
downloadNotification = 다운로드가 완료되었습니다.
|
||||
downloadFinish = 다운로드 완료
|
||||
// This message is displayed when uploading or downloading a file, e.g. "(1,3 MB of 10 MB)".
|
||||
fileSizeProgress = ({ $partialSize } / { $totalSize })
|
||||
// Firefox Send is a brand name and should not be localized. Title text for button should be the same.
|
||||
sendYourFilesLink = Firefox Send 써보기
|
||||
.title = Firefox Send 써보기
|
||||
downloadingPageProgress = { $filename } ({ $size }) 다운로드 중
|
||||
downloadingPageMessage = 파일을 가져오고 복호화하는 동안 탭을 닫지 말아주세요.
|
||||
errorAltText
|
||||
.alt = 업로드 오류
|
||||
errorPageHeader = 오류가 발생했습니다!
|
||||
errorPageMessage = 파일을 업로드하는 도중 오류가 발생했습니다.
|
||||
errorPageLink = 다른 파일 보내기
|
||||
fileTooBig = 파일의 크기가 너무 큽니다. { $size } 보다 작아야 합니다.
|
||||
linkExpiredAlt
|
||||
.alt = 링크가 만료됨
|
||||
expiredPageHeader = 이 링크는 만료되었거나 애초부터 존재하지 않았습니다!
|
||||
notSupportedHeader = 이 브라우저는 지원되지 않습니다.
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
notSupportedDetail = 안타깝게도 이 브라우저는 Firefox Send에 사용되는 웹 기술을 지원하지 않습니다. 다른 브라우저로 다시 시도해주세요. Firefox를 추천합니다!
|
||||
notSupportedLink = 왜 이 브라우저는 지원이 되지 않나요?
|
||||
notSupportedOutdatedDetail = 안타깝게도 현재 브라우저 버전에서는 Firefox Send에 사용되는 웹 기술을 지원하지 않습니다. 브라우저 업데이트가 필요합니다.
|
||||
updateFirefox = Firefox 업데이트
|
||||
downloadFirefoxButtonSub = 무료 다운로드
|
||||
uploadedFile = 파일
|
||||
copyFileList = URL 복사
|
||||
// expiryFileList is used as a column header
|
||||
expiryFileList = 만료기한
|
||||
deleteFileList = 삭제
|
||||
nevermindButton = 괜찮습니다
|
||||
legalHeader = 이용약관 & 개인정보 보호
|
||||
legalNoticeTestPilot = Firefox Send는 현재 Test Pilot 실험 중이고, Test Pilot <a>이용 약관</a>과 <a>개인정보 보호공지</a>가 적용됩니다. 이 실험과 데이터 수집에 관해서는 <a>여기</a>에서 더 알아볼 수 있습니다.
|
||||
legalNoticeMozilla = 또한, Firefox Send 웹사이트 사용에는 <a>웹사이트 개인정보 공지</a>와 <a>웹 사이트 이용약관</a>이 적용됩니다.
|
||||
deletePopupText = 이 파일을 지우시겠습니까?
|
||||
deletePopupYes = 예
|
||||
deletePopupCancel = 아니오
|
||||
deleteButtonHover
|
||||
.title = 삭제
|
||||
copyUrlHover
|
||||
.title = URL 복사
|
||||
footerLinkLegal = 법적 정보
|
||||
// Test Pilot is a proper name and should not be localized.
|
||||
footerLinkAbout = Test Pilot 정보
|
||||
footerLinkPrivacy = 개인정보 보호
|
||||
footerLinkTerms = 이용 약관
|
||||
footerLinkCookies = 쿠키
|
||||
@@ -11,7 +11,7 @@ uploadPageBrowseButton = Pilih fail dalam komputer anda
|
||||
.title = Pilih fail dalam komputer anda
|
||||
uploadPageMultipleFilesAlert = Memuat naik pelbagai fail atau satu folder masih belum disokong.
|
||||
uploadPageBrowseButtonTitle = Muat naik fail
|
||||
uploadingPageHeader = Memuat naik Fail Anda
|
||||
uploadingPageProgress = Memuat naik { $filename } ({ $size })
|
||||
importingFile = Mengimport…
|
||||
verifyingFile = Mengesahkan...
|
||||
encryptingFile = Mengenkripsi...
|
||||
@@ -50,6 +50,8 @@ downloadButtonLabel = Muat turun
|
||||
.title = Muat turun
|
||||
downloadNotification = Muat turun anda sudah siap.
|
||||
downloadFinish = Muat turun Selesai
|
||||
// This message is displayed when uploading or downloading a file, e.g. "(1,3 MB of 10 MB)".
|
||||
fileSizeProgress = ({ $partialSize } daripada { $totalSize })
|
||||
// Firefox Send is a brand name and should not be localized. Title text for button should be the same.
|
||||
sendYourFilesLink = Cuba Firefox Send
|
||||
.title = Cuba Firefox Send
|
||||
|
||||
@@ -67,6 +67,7 @@ expiredPageHeader = Denne lenken er utløpt eller har aldri eksistert i utgangsp
|
||||
notSupportedHeader = Din nettleser er ikke støttet.
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
notSupportedDetail = Dessverre støtter denne nettleseren ikke webteknologien som driver Firefox Send. Du må prøve en annen nettleser. Vi anbefaler Firefox!
|
||||
notSupportedLink = Hvorfor er ikke nettleseren min støttet?
|
||||
notSupportedOutdatedDetail = Dessverre støtter ikke denne versjonen av Firefox netteknologien som driver Firefox Send. Du trenger å oppdatere nettleseren din.
|
||||
updateFirefox = Oppdater Firefox
|
||||
downloadFirefoxButtonSub = Gratis nedlasting
|
||||
|
||||
@@ -11,7 +11,7 @@ uploadPageBrowseButton = Selecteer een bestand op uw computer
|
||||
.title = Selecteer een bestand op uw computer
|
||||
uploadPageMultipleFilesAlert = Het uploaden van meerdere bestanden of een map wordt momenteel niet ondersteund.
|
||||
uploadPageBrowseButtonTitle = bestand uploaden
|
||||
uploadingPageHeader = Uw bestand wordt geüpload
|
||||
uploadingPageProgress = { $filename } ({ $size }) wordt geüpload
|
||||
importingFile = Importeren…
|
||||
verifyingFile = Verifiëren…
|
||||
encryptingFile = Versleutelen…
|
||||
@@ -50,6 +50,8 @@ downloadButtonLabel = Downloaden
|
||||
.title = Downloaden
|
||||
downloadNotification = Uw download is voltooid.
|
||||
downloadFinish = Downloaden voltooid
|
||||
// This message is displayed when uploading or downloading a file, e.g. "(1,3 MB of 10 MB)".
|
||||
fileSizeProgress = ({ $partialSize } van { $totalSize })
|
||||
// Firefox Send is a brand name and should not be localized. Title text for button should be the same.
|
||||
sendYourFilesLink = Firefox Send proberen
|
||||
.title = Firefox Send proberen
|
||||
@@ -67,6 +69,7 @@ expiredPageHeader = Deze koppeling is verlopen of heeft überhaupt nooit bestaan
|
||||
notSupportedHeader = Uw browser wordt niet ondersteund.
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
notSupportedDetail = Helaas ondersteunt deze browser de webtechnologie die Firefox Send gebruikt niet. U dient een andere browser te proberen. Firefox wordt aanbevolen!
|
||||
notSupportedLink = Waarom wordt mijn browser niet ondersteund?
|
||||
notSupportedOutdatedDetail = Helaas ondersteunt deze versie van Firefox de webtechnologie die Firefox Send gebruikt niet. U dient uw browser bij te werken.
|
||||
updateFirefox = Firefox bijwerken
|
||||
downloadFirefoxButtonSub = Gratis download
|
||||
|
||||
@@ -11,7 +11,7 @@ uploadPageBrowseButton = Vel ei fil på datamaskina di
|
||||
.title = Vel ei fil på datamaskina di
|
||||
uploadPageMultipleFilesAlert = Opplasting av fleire filer eller ei mappe er for tida ikkje støtta.
|
||||
uploadPageBrowseButtonTitle = Last opp fil
|
||||
uploadingPageHeader = Lastar opp fila di
|
||||
uploadingPageProgress = Lastar opp { $filename } ({ $size })
|
||||
importingFile = Importerer…
|
||||
verifyingFile = Stadfestar…
|
||||
encryptingFile = Krypterer…
|
||||
@@ -50,6 +50,8 @@ downloadButtonLabel = Last ned
|
||||
.title = Last ned
|
||||
downloadNotification = Nedlastinga er fullført.
|
||||
downloadFinish = Nedlastinga er fullført.
|
||||
// This message is displayed when uploading or downloading a file, e.g. "(1,3 MB of 10 MB)".
|
||||
fileSizeProgress = ({ $partialSize } av { $totalSize })
|
||||
// Firefox Send is a brand name and should not be localized. Title text for button should be the same.
|
||||
sendYourFilesLink = Prøv Firefox Send
|
||||
.title = Prøv Firefox Send
|
||||
|
||||
@@ -11,7 +11,7 @@ uploadPageBrowseButton = Selecione um arquivo em seu computador
|
||||
.title = Selecione um arquivo em seu computador
|
||||
uploadPageMultipleFilesAlert = Enviar múltiplos arquivos ou uma pasta ainda não é suportado.
|
||||
uploadPageBrowseButtonTitle = Enviar arquivo
|
||||
uploadingPageHeader = Enviando seu arquivo
|
||||
uploadingPageProgress = Enviando { $filename } ({ $size })
|
||||
importingFile = Importando…
|
||||
verifyingFile = Verificando…
|
||||
encryptingFile = Criptografando…
|
||||
@@ -40,21 +40,23 @@ sendAnotherFileLink = Enviar outro arquivo
|
||||
.title = Enviar outro arquivo
|
||||
// Alternative text used on the download link/button (indicates an action).
|
||||
downloadAltText
|
||||
.alt = Download
|
||||
downloadFileName = Download { $filename }
|
||||
.alt = Baixar
|
||||
downloadFileName = Baixar { $filename }
|
||||
downloadFileSize = ({ $size })
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
downloadMessage = Seu amigo está te enviando um arquivo através do Firefox Send, um serviço que permite compartilhar arquivos com segurança, privacidade e um link encriptado que automaticamente expira para garantir que suas coisas não permaneçam on-line eternamente.
|
||||
downloadMessage = Seu amigo está te enviando um arquivo através do Firefox Send, um serviço que permite compartilhar arquivos com um link seguro, privado e criptografado que automaticamente expira para garantir que suas coisas não permaneçam on-line eternamente.
|
||||
// Text and title used on the download link/button (indicates an action).
|
||||
downloadButtonLabel = Download
|
||||
.title = Download
|
||||
downloadButtonLabel = Baixar
|
||||
.title = Baixar
|
||||
downloadNotification = Seu download terminou.
|
||||
downloadFinish = Download completo
|
||||
// This message is displayed when uploading or downloading a file, e.g. "(1,3 MB of 10 MB)".
|
||||
fileSizeProgress = ({ $partialSize } de { $totalSize })
|
||||
// Firefox Send is a brand name and should not be localized. Title text for button should be the same.
|
||||
sendYourFilesLink = Experimente o Firefox Send
|
||||
.title = Experimente o Firefox Send
|
||||
downloadingPageProgress = Baixando { $filename } ({ $size })
|
||||
downloadingPageMessage = Por favor deixe essa aba aberta enquanto buscamos seu arquivo e o decriptamos.
|
||||
downloadingPageMessage = Por favor, deixe essa aba aberta enquanto buscamos seu arquivo e o descriptografamos.
|
||||
errorAltText
|
||||
.alt = Erro no envio
|
||||
errorPageHeader = Oops, ocorreu um erro!
|
||||
@@ -68,7 +70,7 @@ notSupportedHeader = Seu navegador não tem suporte.
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
notSupportedDetail = Infelizmente esse navegador não suporta a tecnologia utilizada pelo Firefox Send. Tente com outro navegador. Nós recomendamos o Firefox! ;-)
|
||||
notSupportedLink = Por que meu navegador não é suportado?
|
||||
notSupportedOutdatedDetail = Infelizmente esta versão do Firefox não suporta a tecnologia web que faz o Firefox Send funcionar. Você precisa atualizar o seu navegador.
|
||||
notSupportedOutdatedDetail = Infelizmente essa versão do Firefox não suporta a tecnologia web que faz o Firefox Send funcionar. Você precisa atualizar o seu navegador.
|
||||
updateFirefox = Atualizar o Firefox
|
||||
downloadFirefoxButtonSub = Download gratuito
|
||||
uploadedFile = Arquivo
|
||||
@@ -79,15 +81,15 @@ deleteFileList = Excluir
|
||||
nevermindButton = Esqueça
|
||||
legalHeader = Termos e privacidade
|
||||
legalNoticeTestPilot = Firefox Send é um experimento do Test Pilot, e sujeito aos <a>Termos de Serviço</a> e <a>Políticas de Privacidade</a> do Test Pilot. Você pode aprender mais sobre esse experimento e a coleta de dados <a>aqui</a>.
|
||||
legalNoticeMozilla = O uso do site Firefox Send também está sujeito a <a>Política de Privacidade</a> e <a>Websites Terms of Use</a> da Mozilla.
|
||||
deletePopupText = Excluir este arquivo
|
||||
legalNoticeMozilla = O uso do site Firefox Send também está sujeito a <a>Política de Privacidade</a> e ao <a>Termos de Uso de Sites</a> da Mozilla.
|
||||
deletePopupText = Excluir este arquivo?
|
||||
deletePopupYes = Sim
|
||||
deletePopupCancel = Cancelar
|
||||
deleteButtonHover
|
||||
.title = Excluir
|
||||
copyUrlHover
|
||||
.title = Copiar URL
|
||||
footerLinkLegal = Legal
|
||||
footerLinkLegal = Jurídico
|
||||
// Test Pilot is a proper name and should not be localized.
|
||||
footerLinkAbout = Sobre o Test Pilot
|
||||
footerLinkPrivacy = Privacidade
|
||||
|
||||
@@ -11,7 +11,7 @@ uploadPageBrowseButton = Selecionar um ficheiro no seu computador
|
||||
.title = Selecionar um ficheiro no seu computador
|
||||
uploadPageMultipleFilesAlert = Carregar múltiplos ficheiros ou uma pasta não é atualmente suportado.
|
||||
uploadPageBrowseButtonTitle = Carregar ficheiro
|
||||
uploadingPageHeader = A carregar o seu ficheiro
|
||||
uploadingPageProgress = A carregar { $filename } ({ $size })
|
||||
importingFile = A importar...
|
||||
verifyingFile = A verificar...
|
||||
encryptingFile = A encriptar...
|
||||
@@ -50,6 +50,8 @@ downloadButtonLabel = Descarregar
|
||||
.title = Descarregar
|
||||
downloadNotification = A sua descarga foi completada.
|
||||
downloadFinish = Descarga completada
|
||||
// This message is displayed when uploading or downloading a file, e.g. "(1,3 MB of 10 MB)".
|
||||
fileSizeProgress = ({ $partialSize } de { $totalSize })
|
||||
// Firefox Send is a brand name and should not be localized. Title text for button should be the same.
|
||||
sendYourFilesLink = Tentar o Firefox Send
|
||||
.title = Tentar o Firefox Send
|
||||
|
||||
@@ -67,6 +67,7 @@ expiredPageHeader = Это ссылка просрочена или никогд
|
||||
notSupportedHeader = Ваш браузер не поддерживается.
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
notSupportedDetail = К сожалению, этот браузер не поддерживает веб-технологию, благодаря которой работает Firefox Send. Ваш нужно попробовать использовать другой браузер. Мы рекомендуем Firefox!
|
||||
notSupportedLink = Почему мой браузер не поддерживается?
|
||||
notSupportedOutdatedDetail = К сожалению, эта версия Firefox не поддерживает веб-технологию, благодаря которой работает Firefox Send. Ваш нужно обновить свой браузер.
|
||||
updateFirefox = Обновить Firefox
|
||||
downloadFirefoxButtonSub = Бесплатная загрузка
|
||||
|
||||
@@ -11,7 +11,7 @@ uploadPageBrowseButton = Vyberte súbor vo vašom počítači
|
||||
.title = Vyberte súbor vo vašom počítači
|
||||
uploadPageMultipleFilesAlert = Nahrávanie viacerých súborov alebo priečinkov momentálne nie je podporované.
|
||||
uploadPageBrowseButtonTitle = Nahrať súbor
|
||||
uploadingPageHeader = Nahrávanie vášho súboru
|
||||
uploadingPageProgress = Nahrávanie súboru { $filename } ({ $size })
|
||||
importingFile = Importuje sa…
|
||||
verifyingFile = Overuje sa…
|
||||
encryptingFile = Šifruje sa…
|
||||
@@ -50,6 +50,8 @@ downloadButtonLabel = Prevziať
|
||||
.title = Prevziať
|
||||
downloadNotification = Vaše preberanie bolo dokončené.
|
||||
downloadFinish = Preberanie bolo dokončené
|
||||
// This message is displayed when uploading or downloading a file, e.g. "(1,3 MB of 10 MB)".
|
||||
fileSizeProgress = ({ $partialSize } z { $totalSize })
|
||||
// Firefox Send is a brand name and should not be localized. Title text for button should be the same.
|
||||
sendYourFilesLink = Vyskúšajte Firefox Send
|
||||
.title = Vyskúšajte Firefox Send
|
||||
@@ -67,6 +69,7 @@ expiredPageHeader = Platnosť tohto odkazu vypršala alebo daný odkaz nikdy nee
|
||||
notSupportedHeader = Váš prehliadač nie je podporovaný.
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
notSupportedDetail = Žiaľ, tento prehliadač nepodporuje webovú technológiu, ktorá poháňa službu Firefox Send. Budete musieť vyskúšať iný prehliadač. My vám odporúčame Firefox!
|
||||
notSupportedLink = Prečo nie je môj prehliadač podporovaný?
|
||||
notSupportedOutdatedDetail = Žiaľ, táto verzia Firefoxu nepodporuje webovú technológiu, ktorá poháňa Firefox Send. Budete musieť aktualizovať svoj prehliadač.
|
||||
updateFirefox = Aktualizovať Firefox
|
||||
downloadFirefoxButtonSub = Prevziať zadarmo
|
||||
|
||||
@@ -11,7 +11,7 @@ uploadPageBrowseButton = Izberite datoteko na računalniku
|
||||
.title = Izberite datoteko na računalniku
|
||||
uploadPageMultipleFilesAlert = Nalaganje več datotek ali map trenutno ni podprto.
|
||||
uploadPageBrowseButtonTitle = Naloži datoteko
|
||||
uploadingPageHeader = Nalaganje datoteke
|
||||
uploadingPageProgress = Nalaganje { $filename } ({ $size })
|
||||
importingFile = Uvažanje …
|
||||
verifyingFile = Preverjanje …
|
||||
encryptingFile = Šifriranje ...
|
||||
@@ -50,6 +50,8 @@ downloadButtonLabel = Prenesi
|
||||
.title = Prenesi
|
||||
downloadNotification = Vaš prenos je končan.
|
||||
downloadFinish = Prenos končan
|
||||
// This message is displayed when uploading or downloading a file, e.g. "(1,3 MB of 10 MB)".
|
||||
fileSizeProgress = ({ $partialSize } od { $totalSize })
|
||||
// Firefox Send is a brand name and should not be localized. Title text for button should be the same.
|
||||
sendYourFilesLink = Preskusite Firefox Send
|
||||
.title = Preskusite Firefox Send
|
||||
|
||||
@@ -11,7 +11,7 @@ uploadPageBrowseButton = Изаберите датотеку на рачунар
|
||||
.title = Изаберите датотеку на рачунару
|
||||
uploadPageMultipleFilesAlert = Отпремање фасцикли или више датотека тренутно није подржано.
|
||||
uploadPageBrowseButtonTitle = Отпреми датотеку
|
||||
uploadingPageHeader = Ваша датотека се отпрема
|
||||
uploadingPageProgress = Отпремам { $filename } ({ $size })
|
||||
importingFile = Увозим…
|
||||
verifyingFile = Потврђујем…
|
||||
encryptingFile = Шифрујем…
|
||||
@@ -50,6 +50,8 @@ downloadButtonLabel = Преузми
|
||||
.title = Преузми
|
||||
downloadNotification = Ваше преузимање је завршено.
|
||||
downloadFinish = Преузимање је завршено.
|
||||
// This message is displayed when uploading or downloading a file, e.g. "(1,3 MB of 10 MB)".
|
||||
fileSizeProgress = ({ $partialSize } од { $totalSize })
|
||||
// Firefox Send is a brand name and should not be localized. Title text for button should be the same.
|
||||
sendYourFilesLink = Испробајте Firefox Send
|
||||
.title = Испробајте Firefox Send
|
||||
|
||||
@@ -67,9 +67,8 @@ expiredPageHeader = Den här länken har upphört eller har aldrig existerat i f
|
||||
notSupportedHeader = Din webbläsare stöds inte.
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
notSupportedDetail = Tyvärr stöder inte webbläsaren den webbteknologi som används av Firefox Send. Du måste försöka med en annan webbläsare. Vi rekommenderar Firefox!
|
||||
notSupportedOutdatedDetail =
|
||||
Tyvärr stödjer den här versionen av Firefox inte webbtekniken som driver Firefox Send. Du måste uppdatera din webbläsare.
|
||||
|
||||
notSupportedLink = Varför stödjs inte min webbläsare?
|
||||
notSupportedOutdatedDetail = Tyvärr stödjer den här versionen av Firefox inte webbtekniken som driver Firefox Send. Du måste uppdatera din webbläsare.
|
||||
updateFirefox = Uppdatera Firefox
|
||||
downloadFirefoxButtonSub = Gratis nedladdning
|
||||
uploadedFile = Fil
|
||||
|
||||
83
public/locales/te/send.ftl
Normal file
83
public/locales/te/send.ftl
Normal file
@@ -0,0 +1,83 @@
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
title = Firefox Send
|
||||
siteSubtitle = జాల ప్రయోగం
|
||||
siteFeedback = అభిప్రాయం
|
||||
uploadPageLearnMore = ఇంకా తెలుసుకోండి
|
||||
uploadPageDropMessage = ఎగుమతిని ప్రారంభించడానికి మీ ఫైలును ఇక్కడ విడిచిపెట్టండి
|
||||
uploadPageSizeMessage = అత్యంత నమ్మకమైన కార్యం కోసం, మీ ఫైలును 1GB కంటే తక్కువగా ఉంచడం ఉత్తమం
|
||||
uploadPageBrowseButton = మీ కంప్యూటర్లో ఒక ఫైలును ఎంచుకోండి
|
||||
.title = మీ కంప్యూటర్లో ఒక ఫైలును ఎంచుకోండి
|
||||
uploadPageBrowseButtonTitle = ఫైలును ఎగుమతి చేయండి
|
||||
uploadingPageHeader = మీ ఫైలు ఎగుమతి అవుతుంది
|
||||
importingFile = దిగుమతవుతోంది...
|
||||
verifyingFile = పరిశీలిస్తున్నది…
|
||||
encryptingFile = గుప్తీకరిస్తోంది...
|
||||
decryptingFile = వ్యక్తపరుస్తోంది...
|
||||
notifyUploadDone = మీ ఎగుమతి పూర్తయింది.
|
||||
uploadingPageMessage = మీ ఫైలును మీరు ఎగుమతి చేసిన తర్వాత గడువు ఎంపికలను సరిగా ఏర్పాటు చేయగలరు.
|
||||
uploadingPageCancel = ఎగుమతి రద్దు చేయండి
|
||||
.title = ఎగుమతి రద్దు చేయండి
|
||||
uploadCancelNotification = మీ ఎగుమతి రద్దు చేయబడింది.
|
||||
uploadingPageLargeFileMessage = ఈ ఫైలు పెద్దగా ఉంది అందువలన ఎగుమతి చేయడానికి కొంత సమయం పట్టవచ్చు. వేచి ఉండండి!
|
||||
uploadingFileNotification = ఎగుమతి పూర్తయినప్పుడు నాకు తెలియచేయండి.
|
||||
uploadSuccessConfirmHeader = పంపించడానికి సిద్ధంగా ఉంది
|
||||
uploadSvgAlt
|
||||
.alt = ఎగుమతి చేయండి
|
||||
uploadSuccessTimingHeader = మీ ఫైలు లంకె గడువు 1 దిగుమతి తరువాత లేదా 24 గంటల తరువాత ముగుస్తుంది.
|
||||
copyUrlFormLabelWithName = మీ ఫైల్ను పంపడానికి లంకెను నకలు చేయండి మరియు పంచండి: { $filename }
|
||||
// Note: Title text for button should be the same.
|
||||
copyUrlFormButton = క్లిప్బోర్డ్కు నకలు చేయండి
|
||||
.title = క్లిప్బోర్డ్కు నకలు చేయండి
|
||||
copiedUrl = నకలు చేయబడింది!
|
||||
// Note: Title text for button should be the same.
|
||||
deleteFileButton = ఫైలును తొలగించండి
|
||||
.title = ఫైలును తొలగించండి
|
||||
// Note: Title text for button should be the same.
|
||||
sendAnotherFileLink = మరో ఫైలును పంపండి
|
||||
.title = మరో ఫైలును పంపండి
|
||||
// Alternative text used on the download link/button (indicates an action).
|
||||
downloadAltText
|
||||
.alt = దిగుమతి
|
||||
downloadFileName = దిగుమతి { $filename }
|
||||
downloadFileSize = ({ $size })
|
||||
// Text and title used on the download link/button (indicates an action).
|
||||
downloadButtonLabel = దిగుమతి
|
||||
.title = దిగుమతి
|
||||
downloadNotification = మీ దిగుమతి పూర్తయ్యింది.
|
||||
downloadFinish = దిగుమతి పూర్తయింది
|
||||
// Firefox Send is a brand name and should not be localized. Title text for button should be the same.
|
||||
sendYourFilesLink = Firefox sendను ప్రయత్నించండి
|
||||
.title = Firefox sendను ప్రయత్నించండి
|
||||
downloadingPageProgress = దిగుమతిచేస్తున్నది { $filename } ({ $size })
|
||||
errorAltText
|
||||
.alt = ఎగుమతిలో లోపం
|
||||
errorPageHeader = ఏదో తప్పిదం జరిగింది!
|
||||
errorPageMessage = ఫైల్ను ఎగుమతి చేయడంలో లోపం ఉంది.
|
||||
errorPageLink = మరో ఫైలును పంపండి
|
||||
linkExpiredAlt
|
||||
.alt = లంకె గడువు ముగిసింది
|
||||
expiredPageHeader = ఈ లంకె గడువు ముగిసింది లేదా ముందు ఎప్పుడూ ఉనికిలో లేదు!
|
||||
notSupportedHeader = మీ విహారిణికి మద్దతు లేదు.
|
||||
notSupportedLink = నా విహారిణికి ఎందుకు మద్దతు లేదు?
|
||||
updateFirefox = Firefoxను నవీకరించు
|
||||
downloadFirefoxButtonSub = ఉచిత దిగుమతులు
|
||||
uploadedFile = దస్త్రం
|
||||
copyFileList = URL నకలుతీయి
|
||||
// expiryFileList is used as a column header
|
||||
expiryFileList = ఇంతలో గడువుతీరును
|
||||
deleteFileList = తొలగించు
|
||||
nevermindButton = పర్వాలేదు
|
||||
legalHeader = నిబంధనలు మరియు గోప్యత
|
||||
deletePopupText = ఈ ఫైలును తొలగించాలా?
|
||||
deletePopupYes = అవును
|
||||
deletePopupCancel = రద్దుచేయి
|
||||
deleteButtonHover
|
||||
.title = తొలగించు
|
||||
copyUrlHover
|
||||
.title = URLను నకలు చేయండి
|
||||
footerLinkLegal = చట్టపరమైన
|
||||
// Test Pilot is a proper name and should not be localized.
|
||||
footerLinkAbout = టెస్ట్ పైలట్ గురించి
|
||||
footerLinkPrivacy = గోప్యత
|
||||
footerLinkTerms = నియమాలు
|
||||
footerLinkCookies = కుకీలు
|
||||
@@ -30,9 +30,11 @@ uploadSuccessTimingHeader = Dosyanız 1 kez indirildikten veya 24 saat geçtikte
|
||||
copyUrlFormLabelWithName = { $filename } dosyanızı başkasına göndermek için aşağıdaki linki kopyalayın.
|
||||
// Note: Title text for button should be the same.
|
||||
copyUrlFormButton = Panoya kopyala
|
||||
.title = Panoya kopyala
|
||||
copiedUrl = Kopyalandı!
|
||||
// Note: Title text for button should be the same.
|
||||
deleteFileButton = Dosyayı sil
|
||||
.title = Dosyayı sil
|
||||
// Note: Title text for button should be the same.
|
||||
sendAnotherFileLink = Başka bir dosya daha gönder
|
||||
.title = Başka bir dosya daha gönder
|
||||
@@ -50,6 +52,7 @@ downloadNotification = İndirme tamamlandı.
|
||||
downloadFinish = İndirme tamamlandı
|
||||
// Firefox Send is a brand name and should not be localized. Title text for button should be the same.
|
||||
sendYourFilesLink = Firefox Send’i deneyin
|
||||
.title = Firefox Send’i deneyin
|
||||
downloadingPageProgress = { $filename } indiriliyor ({ $size })
|
||||
downloadingPageMessage = Dosyanız indirilip şifresi çözülürken lütfen bu sekmeyi açık bırakın.
|
||||
errorAltText
|
||||
|
||||
@@ -11,7 +11,7 @@ uploadPageBrowseButton = Виберіть файл на комп'ютері
|
||||
.title = Виберіть файл на комп'ютері
|
||||
uploadPageMultipleFilesAlert = Вивантаження кількох файлів чи тек на даний момент не підтримується.
|
||||
uploadPageBrowseButtonTitle = Вивантажити файл
|
||||
uploadingPageHeader = Вивантажуємо ваш файл
|
||||
uploadingPageProgress = Вивантажуємо { $filename } ({ $size })
|
||||
importingFile = Імпортуємо...
|
||||
verifyingFile = Перевіряємо...
|
||||
encryptingFile = Шифруємо...
|
||||
@@ -50,6 +50,8 @@ downloadButtonLabel = Завантажити
|
||||
.title = Завантажити
|
||||
downloadNotification = Ваше завантаження готово.
|
||||
downloadFinish = Завантаження готово
|
||||
// This message is displayed when uploading or downloading a file, e.g. "(1,3 MB of 10 MB)".
|
||||
fileSizeProgress = ({ $partialSize } з { $totalSize })
|
||||
// Firefox Send is a brand name and should not be localized. Title text for button should be the same.
|
||||
sendYourFilesLink = Спробуйте Firefox Send
|
||||
.title = Спробуйте Firefox Send
|
||||
@@ -67,6 +69,7 @@ expiredPageHeader = Посилання не існує, або час його
|
||||
notSupportedHeader = Ваш браузер не підтримується.
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
notSupportedDetail = На жаль, цей браузер не підтримує веб-технологію, завдяки якій працює Firefox Send. Вам треба скористатися іншим браузером. Ми рекомендуємо Firefox!
|
||||
notSupportedLink = Чому мій браузер не підтримується?
|
||||
notSupportedOutdatedDetail = На жаль, ця версія Firefox не підтримує веб-технологію, завдяки якій працює Firefox Send. Вам потрібно оновити свій браузер.
|
||||
updateFirefox = Оновити Firefox
|
||||
downloadFirefoxButtonSub = Безкоштовне завантаження
|
||||
|
||||
97
public/locales/vi/send.ftl
Normal file
97
public/locales/vi/send.ftl
Normal file
@@ -0,0 +1,97 @@
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
title = Firefox Send
|
||||
siteSubtitle = Thử nghiệm trên web
|
||||
siteFeedback = Phản hồi
|
||||
uploadPageHeader = Chia sẻ tập tin riêng tư, được mã hóa
|
||||
uploadPageExplainer = Gửi tập tin qua một liên kết an toàn, riêng tư, được mã hóa và tự động hết hạn để chắc chắn rằng dữ liệu của bạn không nằm mãi mãi trên Internet.
|
||||
uploadPageLearnMore = Tìm hiểu thêm
|
||||
uploadPageDropMessage = Kéo thả tập tin của bạn vào đây và bắt đầu tải lên
|
||||
uploadPageSizeMessage = Để có thể hoạt động tốt nhất, hãy giữ tập tin của bạn dưới 1GB.
|
||||
uploadPageBrowseButton = Chọn một tập tin từ máy tính
|
||||
.title = Chọn một tập tin từ máy tính
|
||||
uploadPageMultipleFilesAlert = Tải lên nhiều tập tin một lúc hoặc tải lên một thư mục chưa được hỗ trợ.
|
||||
uploadPageBrowseButtonTitle = Tải tập tin lên
|
||||
uploadingPageProgress = Đang tải lên { $filename } ({ $size })
|
||||
importingFile = Đang nhập...
|
||||
verifyingFile = Đang xác thực...
|
||||
encryptingFile = Đang mã hóa...
|
||||
decryptingFile = Đang giải mã...
|
||||
notifyUploadDone = Quá trình tải lên đã hoàn tất.
|
||||
uploadingPageMessage = Một khi tập tin được tải lên, bạn sẽ có thể thiết lập các tùy chọn hết hạn của tập tin.
|
||||
uploadingPageCancel = Hủy tải lên
|
||||
.title = Hủy tải lên
|
||||
uploadCancelNotification = Quá trình tải lên đã bị hủy.
|
||||
uploadingPageLargeFileMessage = Tập tin này khá nặng và sẽ tốn một chút thời gian để tải lên. Chờ chút nhé!
|
||||
uploadingFileNotification = Thông báo cho tôi khi tải lên hoàn tất.
|
||||
uploadSuccessConfirmHeader = Đã sẵn sàng để Gửi
|
||||
uploadSvgAlt
|
||||
.alt = Tải lên
|
||||
uploadSuccessTimingHeader = Liên kết đến tập tin của bạn sẽ hết hạn sau 1 lượt tải về hoặc trong 24 giờ.
|
||||
copyUrlFormLabelWithName = Sao chép và chia sẻ liên kết để gửi tập tin của bạn: { $filename }
|
||||
// Note: Title text for button should be the same.
|
||||
copyUrlFormButton = Sao chép vào vùng nhớ tạm
|
||||
.title = Sao chép vào vùng nhớ tạm
|
||||
copiedUrl = Đã sao chép!
|
||||
// Note: Title text for button should be the same.
|
||||
deleteFileButton = Xóa tập tin
|
||||
.title = Xóa tập tin
|
||||
// Note: Title text for button should be the same.
|
||||
sendAnotherFileLink = Gửi tập tin khác
|
||||
.title = Gửi tập tin khác
|
||||
// Alternative text used on the download link/button (indicates an action).
|
||||
downloadAltText
|
||||
.alt = Tải về
|
||||
downloadFileName = Tải về { $filename }
|
||||
downloadFileSize = ({ $size })
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
downloadMessage = Bạn của bạn đang gửi một tập tin thông qua Firefox Send, một dịch vụ cho phép bạn chia sẻ tập tin một cách an toàn, riêng tư, có liên kết được mã hóa và sẽ tự động hết hạn để chắc chắn rằng dữ liệu của bạn không nằm mãi mãi trên Internet.
|
||||
// Text and title used on the download link/button (indicates an action).
|
||||
downloadButtonLabel = Tải về
|
||||
.title = Tải về
|
||||
downloadNotification = Quá trình tải về đã hoàn tất.
|
||||
downloadFinish = Tải về hoàn tất
|
||||
// This message is displayed when uploading or downloading a file, e.g. "(1,3 MB of 10 MB)".
|
||||
fileSizeProgress = ({ $partialSize } trong { $totalSize })
|
||||
// Firefox Send is a brand name and should not be localized. Title text for button should be the same.
|
||||
sendYourFilesLink = Dùng thử Firefox Send
|
||||
.title = Dùng thử Firefox Send
|
||||
downloadingPageProgress = Đang tải về { $filename } ({ $size })
|
||||
downloadingPageMessage = Vui lòng giữ cửa sổ này mở trong khi chúng tôi lấy tập tin và giải mã chúng.
|
||||
errorAltText
|
||||
.alt = Lỗi tải lên
|
||||
errorPageHeader = Có gì đó không ổn!
|
||||
errorPageMessage = Đã có lỗi trong quá trình tải lên tập tin.
|
||||
errorPageLink = Gửi tập tin khác
|
||||
fileTooBig = Tập tin này quá lớn để tải lên. Kích thước tập tin phải nhỏ hơn { $size }.
|
||||
linkExpiredAlt
|
||||
.alt = Liên kết đã hết hạn
|
||||
expiredPageHeader = Liên kết này đã hết hạn hoặc chưa từng được sử dụng!
|
||||
notSupportedHeader = Trình duyệt của bạn không được hỗ trợ.
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
notSupportedDetail = Thật không may trình duyệt này không hỗ trợ công nghệ đã tạo nên Firefox Send. Bạn cần thử với trình duyệt khác. Chúng tôi khuyên dùng Firefox!
|
||||
notSupportedLink = Tại sao trình duyệt của tôi không được hỗ trợ?
|
||||
notSupportedOutdatedDetail = Thật không may là phiên bản Firefox này không hỗ trợ công nghệ được sử dụng trong Firefox Send. Bạn cần cập nhật trình duyệt của bạn.
|
||||
updateFirefox = Cập nhật Firefox
|
||||
downloadFirefoxButtonSub = Tải về miễn phí
|
||||
uploadedFile = Tập tin
|
||||
copyFileList = Sao chép URL
|
||||
// expiryFileList is used as a column header
|
||||
expiryFileList = Hết hạn trong
|
||||
deleteFileList = Xóa
|
||||
nevermindButton = Đừng bận tâm
|
||||
legalHeader = Điều khoản & Quyền riêng tư
|
||||
legalNoticeTestPilot = Firefox Send hiện tại đang là một thử nghiệm Test Pilot, và phải tuân theo <a>Điều khoản dịch vụ</a> và <a>Lưu ý về Quyền riêng tư</a>. Bạn có thể tìm hiểu thêm về thử nghiệm này và dữ liệu được thu thập <a>tại đây</a>.
|
||||
legalNoticeMozilla = Sử dụng trang web Firefox Send cũng phải tuân theo Mozilla's <a>Lưu ý về Quyền riêng tư của trang web</a> và <a>Điều khoản sử dụng của trang web</a>.
|
||||
deletePopupText = Xóa tập tin này?
|
||||
deletePopupYes = Đồng ý
|
||||
deletePopupCancel = Hủy bỏ
|
||||
deleteButtonHover
|
||||
.title = Xóa
|
||||
copyUrlHover
|
||||
.title = Sao chép URL
|
||||
footerLinkLegal = Pháp lý
|
||||
// Test Pilot is a proper name and should not be localized.
|
||||
footerLinkAbout = Về Test Pilot
|
||||
footerLinkPrivacy = Quyền riêng tư
|
||||
footerLinkTerms = Điều khoản
|
||||
footerLinkCookies = Cookies
|
||||
@@ -11,7 +11,7 @@ uploadPageBrowseButton = 选择一个您电脑上的文件
|
||||
.title = 选择一个您电脑上的文件
|
||||
uploadPageMultipleFilesAlert = 目前不支持上传多个文件或上传文件夹。
|
||||
uploadPageBrowseButtonTitle = 上传文件
|
||||
uploadingPageHeader = 正在上传您的文件
|
||||
uploadingPageProgress = 正在上传 { $filename } ({ $size })
|
||||
importingFile = 正在导入…
|
||||
verifyingFile = 正在验证…
|
||||
encryptingFile = 正在加密…
|
||||
@@ -50,6 +50,8 @@ downloadButtonLabel = 下载
|
||||
.title = 下载
|
||||
downloadNotification = 您的下载已完成。
|
||||
downloadFinish = 下载完成
|
||||
// This message is displayed when uploading or downloading a file, e.g. "(1,3 MB of 10 MB)".
|
||||
fileSizeProgress = ({ $partialSize } / { $totalSize })
|
||||
// Firefox Send is a brand name and should not be localized. Title text for button should be the same.
|
||||
sendYourFilesLink = 尝试 Firefox Send
|
||||
.title = 尝试 Firefox Send
|
||||
@@ -67,6 +69,7 @@ expiredPageHeader = 此链接已过期或者从未生效。
|
||||
notSupportedHeader = 不支持您的浏览器。
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
notSupportedDetail = 很可惜,您的浏览器不支持 Firefox Send 所使用的 Web 技术。请改用其他浏览器。我们推荐使用 Firefox!
|
||||
notSupportedLink = 为什么不支持我的浏览器?
|
||||
notSupportedOutdatedDetail = 很可惜,此版本的 Firefox 不支持 Firefox Send 所使用的 Web 技术。您需要更新浏览器才能使用它。
|
||||
updateFirefox = 更新 Firefox
|
||||
downloadFirefoxButtonSub = 免费下载
|
||||
|
||||
@@ -11,7 +11,7 @@ uploadPageBrowseButton = 選擇您電腦上的檔案
|
||||
.title = 選擇您電腦上的檔案
|
||||
uploadPageMultipleFilesAlert = 目前暫不支援上傳多個檔案或資料夾。
|
||||
uploadPageBrowseButtonTitle = 上傳檔案
|
||||
uploadingPageHeader = 正在上傳檔案
|
||||
uploadingPageProgress = 正在上傳 { $filename }({ $size })
|
||||
importingFile = 匯入中…
|
||||
verifyingFile = 驗證中…
|
||||
encryptingFile = 加密中…
|
||||
@@ -50,6 +50,8 @@ downloadButtonLabel = 下載
|
||||
.title = 下載
|
||||
downloadNotification = 下載完成。
|
||||
downloadFinish = 下載完成
|
||||
// This message is displayed when uploading or downloading a file, e.g. "(1,3 MB of 10 MB)".
|
||||
fileSizeProgress = ({ $partialSize },共 { $totalSize })
|
||||
// Firefox Send is a brand name and should not be localized. Title text for button should be the same.
|
||||
sendYourFilesLink = 試用 Firefox Send
|
||||
.title = 試用 Firefox Send
|
||||
@@ -67,6 +69,7 @@ expiredPageHeader = 鏈結已失效,或根本不存在!
|
||||
notSupportedHeader = 不支援您的瀏覽器。
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
notSupportedDetail = 很可惜,您使用的瀏覽器並不支援 Firefox Send 所需的 Web 技術。請改用其他瀏覽器,我們推薦使用 Firefox!
|
||||
notSupportedLink = 為什麼我的瀏覽器不支援?
|
||||
notSupportedOutdatedDetail = 很可惜,此版本的 Firefox 不支援 Firefox Send 所需的 Web 技術。請更新瀏覽器後再使用。
|
||||
updateFirefox = 更新 Firefox
|
||||
downloadFirefoxButtonSub = 免費下載
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*** index.html ***/
|
||||
html {
|
||||
background: url('resources/send_bg.svg');
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', Helvetica,
|
||||
Arial, sans-serif;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'segoe ui',
|
||||
'helvetica neue', helvetica, ubuntu, roboto, noto, arial, sans-serif;
|
||||
font-weight: 200;
|
||||
background-size: 110%;
|
||||
background-repeat: no-repeat;
|
||||
@@ -13,6 +13,8 @@ html {
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'segoe ui',
|
||||
'helvetica neue', helvetica, ubuntu, roboto, noto, arial, sans-serif;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0;
|
||||
@@ -266,12 +268,33 @@ tbody {
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
#uploaded-file {
|
||||
width: 35%;
|
||||
}
|
||||
|
||||
#copy-file-list {
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
#expiry-file-list {
|
||||
width: 21%;
|
||||
}
|
||||
|
||||
#delete-file-list {
|
||||
width: 12%;
|
||||
}
|
||||
|
||||
.icon-delete,
|
||||
.icon-copy,
|
||||
.icon-check {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.icon-copy[disabled="disabled"] {
|
||||
pointer-events: none;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
/* Popup container */
|
||||
.popup {
|
||||
position: absolute;
|
||||
@@ -504,6 +527,7 @@ tbody {
|
||||
background: #05a700;
|
||||
border: 1px solid #05a700;
|
||||
cursor: auto;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
#delete-file {
|
||||
|
||||
@@ -4,7 +4,6 @@ const busboy = require('connect-busboy');
|
||||
const path = require('path');
|
||||
const bodyParser = require('body-parser');
|
||||
const helmet = require('helmet');
|
||||
const bytes = require('bytes');
|
||||
const conf = require('./config.js');
|
||||
const storage = require('./storage.js');
|
||||
const Raven = require('raven');
|
||||
@@ -141,14 +140,16 @@ app.get('/download/:id', async (req, res) => {
|
||||
}
|
||||
|
||||
try {
|
||||
const filename = await storage.filename(id);
|
||||
const contentLength = await storage.length(id);
|
||||
const timeToExpiry = await storage.ttl(id);
|
||||
const efilename = await storage.filename(id);
|
||||
const filename = decodeURIComponent(efilename);
|
||||
const filenameJson = JSON.stringify({ filename });
|
||||
const sizeInBytes = await storage.length(id);
|
||||
const ttl = await storage.ttl(id);
|
||||
res.render('download', {
|
||||
filename: decodeURIComponent(filename),
|
||||
filesize: bytes(contentLength),
|
||||
sizeInBytes: contentLength,
|
||||
timeToExpiry: timeToExpiry
|
||||
filename,
|
||||
filenameJson,
|
||||
sizeInBytes,
|
||||
ttl
|
||||
});
|
||||
} catch (e) {
|
||||
res.status(404).render('notfound');
|
||||
@@ -175,10 +176,7 @@ app.get('/assets/download/:id', async (req, res) => {
|
||||
|
||||
file_stream.on('end', async () => {
|
||||
try {
|
||||
const err = await storage.forceDelete(id);
|
||||
if (!err) {
|
||||
log.info('Deleted:', id);
|
||||
}
|
||||
await storage.forceDelete(id);
|
||||
} catch (e) {
|
||||
log.info('DeleteError:', id);
|
||||
}
|
||||
@@ -208,7 +206,6 @@ app.post('/delete/:id', async (req, res) => {
|
||||
try {
|
||||
const err = await storage.delete(id, delete_token);
|
||||
if (!err) {
|
||||
log.info('Deleted:', id);
|
||||
res.sendStatus(200);
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -228,7 +225,6 @@ app.post('/upload', (req, res, next) => {
|
||||
}
|
||||
|
||||
if (
|
||||
!meta.hasOwnProperty('aad') ||
|
||||
!meta.hasOwnProperty('id') ||
|
||||
!meta.hasOwnProperty('filename') ||
|
||||
!validateIV(meta.id)
|
||||
@@ -238,12 +234,9 @@ app.post('/upload', (req, res, next) => {
|
||||
}
|
||||
|
||||
meta.delete = crypto.randomBytes(10).toString('hex');
|
||||
log.info('meta', meta);
|
||||
req.pipe(req.busboy);
|
||||
|
||||
req.busboy.on('file', async (fieldname, file, filename) => {
|
||||
log.info('Uploading:', newId);
|
||||
|
||||
try {
|
||||
await storage.set(newId, file, filename, meta);
|
||||
|
||||
@@ -264,10 +257,7 @@ app.post('/upload', (req, res, next) => {
|
||||
|
||||
req.on('close', async err => {
|
||||
try {
|
||||
const err = await storage.forceDelete(newId);
|
||||
if (!err) {
|
||||
log.info('Deleted:', newId);
|
||||
}
|
||||
await storage.forceDelete(newId);
|
||||
} catch (e) {
|
||||
log.info('DeleteError:', newId);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ const redis_client = redis.createClient({
|
||||
});
|
||||
|
||||
redis_client.on('error', err => {
|
||||
log.info('Redis:', err);
|
||||
log.error('Redis:', err);
|
||||
});
|
||||
|
||||
if (conf.s3_bucket) {
|
||||
@@ -155,6 +155,7 @@ function localDelete(id, delete_token) {
|
||||
reject();
|
||||
} else {
|
||||
redis_client.del(id);
|
||||
log.info('Deleted:', id);
|
||||
resolve(fs.unlinkSync(path.join(__dirname, '../static', id)));
|
||||
}
|
||||
});
|
||||
@@ -201,7 +202,6 @@ function awsGet(id) {
|
||||
try {
|
||||
return s3.getObject(params).createReadStream();
|
||||
} catch (err) {
|
||||
log.info('GetFailed', 'Get Object from s3 failed.');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -222,7 +222,6 @@ function awsSet(newId, file, filename, meta) {
|
||||
() => {
|
||||
redis_client.hmset(newId, meta);
|
||||
redis_client.expire(newId, conf.expire_seconds);
|
||||
log.info('awsUploadFinish', 'Upload Finished of ' + filename);
|
||||
},
|
||||
err => {
|
||||
if (hitLimit) {
|
||||
@@ -263,7 +262,7 @@ function awsForceDelete(id) {
|
||||
|
||||
s3.deleteObject(params, function(err, _data) {
|
||||
redis_client.del(id);
|
||||
err ? reject(err) : resolve(err);
|
||||
err ? reject(err) : resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ const driver = new webdriver.Builder().forBrowser('firefox').build();
|
||||
|
||||
driver.get(path.join('file:///', __dirname, '/frontend.test.html'));
|
||||
driver.wait(until.titleIs('Mocha Tests'));
|
||||
driver.wait(until.titleMatches(/^[0-1]$/));
|
||||
driver.wait(until.titleMatches(/^[0-9]$/));
|
||||
|
||||
driver.getTitle().then(title => {
|
||||
driver.quit().then(() => {
|
||||
|
||||
@@ -4,12 +4,10 @@ const FakeFile = window.FakeFile;
|
||||
const assert = window.assert;
|
||||
const server = window.server;
|
||||
const hexToArray = window.hexToArray;
|
||||
const arrayToHex = window.arrayToHex;
|
||||
const sinon = window.sinon;
|
||||
|
||||
let file;
|
||||
let encryptedIV;
|
||||
let fileHash;
|
||||
let secretKey;
|
||||
let originalBlob;
|
||||
|
||||
@@ -59,28 +57,6 @@ describe('File Sender', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('Should get a hashing event emission', function() {
|
||||
const file = new FakeFile('hello_world.txt', ['This is some data.']);
|
||||
const fs = new FileSender(file);
|
||||
let testHashing = true;
|
||||
|
||||
fs.on('hashing', isStillHashing => {
|
||||
assert(!(!testHashing && isStillHashing));
|
||||
testHashing = isStillHashing;
|
||||
});
|
||||
|
||||
return fs
|
||||
.upload()
|
||||
.then(info => {
|
||||
assert(info);
|
||||
assert(!testHashing);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err, err.stack);
|
||||
assert.fail();
|
||||
});
|
||||
});
|
||||
|
||||
it('Should get a encrypting event emission', function() {
|
||||
const file = new FakeFile('hello_world.txt', ['This is some data.']);
|
||||
const fs = new FileSender(file);
|
||||
@@ -116,45 +92,40 @@ describe('File Sender', function() {
|
||||
readRaw.onload = function(event) {
|
||||
const rawArray = new Uint8Array(this.result);
|
||||
originalBlob = rawArray;
|
||||
|
||||
window.crypto.subtle.digest('SHA-256', rawArray).then(hash => {
|
||||
fileHash = hash;
|
||||
window.crypto.subtle
|
||||
.importKey(
|
||||
'jwk',
|
||||
{
|
||||
kty: 'oct',
|
||||
k: key,
|
||||
alg: 'A128GCM',
|
||||
ext: true
|
||||
},
|
||||
{
|
||||
name: 'AES-GCM'
|
||||
},
|
||||
true,
|
||||
['encrypt', 'decrypt']
|
||||
)
|
||||
.then(cryptoKey => {
|
||||
window.crypto.subtle
|
||||
.encrypt(
|
||||
{
|
||||
name: 'AES-GCM',
|
||||
iv: hexToArray(IV),
|
||||
additionalData: hash,
|
||||
tagLength: 128
|
||||
},
|
||||
cryptoKey,
|
||||
rawArray
|
||||
)
|
||||
.then(encrypted => {
|
||||
assert(
|
||||
new Uint8Array(encrypted).toString() ===
|
||||
new Uint8Array(file).toString()
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
window.crypto.subtle
|
||||
.importKey(
|
||||
'jwk',
|
||||
{
|
||||
kty: 'oct',
|
||||
k: key,
|
||||
alg: 'A128GCM',
|
||||
ext: true
|
||||
},
|
||||
{
|
||||
name: 'AES-GCM'
|
||||
},
|
||||
true,
|
||||
['encrypt', 'decrypt']
|
||||
)
|
||||
.then(cryptoKey => {
|
||||
window.crypto.subtle
|
||||
.encrypt(
|
||||
{
|
||||
name: 'AES-GCM',
|
||||
iv: hexToArray(IV),
|
||||
tagLength: 128
|
||||
},
|
||||
cryptoKey,
|
||||
rawArray
|
||||
)
|
||||
.then(encrypted => {
|
||||
assert(
|
||||
new Uint8Array(encrypted).toString() ===
|
||||
new Uint8Array(file).toString()
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
readRaw.readAsArrayBuffer(newFile);
|
||||
@@ -179,7 +150,6 @@ describe('File Receiver', function() {
|
||||
|
||||
FakeXHR.prototype.getResponseHeader = function() {
|
||||
return JSON.stringify({
|
||||
aad: arrayToHex(new Uint8Array(fileHash)),
|
||||
filename: 'hello_world.txt',
|
||||
id: encryptedIV
|
||||
});
|
||||
@@ -198,7 +168,6 @@ describe('File Receiver', function() {
|
||||
if (
|
||||
file === undefined ||
|
||||
encryptedIV === undefined ||
|
||||
fileHash === undefined ||
|
||||
secretKey === undefined
|
||||
) {
|
||||
assert.fail(
|
||||
@@ -246,10 +215,6 @@ describe('File Receiver', function() {
|
||||
testDecrypting = isStillDecrypting;
|
||||
});
|
||||
|
||||
fr.on('safe', isSafe => {
|
||||
assert(isSafe);
|
||||
});
|
||||
|
||||
return fr
|
||||
.download()
|
||||
.then(([decrypted, name]) => {
|
||||
@@ -262,123 +227,4 @@ describe('File Receiver', function() {
|
||||
assert.fail();
|
||||
});
|
||||
});
|
||||
|
||||
it('Should emit hashing events', function() {
|
||||
const fr = new FileReceiver();
|
||||
location.hash = secretKey;
|
||||
|
||||
let testHashing = true;
|
||||
|
||||
fr.on('hashing', isStillHashing => {
|
||||
assert(!(!testHashing && isStillHashing));
|
||||
testHashing = isStillHashing;
|
||||
});
|
||||
|
||||
fr.on('safe', isSafe => {
|
||||
assert(isSafe);
|
||||
});
|
||||
|
||||
return fr
|
||||
.download()
|
||||
.then(([decrypted, name]) => {
|
||||
assert(decrypted);
|
||||
assert(name);
|
||||
assert(!testHashing);
|
||||
})
|
||||
.catch(err => {
|
||||
assert.fail();
|
||||
});
|
||||
});
|
||||
|
||||
it('Should catch fraudulent checksums', function(done) {
|
||||
// Use the secret key and file hash of the previous file to encrypt,
|
||||
// which has a different hash than this one (different strings).
|
||||
const newFile = new FakeFile('hello_world.txt', [
|
||||
'This is some data, with a changed hash.'
|
||||
]);
|
||||
const readRaw = new FileReader();
|
||||
|
||||
readRaw.onload = function(event) {
|
||||
const plaintext = new Uint8Array(this.result);
|
||||
window.crypto.subtle
|
||||
.importKey(
|
||||
'jwk',
|
||||
{
|
||||
kty: 'oct',
|
||||
k: secretKey,
|
||||
alg: 'A128GCM',
|
||||
ext: true
|
||||
},
|
||||
{
|
||||
name: 'AES-GCM'
|
||||
},
|
||||
true,
|
||||
['encrypt', 'decrypt']
|
||||
)
|
||||
.then(key => {
|
||||
// The file hash used here is the hash of the fake
|
||||
// file from the previous test; it's a phony checksum.
|
||||
return window.crypto.subtle.encrypt(
|
||||
{
|
||||
name: 'AES-GCM',
|
||||
iv: hexToArray(encryptedIV),
|
||||
additionalData: fileHash,
|
||||
tagLength: 128
|
||||
},
|
||||
key,
|
||||
plaintext
|
||||
);
|
||||
})
|
||||
.then(encrypted => {
|
||||
file = encrypted;
|
||||
const fr = new FileReceiver();
|
||||
location.hash = secretKey;
|
||||
|
||||
fr.on('unsafe', isUnsafe => {
|
||||
assert(isUnsafe);
|
||||
});
|
||||
|
||||
fr.on('safe', () => {
|
||||
// This event should not be emitted.
|
||||
assert.fail();
|
||||
});
|
||||
|
||||
fr
|
||||
.download()
|
||||
.then(() => {
|
||||
assert.fail();
|
||||
done();
|
||||
})
|
||||
.catch(err => {
|
||||
assert(1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
};
|
||||
readRaw.readAsArrayBuffer(newFile);
|
||||
});
|
||||
|
||||
it('Should not decrypt with an incorrect checksum', function() {
|
||||
FakeXHR.prototype.getResponseHeader = function() {
|
||||
return JSON.stringify({
|
||||
aad: 'some_bad_hashz',
|
||||
filename: 'hello_world.txt',
|
||||
id: encryptedIV
|
||||
});
|
||||
};
|
||||
|
||||
const fr = new FileReceiver();
|
||||
location.hash = secretKey;
|
||||
|
||||
return fr
|
||||
.download()
|
||||
.then(([decrypted, name]) => {
|
||||
assert(decrypted);
|
||||
assert(name);
|
||||
assert.fail();
|
||||
})
|
||||
.catch(err => {
|
||||
assert(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -46,7 +46,6 @@ describe('Server integration tests', function() {
|
||||
.set(
|
||||
'X-File-Metadata',
|
||||
JSON.stringify({
|
||||
aad: '11111',
|
||||
id: '111111111111111111111111',
|
||||
filename: 'test_upload.txt'
|
||||
})
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
<script src="/download.js"></script>
|
||||
<div id="download-page-one">
|
||||
<div class="title">
|
||||
<span id="dl-filename"
|
||||
<span id="dl-file"
|
||||
data-filename="{{filename}}"
|
||||
data-size="{{sizeInBytes}}"
|
||||
data-ttl="{{ttl}}"
|
||||
data-l10n-id="downloadFileName"
|
||||
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>
|
||||
data-l10n-args='{{filenameJson}}'></span>
|
||||
<span id="dl-filesize"></span>
|
||||
</div>
|
||||
<div class="description" data-l10n-id="downloadMessage"></div>
|
||||
<img src="/resources/illustration_download.svg" id="download-img" data-l10n-id="downloadAltText"/>
|
||||
@@ -18,10 +18,7 @@
|
||||
</div>
|
||||
|
||||
<div id="download-progress" hidden="true">
|
||||
<div class="title"
|
||||
data-l10n-id="downloadingPageProgress"
|
||||
data-l10n-args='{"filename": "{{filename}}", "size": "{{filesize}}"}'>
|
||||
</div>
|
||||
<div id="dl-title" class="title"></div>
|
||||
<div class="description" data-l10n-id="downloadingPageMessage"></div>
|
||||
<!-- progress bar here -->
|
||||
<div class="progress-bar" id="dl-progress">
|
||||
@@ -35,5 +32,5 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a class="send-new" data-l10n-id="sendYourFilesLink" target="_blank"></a>
|
||||
<a class="send-new" data-state="completed" data-l10n-id="sendYourFilesLink" href="/"></a>
|
||||
</div>
|
||||
|
||||
@@ -21,10 +21,10 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<!-- htmllint attr-bans="false" -->
|
||||
<th width="35%" data-l10n-id="uploadedFile"></th>
|
||||
<th width="25%" data-l10n-id="copyFileList"></th>
|
||||
<th width="21%" data-l10n-id="expiryFileList"></th>
|
||||
<th width="12%" data-l10n-id="deleteFileList"></th>
|
||||
<th id="uploaded-file" data-l10n-id="uploadedFile"></th>
|
||||
<th id="copy-file-list" data-l10n-id="copyFileList"></th>
|
||||
<th id="expiry-file-list" data-l10n-id="expiryFileList"></th>
|
||||
<th id="delete-file-list" data-l10n-id="deleteFileList"></th>
|
||||
<!-- htmllint tag-bans="$previous" -->
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -35,7 +35,7 @@
|
||||
</div>
|
||||
|
||||
<div id="upload-progress" hidden="true">
|
||||
<div class="title" id="upload-filename" data-l10n-id="uploadingPageHeader"></div>
|
||||
<div class="title" id="upload-filename"></div>
|
||||
<div class="description"></div>
|
||||
<!-- progress bar here -->
|
||||
<div class="progress-bar" id="ul-progress">
|
||||
@@ -61,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" id="send-new-completed" data-l10n-id="sendAnotherFileLink"></a>
|
||||
<a class="send-new" data-state="completed" data-l10n-id="sendAnotherFileLink" href="/"></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -69,5 +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" id="send-new-error" data-l10n-id="sendAnotherFileLink"></a>
|
||||
<a class="send-new" data-state="errored" data-l10n-id="sendAnotherFileLink"></a>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="defaultLanguage" content="en-US">
|
||||
@@ -27,7 +28,7 @@
|
||||
<img src="/resources/send_logo.svg" alt="Send"/><h1 class="site-title">Send</h1>
|
||||
</a>
|
||||
<div class="site-subtitle">
|
||||
<a href="https://testpilot.firefox.com" target="_blank">Firefox Test Pilot</a>
|
||||
<a href="https://testpilot.firefox.com">Firefox Test Pilot</a>
|
||||
<div data-l10n-id="siteSubtitle">web experiment</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -36,23 +37,23 @@
|
||||
<div class="all">
|
||||
<noscript>
|
||||
<h2>Firefox Send requires JavaScript</h2>
|
||||
<p><a href="https://github.com/mozilla/send/blob/master/docs/faq.md#why-does-firefox-send-require-javascript" target="_blank" rel="noreferrer noopener">Why does Firefox Send require JavaScript?</a></p>
|
||||
<p><a href="https://github.com/mozilla/send/blob/master/docs/faq.md#why-does-firefox-send-require-javascript">Why does Firefox Send require JavaScript?</a></p>
|
||||
<p>Please enable JavaScript and try again.</p>
|
||||
</noscript>
|
||||
{{{body}}}
|
||||
</div>
|
||||
<div class="footer">
|
||||
<div class="legal-links">
|
||||
<a href="https://www.mozilla.org" target="_blank"><img class="mozilla-logo" src="/resources/mozilla-logo.svg"/></a>
|
||||
<a href="https://www.mozilla.org/about/legal" data-l10n-id="footerLinkLegal" target="_blank">Legal</a>
|
||||
<a href="https://testpilot.firefox.com/about" data-l10n-id="footerLinkAbout" target="_blank">About Test Pilot</a>
|
||||
<a href="/legal" data-l10n-id="footerLinkPrivacy" target="_blank">Privacy</a>
|
||||
<a href="/legal" data-l10n-id="footerLinkTerms" target="_blank">Terms</a>
|
||||
<a href="https://www.mozilla.org/privacy/websites/#cookies" data-l10n-id="footerLinkCookies" target="_blank">Cookies</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">Legal</a>
|
||||
<a href="https://testpilot.firefox.com/about" data-l10n-id="footerLinkAbout">About Test Pilot</a>
|
||||
<a href="/legal" data-l10n-id="footerLinkPrivacy">Privacy</a>
|
||||
<a href="/legal" data-l10n-id="footerLinkTerms">Terms</a>
|
||||
<a href="https://www.mozilla.org/privacy/websites/#cookies" data-l10n-id="footerLinkCookies">Cookies</a>
|
||||
</div>
|
||||
<div class="social-links">
|
||||
<a href="https://github.com/mozilla/send" target="_blank" rel="noreferrer noopener"><img class="github" src="/resources/github-icon.svg"/></a>
|
||||
<a href="https://twitter.com/FxTestPilot" target="_blank" rel="noreferrer noopener"><img class="twitter" src="/resources/twitter-icon.svg"/></a>
|
||||
<a href="https://github.com/mozilla/send"><img class="github" src="/resources/github-icon.svg"/></a>
|
||||
<a href="https://twitter.com/FxTestPilot"><img class="twitter" src="/resources/twitter-icon.svg"/></a>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -4,5 +4,5 @@
|
||||
<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>
|
||||
<a class="send-new" href="/" data-state="notfound" data-l10n-id="sendYourFilesLink"></a>
|
||||
</div>
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
</a>
|
||||
{{else}}
|
||||
<div class="description" data-l10n-id="notSupportedDetail">Unfortunately this browser does not support the web technology that powers Firefox Send. You’ll need to try another browser. We recommend Firefox!</div>
|
||||
<div class="description"><a href="https://github.com/mozilla/send/blob/master/docs/faq.md#why-is-my-browser-not-supported" data-l10n-id="notSupportedLink" target="_blank" rel="noopener noreferrer">Why is my browser not supported?</a></div>
|
||||
<a id="dl-firefox" href="https://www.mozilla.org/firefox/new/?scene=2" target="_blank">
|
||||
<div class="description"><a href="https://github.com/mozilla/send/blob/master/docs/faq.md#why-is-my-browser-not-supported" data-l10n-id="notSupportedLink">Why is my browser not supported?</a></div>
|
||||
<a id="dl-firefox" href="https://www.mozilla.org/firefox/new/?scene=2">
|
||||
<img src="/resources/firefox_logo-only.svg" class="firefox-logo" alt="Firefox"/>
|
||||
<div class="unsupported-button-text">Firefox<br>
|
||||
<span data-l10n-id="downloadFirefoxButtonSub">Free Download</span>
|
||||
|
||||
22
webpack.config.js
Normal file
22
webpack.config.js
Normal file
@@ -0,0 +1,22 @@
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
upload: ['./frontend/src/upload.js'],
|
||||
download: ['./frontend/src/download.js']
|
||||
},
|
||||
output: {
|
||||
filename: '[name].js',
|
||||
path: path.resolve(__dirname, 'public')
|
||||
},
|
||||
module: {
|
||||
loaders: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
loaders: 'babel-loader',
|
||||
include: [path.resolve(__dirname, 'frontend'), path.resolve(__dirname, 'node_modules/testpilot-ga/src')],
|
||||
query: { babelrc: false, presets: ['es2015', 'stage-2'], plugins: ['add-module-exports'] }
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user