Compare commits

...

106 Commits

Author SHA1 Message Date
Danny Coates
87a8cfba40 v1.1.0 2017-08-07 20:59:39 -07:00
Jae Hyeon Park
f929bb2aec Pontoon: Update Korean (ko) localization of Test Pilot: Firefox Send
Localization authors:
- Hyeonseok Shin <hyeonseok@gmail.com>
- Jae Hyeon Park <wogus150@naver.com>
2017-08-08 02:50:53 +00:00
Pin-guang Chen
aaec16cf52 Pontoon: Update Chinese (zh-TW) localization of Test Pilot: Firefox Send
Localization authors:
- Pin-guang Chen <petercpg@mail.moztw.org>
2017-08-08 02:31:08 +00:00
Pierre Neter
5da43f0da7 Pontoon: Update Vietnamese (vi) localization of Test Pilot: Firefox Send
Localization authors:
- Pierre Neter <pierreneter@gmail.com>
2017-08-07 23:30:51 +00:00
Bjørn I
dffb26349b Pontoon: Update Norwegian Nynorsk (nn-NO) localization of Test Pilot: Firefox Send
Localization authors:
- Bjørn I. <bjorn.svindseth@online.no>
2017-08-07 23:30:49 +00:00
manxmensch
c4751842fe Pontoon: Update Malay (ms) localization of Test Pilot: Firefox Send
Localization authors:
- manxmensch <manxmensch@gmail.com>
2017-08-07 22:50:48 +00:00
Danny Coates
08ec9d98b8 Merge pull request #473 from pdehaan/fix-contributors
Sort contributors alphabetically to prevent churn
2017-08-07 15:41:06 -07:00
Danny Coates
5200928d5c Merge pull request #472 from mozilla/remove-hash-tests
removed references to checksums in frontend tests
2017-08-07 15:36:29 -07:00
Peter deHaan
4c3e37f4b5 Sort contributors alphabetically to prevent churn 2017-08-07 15:24:27 -07:00
Abhinav Adduri
60eda64c8d linting 2017-08-07 15:12:11 -07:00
Abhinav Adduri
4db7fc6db3 removed references to checksums in frontend tests 2017-08-07 14:58:22 -07:00
Danny Coates
5844a9a03c Merge pull request #470 from mozilla/remove-hash
removed the file sha256 hash
2017-08-07 14:42:53 -07:00
YFdyh000
9829e46270 Pontoon: Update Chinese (zh-CN) localization of Test Pilot: Firefox Send
Localization authors:
- YFdyh000 <yfdyh000@gmail.com>
2017-08-07 21:30:49 +00:00
Danny Coates
9a150ddb22 removed the file sha256 hash 2017-08-07 14:05:13 -07:00
Marcelo Poli
e7b90ea1b9 Pontoon: Update Spanish (es-AR) localization of Test Pilot: Firefox Send
Localization authors:
- Marcelo Poli <enzomatrix@gmail.com>
2017-08-07 19:51:18 +00:00
Balázs Meskó
07bcfa2d36 Pontoon: Update Hungarian (hu) localization of Test Pilot: Firefox Send
Localization authors:
- Balázs Meskó <meskobalazs@gmail.com>
2017-08-07 19:51:16 +00:00
Juraj Cigáň
dce134744d Pontoon: Update Slovak (sk) localization of Test Pilot: Firefox Send
Localization authors:
- Juraj Cigáň <kusavica@gmail.com>
2017-08-07 19:31:07 +00:00
Michael Wolf
288bc50484 Pontoon: Update Sorbian, Upper (hsb) localization of Test Pilot: Firefox Send
Localization authors:
- Michael Wolf <milupo@sorbzilla.de>
2017-08-07 19:11:05 +00:00
Michael Wolf
09d6c64a9b Pontoon: Update Sorbian, Lower (dsb) localization of Test Pilot: Firefox Send
Localization authors:
- Michael Wolf <milupo@sorbzilla.de>
2017-08-07 19:11:00 +00:00
Francesco Lodolo
d7a542f2c2 Pontoon: Update Italian (it) localization of Test Pilot: Firefox Send
Localization authors:
- Francesco Lodolo <francesco.lodolo@mozillaitalia.org>
2017-08-07 19:10:55 +00:00
Danny Coates
aca82dd880 Merge pull request #469 from ehuggett/patch-1
Increase mimimum node version to 8.2.0
2017-08-07 12:03:59 -07:00
Danny Coates
aa72a4c72c Merge pull request #468 from mozilla/i465
attach delete-file handler only after upload
2017-08-07 12:03:17 -07:00
Марко Костић (Marko Kostić)
a70dcfc905 Pontoon: Update Serbian (sr) localization of Test Pilot: Firefox Send
Localization authors:
- Марко Костић (Marko Kostić) <marko.m.kostic@gmail.com>
2017-08-07 18:50:39 +00:00
Kohei Yoshino
49731ab8ba Pontoon: Update Japanese (ja) localization of Test Pilot: Firefox Send
Localization authors:
- Kohei Yoshino <kohei.yoshino@gmail.com>
2017-08-07 18:50:34 +00:00
ehuggett
3d9e01f8e5 Increase mimimun node version to 8.2.0
This version has a useful protection against http header splitting
2017-08-07 19:50:10 +01:00
Danny Coates
441fe86186 attach delete-file handler only after upload 2017-08-07 11:47:09 -07:00
Lan Glad
f9283f5f6a Pontoon: Update Slovenian (sl) localization of Test Pilot: Firefox Send
Localization authors:
- Lan Glad <upwinxp@gmail.com>
2017-08-07 18:31:13 +00:00
Maykon Chagas
c8e0b696a6 Pontoon: Update Portuguese (pt-BR) localization of Test Pilot: Firefox Send
Localization authors:
- Maykon Chagas <mchagas@riseup.net>
2017-08-07 18:31:10 +00:00
Michael Köhler
cf5c64a3f8 Pontoon: Update German (de) localization of Test Pilot: Firefox Send
Localization authors:
- Michael Köhler <michael.koehler1@gmx.de>
2017-08-07 18:31:06 +00:00
Danny Coates
097ff19c5f Merge pull request #466 from mozilla/webpack
added webpack
2017-08-07 11:06:54 -07:00
Danny Coates
8767b9b6b0 Merge pull request #427 from gautamkrishnar/master
Extended system font list fixes:#408
2017-08-07 11:04:03 -07:00
Danny Coates
4ac7ac2b24 Merge pull request #448 from nskins/th-width
Migrate width attribute to CSS (Fixes #436)
2017-08-07 10:59:52 -07:00
Théo Chevalier
4cdce6c841 Pontoon: Update French (fr) localization of Test Pilot: Firefox Send
Localization authors:
- Théo Chevalier <theo.chevalier11@gmail.com>
2017-08-07 17:50:36 +00:00
Ton
763414c848 Pontoon: Update Dutch (nl) localization of Test Pilot: Firefox Send
Localization authors:
- Ton <tonnes.mb@gmail.com>
2017-08-07 17:50:33 +00:00
Danny Coates
9172af48fd added webpack 2017-08-07 10:33:41 -07:00
Tymur Faradzhev
14a4297db8 Pontoon: Update Ukrainian (uk) localization of Test Pilot: Firefox Send
Localization authors:
- Tymur Faradzhev <faradzhev.timur@gmail.com>
2017-08-07 17:30:49 +00:00
Rodrigo
a78475aff4 Pontoon: Update Portuguese (pt-PT) localization of Test Pilot: Firefox Send
Localization authors:
- Rodrigo <rodrigo.mcunha@hotmail.com>
2017-08-07 17:30:46 +00:00
Danny Coates
d2b57039bf Merge pull request #457 from mozilla/refactor-progress
factored out progress into progress.js
2017-08-07 09:51:29 -07:00
Pierre Neter
dd5be246d4 Pontoon: Update Vietnamese (vi) localization of Test Pilot: Firefox Send
Localization authors:
- hi <hi@duonganhtuan.com>
- Pierre Neter <pierreneter@gmail.com>
2017-08-07 12:10:33 +00:00
hi
779005845e Pontoon: Update Vietnamese (vi) localization of Test Pilot: Firefox Send
Localization authors:
- hi <hi@duonganhtuan.com>
- Pierre Neter <pierreneter@gmail.com>
2017-08-07 11:50:56 +00:00
hi
f3499326bd Pontoon: Update Vietnamese (vi) localization of Test Pilot: Firefox Send
Localization authors:
- hi <hi@duonganhtuan.com>
- Pierre Neter <pierreneter@gmail.com>
2017-08-07 11:30:52 +00:00
hi
fe10b131fc Pontoon: Update Vietnamese (vi) localization of Test Pilot: Firefox Send
Localization authors:
- hi <hi@duonganhtuan.com>
- Pierre Neter <pierreneter@gmail.com>
2017-08-07 11:11:18 +00:00
avelper
9b6b7aa998 Pontoon: Update Spanish (es-ES) localization of Test Pilot: Firefox Send
Localization authors:
- avelper <avelper@mozilla-hispano.org>
2017-08-07 10:10:59 +00:00
Slimane Amiri
e9d5d8ec11 Pontoon: Update Kabyle (kab) localization of Test Pilot: Firefox Send
Localization authors:
- Slimane Amiri <slimane.amiri@gmail.com>
2017-08-07 10:10:57 +00:00
Kohei Yoshino
73e39ad453 Pontoon: Update Japanese (ja) localization of Test Pilot: Firefox Send
Localization authors:
- Kohei Yoshino <kohei.yoshino@gmail.com>
2017-08-07 09:10:34 +00:00
Danny Coates
c91d24cd86 factored out progress into progress.js 2017-08-06 23:45:22 -07:00
Michal Stanke
1fc83aa902 Pontoon: Update Czech (cs) localization of Test Pilot: Firefox Send
Localization authors:
- Michal Stanke <mstanke@mozilla.cz>
2017-08-07 06:31:07 +00:00
Luna Jernberg
5ac013ca40 Pontoon: Update Swedish (sv-SE) localization of Test Pilot: Firefox Send
Localization authors:
- Luna Jernberg <bittin@cafe8bitar.se>
2017-08-06 18:31:42 +00:00
Danny Coates
6d17b86d28 Merge pull request #452 from mozilla/refactor-metrics
refactored metrics
2017-08-06 09:00:16 -07:00
siparon
bdd7044808 Pontoon: Update Hungarian (hu) localization of Test Pilot: Firefox Send
Localization authors:
- siparon <siparon@gmail.com>
2017-08-06 07:50:50 +00:00
Peter deHaan
96bea84a73 Merge pull request #455 from pdehaan/fix-es-cl-tr-locales
Add a few missing strings from es-CL and tr locales
2017-08-05 23:28:29 -07:00
Peter deHaan
9aef7df82c Add a few missing strings from es-CL and tr locales 2017-08-05 22:59:31 -07:00
Danny Coates
b0d36529a1 refactored metrics 2017-08-05 16:38:08 -07:00
Danny Coates
2a7099a7a2 Merge pull request #444 from Johann-S/patch-2
Chain jQuery calls, do not use events alias and store selectors
2017-08-05 16:32:49 -07:00
Maykon Chagas
4c069949f2 Pontoon: Update Portuguese (pt-BR) localization of Test Pilot: Firefox Send
Localization authors:
- Maykon Chagas <mchagas@riseup.net>
2017-08-05 22:10:39 +00:00
Maykon Chagas
699c97a9c0 Pontoon: Update Portuguese (pt-BR) localization of Test Pilot: Firefox Send
Localization authors:
- Maykon Chagas <mchagas@riseup.net>
2017-08-05 21:51:01 +00:00
Johann-S
3c640061f0 Chain jQuery calls, do not use events alias and store often used selectors 2017-08-05 22:31:57 +02:00
Jim Spentzos
bbd74efc2c Pontoon: Update Greek (el) localization of Test Pilot: Firefox Send
Localization authors:
- Jim Spentzos <jamesspentzos@hotmail.com>
- Giorgos S. <giorgos.skafidas@gmx.com>
2017-08-05 20:30:55 +00:00
Danny Coates
2dfbee090a Merge pull request #416 from mozilla/shim-webcrypto
WIP: use webcrypto-liner to support Safari 10
2017-08-05 10:09:09 -07:00
Danny Coates
a03b1b7a9a only _blank feedback link 2017-08-05 09:57:57 -07:00
Danny Coates
594b584500 formatting / links rel 2017-08-05 09:52:37 -07:00
Danny Coates
d74d339e4b Merge remote-tracking branch 'origin/master' into shim-webcrypto 2017-08-05 09:48:24 -07:00
Danny Coates
859554ce21 only _blank links while downloading. fixed safari link after download 2017-08-05 09:40:57 -07:00
Peter deHaan
703325f223 Merge pull request #451 from boopeshmahendran/AddRelNoopenerNoreferrer
Add rel noopener noreferrer to target='_blank' anchor elements (Fixes #439)
2017-08-05 09:17:10 -07:00
Peter deHaan
9a416e3e78 Merge pull request #449 from kenrick95/master
Add X-UA-Compatible meta tag
2017-08-05 09:14:39 -07:00
Maykon Chagas
8821403a9b Pontoon: Update Portuguese (pt-BR) localization of Test Pilot: Firefox Send
Localization authors:
- Maykon Chagas <mchagas@riseup.net>
2017-08-05 14:50:44 +00:00
alex_mayorga
4a841bf563 Pontoon: Update Spanish (es-MX) localization of Test Pilot: Firefox Send
Localization authors:
- alex_mayorga <alex_mayorga@yahoo.com>
2017-08-05 14:31:06 +00:00
Μιχάλης
4d6995536a Pontoon: Update Greek (el) localization of Test Pilot: Firefox Send
Localization authors:
- Μιχάλης <mikem132@protonmail.com>
2017-08-05 13:50:46 +00:00
Μιχάλης
6ef4e86029 Pontoon: Update Greek (el) localization of Test Pilot: Firefox Send
Localization authors:
- Giorgos S. <giorgos.skafidas@gmx.com>
- Μιχάλης <mikem132@protonmail.com>
2017-08-05 13:30:40 +00:00
Juraj Cigáň
2408c766ce Pontoon: Update Slovak (sk) localization of Test Pilot: Firefox Send
Localization authors:
- Juraj Cigáň <kusavica@gmail.com>
2017-08-05 11:10:31 +00:00
Amin Mahmudian
882e13bac6 Pontoon: Update Persian (fa) localization of Test Pilot: Firefox Send
Localization authors:
- Amin Mahmudian <amin.mahmudian@gmail.com>
2017-08-05 08:50:45 +00:00
Boopesh Mahendran
e48c2cf75b Add rel noopener noreferrer to target='_blank' anchor elements 2017-08-05 13:34:44 +05:30
gautamkrishnar
c8f7e60259 fixed lint errors 2017-08-05 12:59:00 +05:30
Danny Coates
432a39d313 Merge remote-tracking branch 'origin/master' into shim-webcrypto 2017-08-04 21:08:32 -07:00
kenrick95
59ed64698d Add X-UA-Compatible meta tag 2017-08-05 11:59:44 +08:00
Nicholas Skinsacos
6edfe5146c Migrate width attribute to CSS (Fixes #436) 2017-08-04 22:33:29 -04:00
Danny Coates
44ea0756ea Merge pull request #433 from pdehaan/issue-429
Prevent download button from being clicked multiple times
2017-08-04 15:46:56 -07:00
Danny Coates
02fc4d74db Merge remote-tracking branch 'origin/master' into shim-webcrypto 2017-08-04 14:47:40 -07:00
Danny Coates
0f77b6d86b Merge pull request #432 from pdehaan/issue-397
Add contributors script
2017-08-04 12:45:23 -07:00
Pin-guang Chen
ae2eb14fda Pontoon: Update Chinese (zh-TW) localization of Test Pilot: Firefox Send
Localization authors:
- Pin-guang Chen <petercpg@mail.moztw.org>
2017-08-04 18:31:09 +00:00
eljuno
455c4f5472 Pontoon: Update Indonesian (id) localization of Test Pilot: Firefox Send
Localization authors:
- eljuno <eljunotrie_anggoro@yahoo.co.id>
2017-08-04 17:31:10 +00:00
Gautam krishna.R
7335232680 fixed missing font 2017-08-04 22:49:03 +05:30
Peter deHaan
2f9372e8e0 Prevent download button from being clicked multiple times 2017-08-04 10:17:15 -07:00
Michael Wolf
80db158ee3 Pontoon: Update Sorbian, Upper (hsb) localization of Test Pilot: Firefox Send
Localization authors:
- Michael Wolf <milupo@sorbzilla.de>
2017-08-04 16:11:48 +00:00
Michael Wolf
6c18f4fb2c Pontoon: Update Sorbian, Lower (dsb) localization of Test Pilot: Firefox Send
Localization authors:
- Michael Wolf <milupo@sorbzilla.de>
2017-08-04 16:11:45 +00:00
Peter deHaan
f28444f4c6 Add contributors script 2017-08-04 09:10:59 -07:00
Marcelo Poli
93ac742a5e Pontoon: Update Spanish (es-AR) localization of Test Pilot: Firefox Send
Localization authors:
- Marcelo Poli <enzomatrix@gmail.com>
2017-08-04 15:31:33 +00:00
Erica
6dcbc19315 Merge pull request #409 from Johann-S/patch-1
Handle copy clipboard disabled
2017-08-04 10:32:24 -04:00
Ton
65df0fa9cf Pontoon: Update Dutch (nl) localization of Test Pilot: Firefox Send
Localization authors:
- Ton <tonnes.mb@gmail.com>
2017-08-04 12:31:11 +00:00
Kohei Yoshino
387e88907c Pontoon: Update Japanese (ja) localization of Test Pilot: Firefox Send
Localization authors:
- Kohei Yoshino <kohei.yoshino@gmail.com>
2017-08-04 10:10:46 +00:00
Gautam krishna.R
9253695f8d Extended system font list fixes:#408 2017-08-04 15:23:51 +05:30
Alexander Slovesnik
67635b9151 Pontoon: Update Russian (ru) localization of Test Pilot: Firefox Send
Localization authors:
- Alexander Slovesnik <unghost@mozilla-russia.org>
2017-08-04 09:10:33 +00:00
Johann-S
e0abfb5cf7 Handle copy clipboard disabled 2017-08-04 10:22:11 +02:00
Fjoerfoks
f1de6a14da Pontoon: Update Frisian (fy-NL) localization of Test Pilot: Firefox Send
Localization authors:
- Fjoerfoks <fryskefirefox@gmail.com>
2017-08-04 07:30:43 +00:00
Sairam Raavi
38052c1b85 Pontoon: Update Telugu (te) localization of Test Pilot: Firefox Send
Localization authors:
- Sairam Raavi <sairam@riseup.net>
2017-08-04 05:50:36 +00:00
Sahithi
5847faf4e2 Pontoon: Update Telugu (te) localization of Test Pilot: Firefox Send
Localization authors:
- Sairam Raavi <sairam@riseup.net>
- Sahithi <sahithi.thinker@gmail.com>
- నితిన్ <sainithin95@gmail.com>
2017-08-04 05:31:26 +00:00
Francesco Lodolo
0c1c8178f5 Pontoon: Update Italian (it) localization of Test Pilot: Firefox Send
Localization authors:
- Francesco Lodolo <francesco.lodolo@mozillaitalia.org>
2017-08-04 05:31:24 +00:00
Sahithi
86fbd2a4b5 Pontoon: Update Telugu (te) localization of Test Pilot: Firefox Send
Localization authors:
- Sairam Raavi <sairam@riseup.net>
- Sahithi <sahithi.thinker@gmail.com>
2017-08-04 05:10:53 +00:00
Danny Coates
8cfb45944c pruned server logs 2017-08-03 21:47:03 -07:00
Tymur Faradzhev
0289409c9c Pontoon: Update Ukrainian (uk) localization of Test Pilot: Firefox Send
Localization authors:
- Tymur Faradzhev <faradzhev.timur@gmail.com>
2017-08-04 04:10:53 +00:00
Danny Coates
b93a33eba4 an ios friendly copy 2017-08-03 21:06:15 -07:00
Danny Coates
fb41a40128 use webcrypto-liner to support Safari 10 2017-08-03 19:13:38 -07:00
ravmn
e18fda91fe Pontoon: Update Spanish (es-CL) localization of Test Pilot: Firefox Send
Localization authors:
- ravmn <ravmn@ravmn.cl>
2017-08-04 00:31:52 +00:00
YFdyh000
b077635427 Pontoon: Update Chinese (zh-CN) localization of Test Pilot: Firefox Send
Localization authors:
- YFdyh000 <yfdyh000@gmail.com>
2017-08-04 00:31:49 +00:00
Håvar Henriksen
4d4098b7c9 Pontoon: Update Norwegian Bokmål (nb-NO) localization of Test Pilot: Firefox Send
Localization authors:
- Håvar Henriksen <havar@firefox.no>
2017-08-03 23:31:13 +00:00
Emin Mastizada
9e4838d121 Pontoon: Update Azerbaijani (az) localization of Test Pilot: Firefox Send
Localization authors:
- Emin Mastizada <emin@mastizada.com>
2017-08-03 23:31:11 +00:00
62 changed files with 4711 additions and 3150 deletions

78
CONTRIBUTORS Normal file
View 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ć)

View File

@@ -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
};

View File

@@ -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');
});
}
});
});

View File

@@ -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)]);
}
});
});
}
}

View File

@@ -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
View 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
View 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
View 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
};

View File

@@ -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');
});
});
});

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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

File diff suppressed because one or more lines are too long

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 = Ακύρωση

View File

@@ -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

View File

@@ -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

View File

@@ -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í

View File

@@ -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

View File

@@ -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

View 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 = کوکی‌ها

View File

@@ -11,7 +11,7 @@ uploadPageBrowseButton = Sélectionner un fichier sur lordinateur
.title = Sélectionner un fichier sur lordinateur
uploadPageMultipleFilesAlert = Lenvoi de plusieurs fichiers ou de dossiers nest 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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 = 無料ダウンロード

View File

@@ -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

View 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 = 쿠키

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 = Бесплатная загрузка

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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 = కుకీలు

View File

@@ -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 Sendi deneyin
.title = Firefox Sendi deneyin
downloadingPageProgress = { $filename } indiriliyor ({ $size })
downloadingPageMessage = Dosyanız indirilip şifresi çözülürken lütfen bu sekmeyi açık bırakın.
errorAltText

View File

@@ -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 = Безкоштовне завантаження

View 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

View File

@@ -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 = 免费下载

View File

@@ -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 = 免費下載

View File

@@ -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 {

View 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);
}

View File

@@ -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();
});
});
}

View File

@@ -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(() => {

View File

@@ -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);
});
});
});

View File

@@ -46,7 +46,6 @@ describe('Server integration tests', function() {
.set(
'X-File-Metadata',
JSON.stringify({
aad: '11111',
id: '111111111111111111111111',
filename: 'test_upload.txt'
})

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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. Youll 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
View 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'] }
}
]
}
};