Compare commits

...

113 Commits

Author SHA1 Message Date
Danny Coates
0bf8481fd0 v1.1.2 2017-08-17 10:52:33 -07:00
Danny Coates
ae0758ac14 fixed download test 2017-08-17 10:27:18 -07:00
Danny Coates
e9405f49ee xhr download as octet-stream 2017-08-17 10:19:18 -07:00
Danny Coates
a1d0eef8a5 debugging #524 2017-08-17 09:46:51 -07:00
Danny Coates
254b806fb4 fixes #523 copy & share text 2017-08-17 09:27:06 -07:00
Danny Coates
a7aee1450f fixes #522 copy button check mark 2017-08-17 09:16:03 -07:00
Sahithi
fa5573a5ff Pontoon: Update Telugu (te) localization of Test Pilot: Firefox Send
Localization authors:
- Sahithi <sahithi.thinker@gmail.com>
2017-08-17 11:31:19 +00:00
Danny Coates
9714bb0a0a v1.1.1 2017-08-16 18:29:35 -07:00
Danny Coates
ad82d30dd9 Merge pull request #516 from mozilla/cache-assets
cache assets
2017-08-16 17:44:09 -07:00
Danny Coates
757ac14d1a webpacked the heck out of the build 2017-08-16 14:21:01 -07:00
Danny Coates
3e066258c4 Merge pull request #520 from mozilla/i519
fix drag & drop
2017-08-16 11:11:46 -07:00
Danny Coates
127f73b4fe fixes #519 drag & drop 2017-08-16 11:03:17 -07:00
Emin Mastizada
ae5009e1e3 Pontoon: Update Azerbaijani (az) localization of Test Pilot: Firefox Send
Localization authors:
- Emin Mastizada <emin@mastizada.com>
2017-08-16 07:31:19 +00:00
Danny Coates
0ab8ddc894 groundwork for asset caching 2017-08-15 11:55:52 -07:00
Danny Coates
b429841534 use new upload button string 2017-08-15 10:30:19 -07:00
jesferman1993
279f6df6f4 Pontoon: Update Spanish (es-ES) localization of Test Pilot: Firefox Send
Localization authors:
- jesferman1993 <jesferman1993@hotmail.com>
2017-08-15 09:11:11 +00:00
Slimane Amiri
634e6b2834 Pontoon: Update Kabyle (kab) localization of Test Pilot: Firefox Send
Localization authors:
- Slimane Amiri <slimane.amiri@gmail.com>
2017-08-15 07:31:06 +00:00
Danny Coates
856b2cdc60 Merge pull request #515 from mozilla/refactor-upload
removed jquery from upload.js
2017-08-14 21:23:04 -07:00
Danny Coates
41351f877c removed jquery from upload.js 2017-08-14 20:00:14 -07:00
Andreas Pettersson
afbb89fbe8 Pontoon: Update Swedish (sv-SE) localization of Test Pilot: Firefox Send
Localization authors:
- Andreas Pettersson <az@kth.se>
2017-08-14 22:31:48 +00:00
Pierre Neter
b40b45273d Pontoon: Update Vietnamese (vi) localization of Test Pilot: Firefox Send
Localization authors:
- Pierre Neter <pierreneter@gmail.com>
2017-08-14 19:31:32 +00:00
Danny Coates
f1fb877c7f Merge pull request #514 from mozilla/refactor-download
use async and removed jquery from download.js
2017-08-14 12:12:11 -07:00
Jon Vadillo
9d7ad06b1a Pontoon: Update Spanish (es-ES) localization of Test Pilot: Firefox Send
Localization authors:
- Jon Vadillo <vadillo.jon@gmail.com>
2017-08-14 18:30:39 +00:00
Danny Coates
c6a4b089d9 use async and removed jquery from download.js 2017-08-14 11:14:47 -07:00
Danny Coates
24917f8aa5 Merge pull request #513 from mozilla/svg-progress
use svg for progress
2017-08-14 10:05:03 -07:00
Danny Coates
eada94b262 use svg for progress 2017-08-13 18:46:05 -07:00
Danny Coates
43fa551a64 improved exist check 2017-08-13 18:44:59 -07:00
Roberto Alvarado
e1137db946 Pontoon: Update Spanish (es-MX) localization of Test Pilot: Firefox Send
Localization authors:
- Roberto Alvarado <ralv888@gmail.com>
2017-08-14 00:50:56 +00:00
Enol
c1878649b3 Pontoon: Update Asturian (ast) localization of Test Pilot: Firefox Send
Localization authors:
- Enol <enolp@softastur.org>
2017-08-13 17:50:44 +00:00
Michael Wolf
30b86b14ed Pontoon: Update Sorbian, Upper (hsb) localization of Test Pilot: Firefox Send
Localization authors:
- Michael Wolf <milupo@sorbzilla.de>
2017-08-13 15:31:13 +00:00
Michael Wolf
e91b341f8a Pontoon: Update Sorbian, Lower (dsb) localization of Test Pilot: Firefox Send
Localization authors:
- Michael Wolf <milupo@sorbzilla.de>
2017-08-13 15:31:11 +00:00
Jakub Rychlý
70148232c6 Pontoon: Update Czech (cs) localization of Test Pilot: Firefox Send
Localization authors:
- Jakub Rychlý <jrychly@jakubrychly.cz>
- sajdl.vojtech <sajdl.vojtech@gmail.com>
2017-08-13 12:31:06 +00:00
Alexander Slovesnik
56e3d5766c Pontoon: Update Russian (ru) localization of Test Pilot: Firefox Send
Localization authors:
- Alexander Slovesnik <unghost@mozilla-russia.org>
2017-08-12 22:11:20 +00:00
Rhoslyn Prys
350e31ae4a Pontoon: Update Welsh (cy) localization of Test Pilot: Firefox Send
Localization authors:
- Rhoslyn Prys <rprys@yahoo.com>
2017-08-12 19:31:25 +00:00
Marcelo Poli
0794bcc458 Pontoon: Update Spanish (es-AR) localization of Test Pilot: Firefox Send
Localization authors:
- Marcelo Poli <enzomatrix@gmail.com>
2017-08-12 18:31:01 +00:00
Belayet Hossain
a6aee8ad62 Pontoon: Update Bengali (bn-BD) localization of Test Pilot: Firefox Send
Localization authors:
- Belayet Hossain <bellayet@gmail.com>
- S M Sarwar Nobin <smsarwar1996@gmail.com>
2017-08-12 18:30:59 +00:00
josotrix
8305d13dab Pontoon: Update Spanish (es-CL) localization of Test Pilot: Firefox Send
Localization authors:
- josotrix <josotrix@ravmn.cl>
2017-08-12 17:11:33 +00:00
Selim Şumlu
441a520765 Pontoon: Update Turkish (tr) localization of Test Pilot: Firefox Send
Localization authors:
- Selim Şumlu <selim@sum.lu>
2017-08-12 15:50:46 +00:00
josotrix
ba84e59f39 Pontoon: Update Spanish (es-CL) localization of Test Pilot: Firefox Send
Localization authors:
- josotrix <josotrix@ravmn.cl>
- ravmn <ravmn@ravmn.cl>
2017-08-12 15:50:43 +00:00
Pin-guang Chen
22a316ab58 Pontoon: Update Chinese (zh-TW) localization of Test Pilot: Firefox Send
Localization authors:
- Pin-guang Chen <petercpg@mail.moztw.org>
2017-08-12 15:31:26 +00:00
Juraj Cigáň
6b9502d252 Pontoon: Update Slovak (sk) localization of Test Pilot: Firefox Send
Localization authors:
- Juraj Cigáň <kusavica@gmail.com>
2017-08-12 12:11:21 +00:00
Danny Coates
cdc3a5340d defer main js scripts 2017-08-11 19:13:57 -07:00
Danny Coates
f03f7a0286 Merge pull request #510 from mozilla/hooks
added precommit hook for format
2017-08-11 18:34:48 -07:00
Danny Coates
d8a5789701 added precommit hook for format 2017-08-11 18:06:16 -07:00
Danny Coates
8d26e0e742 Merge pull request #502 from mozilla/refactor-filelist
extracted filelist into its own file
2017-08-11 14:18:35 -07:00
Danny Coates
e142d76cb4 Merge pull request #428 from mozilla/add-twitter-og-cards
add twitter and open graph cards
2017-08-11 14:12:44 -07:00
Danny Coates
c488c1d724 added BASE_URL environment variable 2017-08-11 14:02:44 -07:00
Danny Coates
bed57af6c5 extracted filelist into its own file 2017-08-11 13:50:58 -07:00
Jim Spentzos
7500bd8326 Pontoon: Update Greek (el) localization of Test Pilot: Firefox Send
Localization authors:
- Jim Spentzos <jamesspentzos@hotmail.com>
- Μιχάλης <mikem132@protonmail.com>
2017-08-11 20:31:46 +00:00
Danny Coates
0250924961 Merge pull request #506 from varghesethomase/404-page
404 page
2017-08-11 12:20:02 -07:00
Varghese Thomas
70813556ad Reverting unwanted notfound page response 2017-08-12 00:29:25 +05:30
Марко Костић (Marko Kostić)
2bb9af1943 Pontoon: Update Serbian (sr) localization of Test Pilot: Firefox Send
Localization authors:
- Марко Костић (Marko Kostić) <marko.m.kostic@gmail.com>
2017-08-11 18:51:01 +00:00
Danny Coates
55bd44a8f5 Merge pull request #508 from mozilla/fixes478
fixes 478
2017-08-11 10:58:24 -07:00
Abhinav Adduri
d83900f272 fixes 478 2017-08-11 10:50:37 -07:00
Jordi Serratosa
fa4f9299b2 Pontoon: Update Catalan (ca) localization of Test Pilot: Firefox Send
Localization authors:
- Jordi Serratosa <jordis@softcatala.cat>
2017-08-11 16:31:45 +00:00
Danny Coates
0f7b19c385 Merge pull request #504 from mozilla/fix-japanese-browse-btn
fix japanese browse button
2017-08-11 08:37:34 -07:00
Varghese Thomas
a9c1dd0180 Replacing all send status 404 with notfound page 2017-08-11 20:52:18 +05:30
Varghese Thomas
c468e2f34e Sending not found page for invalid url id 2017-08-11 20:46:59 +05:30
Michael Köhler
718f42897f Pontoon: Update German (de) localization of Test Pilot: Firefox Send
Localization authors:
- Michael Köhler <michael.koehler1@gmx.de>
2017-08-11 13:31:09 +00:00
John Gruen
fb468bd1bc fix japanese browse button 2017-08-11 13:41:14 +02:00
John Gruen
dafe00cabb add twitter and open graph cards 2017-08-11 13:39:04 +02:00
Matjaž Horvat
98aebb7f70 Pontoon: Update Slovenian (sl) localization of Test Pilot: Firefox Send
Localization authors:
- Matjaž Horvat <matjaz.horvat@gmail.com>
2017-08-11 11:31:10 +00:00
Tymur Faradzhev
a990d78bc0 Pontoon: Update Ukrainian (uk) localization of Test Pilot: Firefox Send
Localization authors:
- Tymur Faradzhev <faradzhev.timur@gmail.com>
2017-08-11 08:31:11 +00:00
Balázs Meskó
9b4069be3e Pontoon: Update Hungarian (hu) localization of Test Pilot: Firefox Send
Localization authors:
- Balázs Meskó <meskobalazs@gmail.com>
2017-08-11 08:31:08 +00:00
Håvar Henriksen
ff3bc0dd62 Pontoon: Update Norwegian Bokmål (nb-NO) localization of Test Pilot: Firefox Send
Localization authors:
- Håvar Henriksen <havar@firefox.no>
2017-08-11 06:50:58 +00:00
Luna Jernberg
b39b131928 Pontoon: Update Swedish (sv-SE) localization of Test Pilot: Firefox Send
Localization authors:
- Luna Jernberg <bittin@cafe8bitar.se>
2017-08-11 03:31:14 +00:00
Danny Coates
2646fb9b3c Merge pull request #503 from skystar-p/editorconfig
Added editorconfig
2017-08-10 19:50:46 -07:00
skystar-p
c2b84650e2 added editorconfig 2017-08-11 11:39:56 +09:00
YFdyh000
fecf938ae7 Pontoon: Update Chinese (zh-CN) localization of Test Pilot: Firefox Send
Localization authors:
- YFdyh000 <yfdyh000@gmail.com>
2017-08-11 02:31:45 +00:00
Cláudio Esperança
8abf631430 Pontoon: Update Portuguese (pt-PT) localization of Test Pilot: Firefox Send
Localization authors:
- Cláudio Esperança <cesperanc@gmail.com>
2017-08-11 02:10:43 +00:00
manxmensch
d69c535dda Pontoon: Update Malay (ms) localization of Test Pilot: Firefox Send
Localization authors:
- manxmensch <manxmensch@gmail.com>
2017-08-11 00:50:39 +00:00
Kohei Yoshino
082ca6c57b Pontoon: Update Japanese (ja) localization of Test Pilot: Firefox Send
Localization authors:
- Kohei Yoshino <kohei.yoshino@gmail.com>
2017-08-10 22:10:52 +00:00
Bjørn I
b263231068 Pontoon: Update Norwegian Nynorsk (nn-NO) localization of Test Pilot: Firefox Send
Localization authors:
- Bjørn I. <bjorn.svindseth@online.no>
2017-08-10 20:31:46 +00:00
Francesco Lodolo
947a6d9992 Pontoon: Update Italian (it) localization of Test Pilot: Firefox Send
Localization authors:
- Francesco Lodolo <francesco.lodolo@mozillaitalia.org>
2017-08-10 20:10:54 +00:00
Danny Coates
1ad7edf5a9 fixed bad merge 2017-08-10 12:59:07 -07:00
Schieck :)
0c26204ea1 Pontoon: Update Portuguese (pt-BR) localization of Test Pilot: Firefox Send
Localization authors:
- Schieck :) <ricardoschieck@gmail.com>
2017-08-10 18:51:14 +00:00
Ton
1e3bbee7f1 Pontoon: Update Dutch (nl) localization of Test Pilot: Firefox Send
Localization authors:
- Ton <tonnes.mb@gmail.com>
2017-08-10 18:11:25 +00:00
Danny Coates
ec80e8e622 Merge pull request #499 from mozilla/pier1
use import/export in the frontend code
2017-08-10 11:07:38 -07:00
Théo Chevalier
61e2c0d85b Pontoon: Update French (fr) localization of Test Pilot: Firefox Send
Localization authors:
- Théo Chevalier <theo.chevalier11@gmail.com>
2017-08-10 17:50:53 +00:00
Danny Coates
80db74fc3a Merge pull request #500 from mozilla/i495
fixed build:css on windows
2017-08-10 10:43:07 -07:00
Danny Coates
30936eb2fa fixed build:css on windows 2017-08-10 10:38:36 -07:00
Danny Coates
31e29d58b9 Merge pull request #481 from pdehaan/fix-l10n-id
Cater for mobile and desktop
2017-08-10 10:19:21 -07:00
Danny Coates
702134b3b1 use import/export in the frontend code 2017-08-10 10:03:22 -07:00
Danny Coates
11ae7f857c Merge pull request #493 from mozilla/webpack-dev
added webpack-dev-middleware
2017-08-10 09:29:43 -07:00
Peter deHaan
8827556974 Add new l10n string, but don't use it yet 2017-08-10 09:26:25 -07:00
Danny Coates
21b7f16b1e added webpack-dev-middleware for recompiling on source changes 2017-08-10 09:23:33 -07:00
Danny Coates
314ab237ec Merge pull request #491 from mozilla/i387
added missing exit event cases
2017-08-10 09:06:57 -07:00
Danny Coates
0fa0416c3f Merge pull request #492 from mozilla/no-cookies
make the site mostly work when cookies (localStorage) are disabled
2017-08-10 09:06:15 -07:00
Danny Coates
09faedf059 make the site mostly work when cookies (localStorage) are disabled 2017-08-09 23:12:15 -07:00
Danny Coates
16aa7983ed added missing exit event cases 2017-08-09 16:44:09 -07:00
Marcelo Poli
493bf8dc89 Pontoon: Update Spanish (es-AR) localization of Test Pilot: Firefox Send
Localization authors:
- Marcelo Poli <enzomatrix@gmail.com>
2017-08-09 22:10:59 +00:00
Danny Coates
46432b9649 Merge pull request #490 from mozilla/i489
set the mime type in the download blob
2017-08-09 14:31:55 -07:00
Danny Coates
193664a8e8 set the mime type in the download blob 2017-08-09 14:25:37 -07:00
Danny Coates
626e578acb Merge pull request #485 from mozilla/i479
added progress to tab title when not in focus
2017-08-09 11:42:35 -07:00
Tymur Faradzhev
51bffe11a8 Pontoon: Update Ukrainian (uk) localization of Test Pilot: Firefox Send
Localization authors:
- Tymur Faradzhev <faradzhev.timur@gmail.com>
2017-08-09 08:10:36 +00:00
Danny Coates
08e2c6c112 Merge pull request #474 from varghesethomase/master
Fixing bug #438 by adding role attribute to anchor tags and alt attribute images
2017-08-08 22:41:31 -07:00
Danny Coates
c38d91db98 added progress to tab title when not in focus 2017-08-08 20:23:38 -07:00
Danny Coates
c13839a522 Merge pull request #480 from pdehaan/issue-443
Increase font weight to 500 on <button>s and <label>s
2017-08-08 19:43:46 -07:00
Peter deHaan
4894d5162f Update l10n id 2017-08-08 13:37:33 -07:00
Peter deHaan
77b6fb138f Increase font weight to 500 on <button>s and <label>s 2017-08-08 13:06:59 -07:00
Danny Coates
9dab74891d Merge pull request #419 from pdehaan/autoprefixer
Add autoprefixer and cssnano support
2017-08-08 12:43:56 -07:00
Enol
393d2a0052 Pontoon: Update Asturian (ast) localization of Test Pilot: Firefox Send
Localization authors:
- Enol <enolp@softastur.org>
2017-08-08 19:31:58 +00:00
Enol
44ac783f6a Pontoon: Update Asturian (ast) localization of Test Pilot: Firefox Send
Localization authors:
- Enol <enolp@softastur.org>
2017-08-08 18:50:58 +00:00
Enol
7ea8712538 Pontoon: Update Asturian (ast) localization of Test Pilot: Firefox Send
Localization authors:
- Enol <enolp@softastur.org>
2017-08-08 18:30:54 +00:00
Jordi Serratosa
fb92a793e4 Pontoon: Update Catalan (ca) localization of Test Pilot: Firefox Send
Localization authors:
- Jordi Serratosa <jordis@softcatala.cat>
2017-08-08 12:50:40 +00:00
Luna Jernberg
87eaba6337 Pontoon: Update Swedish (sv-SE) localization of Test Pilot: Firefox Send
Localization authors:
- Luna Jernberg <bittin@cafe8bitar.se>
2017-08-08 11:31:17 +00:00
Selim Şumlu
7e13f2ab32 Pontoon: Update Turkish (tr) localization of Test Pilot: Firefox Send
Localization authors:
- Selim Şumlu <selim@sum.lu>
2017-08-08 11:11:10 +00:00
Slimane Amiri
3214d293ca Pontoon: Update Kabyle (kab) localization of Test Pilot: Firefox Send
Localization authors:
- Slimane Amiri <slimane.amiri@gmail.com>
2017-08-08 10:10:28 +00:00
Håvar Henriksen
1437116cf3 Pontoon: Update Norwegian Bokmål (nb-NO) localization of Test Pilot: Firefox Send
Localization authors:
- Håvar Henriksen <havar@firefox.no>
2017-08-08 07:31:23 +00:00
Varghese Thomas
740001ddde Fixing bug #438 by adding role attribute to anchor tags and alt attributes to images. Also solves #440 2017-08-08 08:05:02 +05:30
Peter deHaan
24af3207e9 Update browserslist 2017-08-07 16:38:19 -07:00
Peter deHaan
38746078ed Add autoprefixer and cssnano support 2017-08-07 16:19:40 -07:00
Jamie
fcea981127 Cater for mobile and desktop
Fixes #421
2017-08-06 16:03:54 +01:00
72 changed files with 6867 additions and 1376 deletions

View File

@@ -6,3 +6,7 @@ test
scripts
docs
firefox
public
views
webpack
frontend

14
.editorconfig Normal file
View File

@@ -0,0 +1,14 @@
root = true
[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
[*.{js,html,yml,json,handlebars}]
indent_style = space
indent_size = 2
[*.toml]
indent_style = space
indent_size = 4

6
.gitignore vendored
View File

@@ -1,10 +1,6 @@
.DS_Store
dist
node_modules
public/upload.js
public/download.js
public/version.json
public/l20n.min.js
public/polyfill.min.js
static/*
!static/info.txt
test/frontend/bundle.js

View File

@@ -1,6 +1,11 @@
extends: stylelint-config-standard
plugins:
- stylelint-no-unsupported-browser-features
rules:
plugin/no-unsupported-browser-features: [true, {severity: warning}]
color-hex-case: lower
declaration-colon-newline-after: null
selector-list-comma-newline-after: null

5
browserslist Normal file
View File

@@ -0,0 +1,5 @@
last 2 chrome versions
last 2 firefox versions
firefox esr
ie >= 9
safari >= 9

View File

@@ -16,7 +16,6 @@ deployment:
latest:
branch: master
commands:
- npm run build
- docker login -e $DOCKER_EMAIL -u $DOCKER_USER -p $DOCKER_PASS
- docker build -t mozilla/send:latest .
- docker push mozilla/send:latest
@@ -24,14 +23,13 @@ deployment:
tag: /.*/
owner: mozilla
commands:
- npm run build
- docker login -e $DOCKER_EMAIL -u $DOCKER_USER -p $DOCKER_PASS
- docker build -t mozilla/send:$CIRCLE_TAG .
- docker push mozilla/send:$CIRCLE_TAG
test:
override:
- npm run build:version
- npm run build
- npm run lint
- npm test
- nsp check

View File

@@ -8,5 +8,6 @@ services:
- "1443:1443"
environment:
- REDIS_HOST=redis
- NODE_ENV=production
redis:
image: redis:alpine

View File

@@ -1,3 +1,9 @@
env:
browser: true
jquery: true
node: false
parserOptions:
sourceType: module
rules:
node/no-unsupported-features: off

View File

@@ -1,5 +1,5 @@
const Raven = require('raven-js');
const { unsupported } = require('./metrics');
import Raven from 'raven-js';
import { unsupported } from './metrics';
if (navigator.doNotTrack !== '1' && window.RAVEN_CONFIG) {
Raven.config(window.SENTRY_ID, window.RAVEN_CONFIG).install();
@@ -17,6 +17,4 @@ if (
});
}
module.exports = {
Raven
};
export { Raven };

View File

@@ -1,30 +1,33 @@
const { Raven } = require('./common');
const FileReceiver = require('./fileReceiver');
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');
import { Raven } from './common';
import FileReceiver from './fileReceiver';
import { bytes, notify, gcmCompliant } from './utils';
import Storage from './storage';
import * as links from './links';
import * as metrics from './metrics';
import * as progress from './progress';
const storage = new Storage();
function onUnload(size) {
metrics.cancelledDownload({ size });
}
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'));
async function download() {
const downloadBtn = document.getElementById('download-btn');
const downloadPanel = document.getElementById('download-page-one');
const progressPanel = document.getElementById('download-progress');
const file = document.getElementById('dl-file');
const size = Number(file.getAttribute('data-size'));
const ttl = Number(file.getAttribute('data-ttl'));
const unloadHandler = onUnload.bind(null, size);
const startTime = Date.now();
const fileReceiver = new FileReceiver();
const fileReceiver = new FileReceiver(
'/assets' + location.pathname.slice(0, -1),
location.hash.slice(1)
);
$downloadBtn.attr('disabled', 'disabled');
$('#download-page-one').attr('hidden', true);
$('#download-progress').removeAttr('hidden');
downloadBtn.disabled = true;
downloadPanel.hidden = true;
progressPanel.hidden = false;
metrics.startedDownload({ size, ttl });
links.setOpenInNewTab(true);
window.addEventListener('unload', unloadHandler);
@@ -41,74 +44,72 @@ function download() {
document.l10n.formatValue('decryptingFile').then(progress.setText);
});
fileReceiver
.download()
.catch(err => {
metrics.stoppedDownload({ size, err });
try {
const file = await fileReceiver.download();
const endTime = Date.now();
const time = endTime - startTime;
const downloadTime = endTime - downloadEnd;
const speed = size / (downloadTime / 1000);
if (err.message === 'notfound') {
location.reload();
} else {
document.l10n.formatValue('errorPageHeader').then(translated => {
$title.text(translated);
});
$downloadBtn.attr('hidden', true);
$('#expired-img').removeAttr('hidden');
}
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]);
});
links.setOpenInNewTab(false);
storage.totalDownloads += 1;
metrics.completedDownload({ size, time, speed });
progress.setText(' ');
document.l10n
.formatValues('downloadNotification', 'downloadFinish')
.then(translated => {
notify(translated[0]);
document.getElementById('dl-title').textContent = translated[1];
document.querySelector('#download-progress .description').textContent =
' ';
});
const dataView = new DataView(file.plaintext);
const blob = new Blob([dataView], { type: file.type });
const downloadUrl = URL.createObjectURL(blob);
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) {
window.navigator.msSaveBlob(blob, file.name);
return;
}
a.download = file.name;
document.body.appendChild(a);
a.click();
URL.revokeObjectURL(downloadUrl);
} catch (err) {
metrics.stoppedDownload({ size, err });
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));
if (err.message === 'notfound') {
location.reload();
} else {
progressPanel.hidden = true;
downloadPanel.hidden = true;
document.getElementById('upload-error').hidden = false;
}
Raven.captureException(err);
}
}
$(() => {
const $file = $('#dl-file');
const filename = $file.attr('data-filename');
const b = Number($file.attr('data-size'));
document.addEventListener('DOMContentLoaded', function() {
const file = document.getElementById('dl-file');
const filename = file.getAttribute('data-filename');
const b = Number(file.getAttribute('data-size'));
const size = bytes(b);
document.l10n
.formatValue('downloadFileSize', { size })
.then(str => $('#dl-filesize').text(str));
document.l10n.formatValue('downloadFileSize', { size }).then(str => {
document.getElementById('dl-filesize').textContent = str;
});
document.l10n
.formatValue('downloadingPageProgress', { filename, size })
.then(str => $('#dl-title').text(str));
.then(str => {
document.getElementById('dl-title').textContent = str;
});
gcmCompliant()
.then(() => {
$('#download-btn').on('click', download);
document
.getElementById('download-btn')
.addEventListener('click', download);
})
.catch(err => {
metrics.unsupported({ err }).then(() => {

163
frontend/src/fileList.js Normal file
View File

@@ -0,0 +1,163 @@
import FileSender from './fileSender';
import Storage from './storage';
import * as metrics from './metrics';
import { allowedCopy, copyToClipboard, ONE_DAY_IN_MS } from './utils';
import bel from 'bel';
import copyImg from '../../public/resources/copy-16.svg';
import closeImg from '../../public/resources/close-16.svg';
const HOUR = 1000 * 60 * 60;
const storage = new Storage();
let fileList = null;
document.addEventListener('DOMContentLoaded', function() {
fileList = document.getElementById('file-list');
toggleHeader();
Promise.all(
storage.files.map(file => {
const id = file.fileId;
return checkExistence(id).then(exists => {
if (exists) {
addFile(storage.getFileById(id));
} else {
storage.remove(id);
}
});
})
)
.catch(err => console.error(err))
.then(toggleHeader);
});
function toggleHeader() {
fileList.hidden = storage.files.length === 0;
}
function timeLeft(milliseconds) {
const minutes = Math.floor(milliseconds / 1000 / 60);
const hours = Math.floor(minutes / 60);
const seconds = Math.floor(milliseconds / 1000 % 60);
if (hours >= 1) {
return `${hours}h ${minutes % 60}m`;
} else if (hours === 0) {
return `${minutes}m ${seconds}s`;
}
return 'Expired';
}
function addFile(file) {
if (!file) {
return;
}
file.creationDate = new Date(file.creationDate);
const url = `${file.url}#${file.secretKey}`;
const future = new Date();
future.setTime(file.creationDate.getTime() + file.expiry);
const countdown = future.getTime() - Date.now();
const row = bel`
<tr>
<td>${file.name}</td>
<td>
<span class="icon-docs" data-l10n-id="copyUrlHover"></span>
<img onclick=${copyClick} src="${copyImg}" class="icon-copy" data-l10n-id="copyUrlHover">
<span data-l10n-id="copiedUrl" class="text-copied" hidden="true"></span>
</td>
<td>${timeLeft(countdown)}</td>
<td>
<span class="icon-cancel-1" data-l10n-id="deleteButtonHover" title="Delete"></span>
<img onclick=${showPopup} src="${closeImg}" class="icon-delete" data-l10n-id="deleteButtonHover" title="Delete">
<div class="popup">
<div class="popuptext" onclick=${stopProp} onblur=${cancel} tabindex="-1">
<div class="popup-message" data-l10n-id="deletePopupText"></div>
<div class="popup-action">
<span class="popup-no" onclick=${cancel} data-l10n-id="deletePopupCancel"></span>
<span class="popup-yes" onclick=${deleteFile} data-l10n-id="deletePopupYes"></span>
</div>
</div>
</div>
</td>
</tr>
`;
const popup = row.querySelector('.popuptext');
const timeCol = row.querySelectorAll('td')[2];
if (!allowedCopy()) {
row.querySelector('.icon-copy').disabled = true;
}
fileList.querySelector('tbody').appendChild(row);
toggleHeader();
poll();
function copyClick(e) {
metrics.copiedLink({ location: 'upload-list' });
copyToClipboard(url);
const icon = e.target;
const text = e.target.nextSibling;
icon.hidden = true;
text.hidden = false;
setTimeout(() => {
icon.hidden = false;
text.hidden = true;
}, 500);
}
function poll() {
const countdown = future.getTime() - Date.now();
if (countdown <= 0) {
storage.remove(file.fileId);
row.parentNode.removeChild(row);
toggleHeader();
}
timeCol.textContent = timeLeft(countdown);
setTimeout(poll, countdown >= HOUR ? 60000 : 1000);
}
function deleteFile() {
FileSender.delete(file.fileId, file.deleteToken);
const ttl = ONE_DAY_IN_MS - (Date.now() - file.creationDate.getTime());
metrics.deletedUpload({
size: file.size,
time: file.totalTime,
speed: file.uploadSpeed,
type: file.typeOfUpload,
location: 'upload-list',
ttl
});
row.parentNode.removeChild(row);
storage.remove(file.fileId);
toggleHeader();
}
function showPopup() {
popup.classList.add('show');
popup.focus();
}
function cancel(e) {
e.stopPropagation();
popup.classList.remove('show');
}
function stopProp(e) {
e.stopPropagation();
}
}
async function checkExistence(id) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => {
if (xhr.readyState === XMLHttpRequest.HEADERS_RECEIVED) {
resolve(xhr.status === 200);
}
};
xhr.onerror = reject;
xhr.ontimeout = reject;
xhr.open('get', '/exists/' + id);
xhr.timeout = 2000;
xhr.send();
});
}
export { addFile };

View File

@@ -1,85 +1,80 @@
const EventEmitter = require('events');
const { hexToArray } = require('./utils');
import EventEmitter from 'events';
import { hexToArray } from './utils';
class FileReceiver extends EventEmitter {
constructor() {
export default class FileReceiver extends EventEmitter {
constructor(url, k) {
super();
this.key = window.crypto.subtle.importKey(
'jwk',
{
k,
kty: 'oct',
alg: 'A128GCM',
ext: true
},
{
name: 'AES-GCM'
},
false,
['decrypt']
);
this.url = url;
}
download() {
return window.crypto.subtle
.importKey(
'jwk',
{
kty: 'oct',
k: location.hash.slice(1),
alg: 'A128GCM',
ext: true
},
{
name: 'AES-GCM'
},
true,
['encrypt', 'decrypt']
)
.then(key => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
downloadFile() {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.onprogress = event => {
if (event.lengthComputable && event.target.status !== 404) {
this.emit('progress', [event.loaded, event.total]);
}
};
xhr.onprogress = event => {
if (event.lengthComputable && event.target.status !== 404) {
this.emit('progress', [event.loaded, event.total]);
}
};
xhr.onload = function(event) {
if (xhr.status === 404) {
reject(new Error('notfound'));
return;
}
xhr.onload = function(event) {
if (xhr.status === 404) {
reject(new Error('notfound'));
return;
}
const blob = new Blob([this.response]);
const fileReader = new FileReader();
fileReader.onload = function() {
const meta = JSON.parse(xhr.getResponseHeader('X-File-Metadata'));
resolve([
{
data: this.result,
filename: meta.filename,
iv: meta.id
},
key
]);
};
const blob = new Blob([this.response]);
const meta = JSON.parse(xhr.getResponseHeader('X-File-Metadata'));
const fileReader = new FileReader();
fileReader.onload = function() {
resolve({
data: this.result,
name: meta.filename,
type: meta.mimeType,
iv: meta.id
});
};
fileReader.readAsArrayBuffer(blob);
};
fileReader.readAsArrayBuffer(blob);
};
xhr.open('get', '/assets' + location.pathname.slice(0, -1), true);
xhr.responseType = 'blob';
xhr.send();
});
})
.then(([fdata, key]) => {
this.emit('decrypting');
return Promise.all([
window.crypto.subtle
.decrypt(
{
name: 'AES-GCM',
iv: hexToArray(fdata.iv),
tagLength: 128
},
key,
fdata.data
)
.then(decrypted => {
return Promise.resolve(decrypted);
}),
decodeURIComponent(fdata.filename)
]);
});
xhr.open('get', this.url);
xhr.responseType = 'blob';
xhr.send();
});
}
async download() {
const key = await this.key;
const file = await this.downloadFile();
this.emit('decrypting');
const plaintext = await window.crypto.subtle.decrypt(
{
name: 'AES-GCM',
iv: hexToArray(file.iv),
tagLength: 128
},
key,
file.data
);
return {
plaintext,
name: decodeURIComponent(file.name),
type: file.type
};
}
}
module.exports = FileReceiver;

View File

@@ -1,12 +1,20 @@
const EventEmitter = require('events');
const { arrayToHex } = require('./utils');
import EventEmitter from 'events';
import { arrayToHex } from './utils';
class FileSender extends EventEmitter {
export default class FileSender extends EventEmitter {
constructor(file) {
super();
this.file = file;
this.iv = window.crypto.getRandomValues(new Uint8Array(12));
this.uploadXHR = new XMLHttpRequest();
this.key = window.crypto.subtle.generateKey(
{
name: 'AES-GCM',
length: 128
},
true,
['encrypt']
);
}
static delete(fileId, token) {
@@ -32,89 +40,79 @@ class FileSender extends EventEmitter {
this.uploadXHR.abort();
}
upload() {
const self = this;
self.emit('loading');
return Promise.all([
window.crypto.subtle.generateKey(
{
name: 'AES-GCM',
length: 128
},
true,
['encrypt', 'decrypt']
),
new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsArrayBuffer(this.file);
reader.onload = function(event) {
const plaintext = new Uint8Array(this.result);
resolve(plaintext);
};
reader.onerror = function(err) {
reject(err);
};
})
])
.then(([secretKey, plaintext]) => {
self.emit('encrypting');
return Promise.all([
window.crypto.subtle.encrypt(
{
name: 'AES-GCM',
iv: this.iv,
tagLength: 128
},
secretKey,
plaintext
),
window.crypto.subtle.exportKey('jwk', secretKey)
]);
})
.then(([encrypted, keydata]) => {
return new Promise((resolve, reject) => {
const file = this.file;
const fileId = arrayToHex(this.iv);
const dataView = new DataView(encrypted);
const blob = new Blob([dataView], { type: file.type });
const fd = new FormData();
fd.append('data', blob, file.name);
readFile() {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsArrayBuffer(this.file);
reader.onload = function(event) {
const plaintext = new Uint8Array(this.result);
resolve(plaintext);
};
reader.onerror = function(err) {
reject(err);
};
});
}
const xhr = self.uploadXHR;
uploadFile(encrypted, keydata) {
return new Promise((resolve, reject) => {
const file = this.file;
const fileId = arrayToHex(this.iv);
const dataView = new DataView(encrypted);
const blob = new Blob([dataView], { type: file.type });
const fd = new FormData();
fd.append('data', blob, file.name);
xhr.upload.addEventListener('progress', e => {
if (e.lengthComputable) {
self.emit('progress', [e.loaded, e.total]);
}
});
const xhr = this.uploadXHR;
xhr.onreadystatechange = () => {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status === 200) {
const responseObj = JSON.parse(xhr.responseText);
return resolve({
url: responseObj.url,
fileId: responseObj.id,
secretKey: keydata.k,
deleteToken: responseObj.delete
});
}
reject(xhr.status);
}
};
xhr.open('post', '/upload', true);
xhr.setRequestHeader(
'X-File-Metadata',
JSON.stringify({
id: fileId,
filename: encodeURIComponent(file.name)
})
);
xhr.send(fd);
});
xhr.upload.addEventListener('progress', e => {
if (e.lengthComputable) {
this.emit('progress', [e.loaded, e.total]);
}
});
xhr.onreadystatechange = () => {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status === 200) {
const responseObj = JSON.parse(xhr.responseText);
return resolve({
url: responseObj.url,
fileId: responseObj.id,
secretKey: keydata.k,
deleteToken: responseObj.delete
});
}
reject(xhr.status);
}
};
xhr.open('post', '/upload', true);
xhr.setRequestHeader(
'X-File-Metadata',
JSON.stringify({
id: fileId,
filename: encodeURIComponent(file.name)
})
);
xhr.send(fd);
});
}
async upload() {
this.emit('loading');
const key = await this.key;
const plaintext = await this.readFile();
this.emit('encrypting');
const encrypted = await window.crypto.subtle.encrypt(
{
name: 'AES-GCM',
iv: this.iv,
tagLength: 128
},
key,
plaintext
);
const keydata = await window.crypto.subtle.exportKey('jwk', key);
return this.uploadFile(encrypted, keydata);
}
}
module.exports = FileSender;

View File

@@ -18,6 +18,4 @@ function setOpenInNewTab(bool) {
}
}
module.exports = {
setOpenInNewTab
};
export { setOpenInNewTab };

View File

@@ -1,6 +1,6 @@
/*** index.html ***/
html {
background: url('resources/send_bg.svg');
background: url('../../public/resources/send_bg.svg');
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'segoe ui',
'helvetica neue', helvetica, ubuntu, roboto, noto, arial, sans-serif;
font-weight: 200;
@@ -22,6 +22,16 @@ body {
position: relative;
}
#progress circle {
stroke: #eee;
stroke-width: 0.75em;
}
#progress #bar {
transition: stroke-dashoffset 300ms linear;
stroke: #3b9dff;
}
.header {
align-items: flex-start;
box-sizing: border-box;
@@ -79,7 +89,7 @@ body {
.feedback {
background-color: #0297f8;
background-image: url('resources/feedback.svg');
background-image: url('../../public/resources/feedback.svg');
background-position: 2px 4px;
background-repeat: no-repeat;
background-size: 18px;
@@ -138,6 +148,10 @@ a {
text-decoration: none;
}
.btn {
font-weight: 500;
}
/** page-one **/
.title {
@@ -208,12 +222,13 @@ a {
border-radius: 5px;
font-size: 15px;
color: #fff;
width: 240px;
min-width: 240px;
height: 44px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
padding: 0 10px;
}
#browse:hover {
@@ -295,6 +310,10 @@ tbody {
opacity: 0.3;
}
.text-copied {
color: #0a8dff;
}
/* Popup container */
.popup {
position: absolute;
@@ -415,7 +434,7 @@ tbody {
position: absolute;
letter-spacing: -0.78px;
font-family: 'Segoe UI', 'SF Pro Text', sans-serif;
top: 53px;
top: 58px;
left: 50%;
transform: translateX(-50%);
-moz-user-select: none;
@@ -644,7 +663,6 @@ tbody {
background: #0297f8;
border: 1px solid #0297f8;
border-radius: 5px;
font-weight: 300;
cursor: pointer;
}

View File

@@ -1,6 +1,13 @@
const testPilotGA = require('testpilot-ga/src/TestPilotGA');
const Storage = require('./storage');
const storage = new Storage(localStorage);
import testPilotGA from 'testpilot-ga/src/TestPilotGA';
import Storage from './storage';
const storage = new Storage();
let hasLocalStorage = false;
try {
hasLocalStorage = !!localStorage;
} catch (e) {
// don't care
}
const analytics = new testPilotGA({
an: 'Firefox Send',
@@ -18,7 +25,10 @@ document.addEventListener('DOMContentLoaded', function() {
});
function sendEvent() {
return analytics.sendEvent.apply(analytics, arguments).catch(() => 0);
return (
hasLocalStorage &&
analytics.sendEvent.apply(analytics, arguments).catch(() => 0)
);
}
function urlToMetric(url) {
@@ -41,6 +51,11 @@ function urlToMetric(url) {
return 'twitter';
case 'https://www.mozilla.org/firefox/new/?scene=2':
return 'download-firefox';
case 'https://qsurvey.mozilla.com/s3/txp-firefox-send':
return 'survey';
case 'https://testpilot.firefox.com/':
case 'https://testpilot.firefox.com/experiments/send':
return 'testpilot';
default:
return 'other';
}
@@ -198,7 +213,7 @@ function exitEvent(target) {
function addExitHandlers() {
const links = Array.from(document.querySelectorAll('a'));
links.forEach(l => {
if (/^http/.test(l.href)) {
if (/^http/.test(l.getAttribute('href'))) {
l.addEventListener('click', exitEvent);
}
});
@@ -219,7 +234,7 @@ function addRestartHandlers() {
});
}
module.exports = {
export {
copiedLink,
startedUpload,
cancelledUpload,

View File

@@ -1,28 +1,37 @@
const { bytes } = require('./utils');
const $ = require('jquery');
require('jquery-circle-progress');
import { bytes, percent } from './utils';
let $progress = null;
let $percent = null;
let $text = null;
let percentText = null;
let text = null;
let title = null;
let bar = null;
let updateTitle = false;
const radius = 73;
const circumference = 2 * Math.PI * radius;
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 }
});
percentText = document.querySelector('.percent-number');
text = document.querySelector('.progress-text');
bar = document.getElementById('bar');
title = document.querySelector('title');
});
document.addEventListener('blur', function() {
updateTitle = true;
});
document.addEventListener('focus', function() {
updateTitle = false;
return title && (title.textContent = 'Firefox Send');
});
function setProgress(params) {
const percent = params.complete / params.total;
$progress.circleProgress('value', percent);
$percent.text(`${Math.floor(percent * 100)}`);
const ratio = params.complete / params.total;
bar.setAttribute('stroke-dashoffset', (1 - ratio) * circumference);
percentText.textContent = Math.floor(ratio * 100);
if (updateTitle) {
title.textContent = percent(ratio);
}
document.l10n
.formatValue('fileSizeProgress', {
partialSize: bytes(params.complete),
@@ -32,10 +41,7 @@ function setProgress(params) {
}
function setText(str) {
$text.text(str);
text.textContent = str;
}
module.exports = {
setProgress,
setText
};
export { setProgress, setText };

View File

@@ -1,8 +1,38 @@
const { isFile } = require('./utils');
import { isFile } from './utils';
class Storage {
constructor(engine) {
this.engine = engine;
class Mem {
constructor() {
this.items = new Map();
}
get length() {
return this.items.size;
}
getItem(key) {
return this.items.get(key);
}
setItem(key, value) {
return this.items.set(key, value);
}
removeItem(key) {
return this.items.delete(key);
}
key(i) {
return this.items.keys()[i];
}
}
export default class Storage {
constructor() {
try {
this.engine = localStorage || new Mem();
} catch (e) {
this.engine = new Mem();
}
}
get totalDownloads() {
@@ -56,11 +86,11 @@ class Storage {
}
getFileById(id) {
return this.engine.getItem(id);
}
has(property) {
return this.engine.hasOwnProperty(property);
try {
return JSON.parse(this.engine.getItem(id));
} catch (e) {
return null;
}
}
remove(property) {
@@ -71,5 +101,3 @@ class Storage {
this.engine.setItem(id, JSON.stringify(file));
}
}
module.exports = Storage;

View File

@@ -1,472 +1,253 @@
/* global MAXFILESIZE EXPIRE_SECONDS */
const { Raven } = require('./common');
const FileSender = require('./fileSender');
const {
import { Raven } from './common';
import FileSender from './fileSender';
import {
allowedCopy,
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');
} from './utils';
import Storage from './storage';
import * as metrics from './metrics';
import * as progress from './progress';
import * as fileList from './fileList';
import checkImg from '../../public/resources/check-16.svg';
const $ = require('jquery');
const storage = new Storage();
const allowedCopy = () => {
const support = !!document.queryCommandSupported;
return support ? document.queryCommandSupported('copy') : false;
};
async function upload(event) {
event.preventDefault();
const pageOne = document.getElementById('page-one');
const link = document.getElementById('link');
const uploadWindow = document.querySelector('.upload-window');
const uploadError = document.getElementById('upload-error');
const uploadProgress = document.getElementById('upload-progress');
const clickOrDrop = event.type === 'drop' ? 'drop' : 'click';
$(() => {
// don't allow upload if not on upload page
if (pageOne.hidden) {
return;
}
storage.totalUploads += 1;
let file = '';
if (clickOrDrop === 'drop') {
if (!event.dataTransfer.files[0]) {
uploadWindow.classList.remove('ondrag');
return;
}
if (
event.dataTransfer.files.length > 1 ||
event.dataTransfer.files[0].size === 0
) {
uploadWindow.classList.remove('ondrag');
document.l10n.formatValue('uploadPageMultipleFilesAlert').then(str => {
alert(str);
});
return;
}
file = event.dataTransfer.files[0];
} else {
file = event.target.files[0];
}
if (file.size > MAXFILESIZE) {
return document.l10n
.formatValue('fileTooBig', { size: bytes(MAXFILESIZE) })
.then(alert);
}
pageOne.hidden = true;
uploadError.hidden = true;
uploadProgress.hidden = false;
document.l10n
.formatValue('uploadingPageProgress', {
size: bytes(file.size),
filename: file.name
})
.then(str => {
document.getElementById('upload-filename').textContent = str;
});
document.l10n.formatValue('importingFile').then(progress.setText);
//don't allow drag and drop when not on page-one
document.body.removeEventListener('drop', upload);
const fileSender = new FileSender(file);
document.getElementById('cancel-upload').addEventListener('click', () => {
fileSender.cancel();
metrics.cancelledUpload({
size: file.size,
type: clickOrDrop
});
location.reload();
});
let uploadStart;
fileSender.on('progress', data => {
uploadStart = uploadStart || Date.now();
progress.setProgress({
complete: data[0],
total: data[1]
});
});
fileSender.on('encrypting', () => {
document.l10n.formatValue('encryptingFile').then(progress.setText);
});
let t;
const startTime = Date.now();
metrics.startedUpload({
size: file.size,
type: clickOrDrop
});
// For large files we need to give the ui a tick to breathe and update
// before we kick off the FileSender
setTimeout(() => {
fileSender
.upload()
.then(info => {
const endTime = Date.now();
const time = endTime - startTime;
const uploadTime = endTime - uploadStart;
const speed = file.size / (uploadTime / 1000);
const expiration = EXPIRE_SECONDS * 1000;
link.setAttribute('value', `${info.url}#${info.secretKey}`);
const copyText = document.getElementById('copy-text');
copyText.setAttribute(
'data-l10n-args',
JSON.stringify({ filename: file.name })
);
copyText.setAttribute('data-l10n-id', 'copyUrlFormLabelWithName');
metrics.completedUpload({
size: file.size,
time,
speed,
type: clickOrDrop
});
const fileData = {
name: file.name,
size: file.size,
fileId: info.fileId,
url: info.url,
secretKey: info.secretKey,
deleteToken: info.deleteToken,
creationDate: new Date(),
expiry: expiration,
totalTime: time,
typeOfUpload: clickOrDrop,
uploadSpeed: speed
};
document.getElementById('delete-file').addEventListener('click', () => {
FileSender.delete(fileData.fileId, fileData.deleteToken).then(() => {
const ttl =
ONE_DAY_IN_MS - (Date.now() - fileData.creationDate.getTime());
metrics
.deletedUpload({
size: fileData.size,
time: fileData.totalTime,
speed: fileData.uploadSpeed,
type: fileData.typeOfUpload,
location: 'success-screen',
ttl
})
.then(() => {
storage.remove(fileData.fileId);
location.reload();
});
});
});
storage.addFile(info.fileId, fileData);
pageOne.hidden = true;
uploadProgress.hidden = true;
uploadError.hidden = true;
document.getElementById('share-link').hidden = false;
fileList.addFile(fileData);
document.l10n.formatValue('notifyUploadDone').then(str => {
notify(str);
});
})
.catch(err => {
// err is 0 when coming from a cancel upload event
if (err === 0) {
return;
}
// only show error page when the error is anything other than user cancelling the upload
Raven.captureException(err);
pageOne.hidden = true;
uploadProgress.hidden = true;
uploadError.hidden = false;
window.clearTimeout(t);
metrics.stoppedUpload({
size: file.size,
type: clickOrDrop,
err
});
});
}, 10);
}
document.addEventListener('DOMContentLoaded', function() {
gcmCompliant()
.then(function() {
const $pageOne = $('#page-one');
const $copyBtn = $('#copy-btn');
const $link = $('#link');
const $uploadWindow = $('.upload-window');
const $uploadError = $('#upload-error');
const $uploadProgress = $('#upload-progress');
const $fileList = $('#file-list');
const pageOne = document.getElementById('page-one');
const copyBtn = document.getElementById('copy-btn');
const link = document.getElementById('link');
const uploadWindow = document.querySelector('.upload-window');
$pageOne.removeAttr('hidden');
$('#file-upload').on('change', onUpload);
pageOne.hidden = false;
document.getElementById('file-upload').addEventListener('change', upload);
$(document.body).on('dragover', allowDrop).on('drop', onUpload);
document.body.addEventListener('dragover', allowDrop);
document.body.addEventListener('drop', upload);
// reset copy button
$copyBtn.attr({
disabled: !allowedCopy(),
'data-l10n-id': 'copyUrlFormButton'
});
copyBtn.disabled = !allowedCopy();
copyBtn.setAttribute('data-l10n-id', 'copyUrlFormButton');
$link.attr('disabled', false);
const toggleHeader = () => {
//hide table header if empty list
if (document.querySelector('tbody').childNodes.length === 1) {
$fileList.attr('hidden', true);
} else {
$fileList.removeAttr('hidden');
}
};
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);
}
}
link.disabled = false;
// copy link to clipboard
$copyBtn.on('click', () => {
if (allowedCopy() && copyToClipboard($link.attr('value'))) {
copyBtn.addEventListener('click', () => {
if (allowedCopy() && copyToClipboard(link.getAttribute('value'))) {
metrics.copiedLink({ location: 'success-screen' });
//disable button for 3s
$copyBtn.attr('disabled', true);
$link.attr('disabled', true);
$copyBtn.html(
'<img src="/resources/check-16.svg" class="icon-check"></img>'
);
copyBtn.disabled = true;
link.disabled = true;
copyBtn.innerHTML = `<img src="${checkImg}" class="icon-check"></img>`;
setTimeout(() => {
$copyBtn.attr({
disabled: false,
'data-l10n-id': 'copyUrlFormButton'
});
$link.attr('disabled', false);
copyBtn.disabled = !allowedCopy();
copyBtn.setAttribute('data-l10n-id', 'copyUrlFormButton');
link.disabled = false;
}, 3000);
}
});
$uploadWindow
.on('dragover', () => {
$uploadWindow.addClass('ondrag');
})
.on('dragleave', () => {
$uploadWindow.removeClass('ondrag');
});
uploadWindow.addEventListener('dragover', () =>
uploadWindow.classList.add('ondrag')
);
uploadWindow.addEventListener('dragleave', () =>
uploadWindow.classList.remove('ondrag')
);
// on file upload by browse or drag & drop
function onUpload(event) {
event.preventDefault();
const clickOrDrop = event.type === 'drop' ? 'drop' : 'click';
// don't allow upload if not on upload page
if ($pageOne.attr('hidden')) {
return;
}
storage.totalUploads += 1;
let file = '';
if (clickOrDrop === 'drop') {
if (!event.originalEvent.dataTransfer.files[0]) {
$uploadWindow.removeClass('ondrag');
return;
}
if (
event.originalEvent.dataTransfer.files.length > 1 ||
event.originalEvent.dataTransfer.files[0].size === 0
) {
$uploadWindow.removeClass('ondrag');
document.l10n
.formatValue('uploadPageMultipleFilesAlert')
.then(str => {
alert(str);
});
return;
}
file = event.originalEvent.dataTransfer.files[0];
} else {
file = event.target.files[0];
}
if (file.size > MAXFILESIZE) {
return document.l10n
.formatValue('fileTooBig', { size: bytes(MAXFILESIZE) })
.then(alert);
}
$pageOne.attr('hidden', true);
$uploadError.attr('hidden', true);
$uploadProgress.removeAttr('hidden');
document.l10n
.formatValue('uploadingPageProgress', {
size: bytes(file.size),
filename: file.name
})
.then(str => {
$('#upload-filename').text(str);
});
document.l10n.formatValue('importingFile').then(progress.setText);
//don't allow drag and drop when not on page-one
$(document.body).off('drop', onUpload);
const fileSender = new FileSender(file);
$('#cancel-upload').on('click', () => {
fileSender.cancel();
metrics.cancelledUpload({
size: file.size,
type: clickOrDrop
});
location.reload();
});
let uploadStart;
fileSender.on('progress', data => {
uploadStart = uploadStart || Date.now();
progress.setProgress({
complete: data[0],
total: data[1]
});
});
fileSender.on('encrypting', () => {
document.l10n.formatValue('encryptingFile').then(progress.setText);
});
let t;
const startTime = Date.now();
metrics.startedUpload({
size: file.size,
type: clickOrDrop
});
// For large files we need to give the ui a tick to breathe and update
// before we kick off the FileSender
setTimeout(() => {
fileSender
.upload()
.then(info => {
const endTime = Date.now();
const time = endTime - startTime;
const uploadTime = endTime - uploadStart;
const speed = file.size / (uploadTime / 1000);
const expiration = EXPIRE_SECONDS * 1000;
metrics.completedUpload({
size: file.size,
time,
speed,
type: clickOrDrop
});
const fileData = {
name: file.name,
size: file.size,
fileId: info.fileId,
url: info.url,
secretKey: info.secretKey,
deleteToken: info.deleteToken,
creationDate: new Date(),
expiry: expiration,
totalTime: time,
typeOfUpload: clickOrDrop,
uploadSpeed: speed
};
$('#delete-file').on('click', () => {
FileSender.delete(
fileData.fileId,
fileData.deleteToken
).then(() => {
const ttl =
ONE_DAY_IN_MS -
(Date.now() - fileData.creationDate.getTime());
metrics
.deletedUpload({
size: fileData.size,
time: fileData.totalTime,
speed: fileData.uploadSpeed,
type: fileData.typeOfUpload,
location: 'success-screen',
ttl
})
.then(() => {
storage.remove(fileData.fileId);
location.reload();
});
});
});
storage.addFile(info.fileId, fileData);
$pageOne.attr('hidden', true);
$uploadProgress.attr('hidden', true);
$uploadError.attr('hidden', true);
$('#share-link').removeAttr('hidden');
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(() => {

View File

@@ -123,10 +123,23 @@ function bytes(num) {
return `${nStr}${UNITS[exponent]}`;
}
function percent(ratio) {
return LOCALIZE_NUMBERS
? ratio.toLocaleString(navigator.languages, { style: 'percent' })
: `${Math.floor(ratio * 100)}%`;
}
function allowedCopy() {
const support = !!document.queryCommandSupported;
return support ? document.queryCommandSupported('copy') : false;
}
const ONE_DAY_IN_MS = 86400000;
module.exports = {
export {
allowedCopy,
bytes,
percent,
copyToClipboard,
arrayToHex,
hexToArray,

6100
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,10 @@
{
"name": "firefox-send",
"description": "File Sharing Experiment",
"version": "1.1.0",
"version": "1.1.2",
"author": "Mozilla (https://mozilla.org)",
"dependencies": {
"aws-sdk": "^2.89.0",
"aws-sdk": "^2.98.0",
"body-parser": "^1.17.2",
"connect-busboy": "0.0.2",
"convict": "^3.0.0",
@@ -13,38 +13,54 @@
"helmet": "^3.8.0",
"mozlog": "^2.1.1",
"raven": "^2.1.0",
"redis": "^2.7.1"
"redis": "^2.8.0"
},
"devDependencies": {
"asmcrypto.js": "0.0.11",
"autoprefixer": "^7.1.2",
"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",
"bel": "^5.0.3",
"browserify": "^14.4.0",
"copy-webpack-plugin": "^4.0.1",
"cross-env": "^5.0.5",
"css-loader": "^0.28.4",
"css-mqpacker": "^6.0.1",
"cssnano": "^3.10.0",
"eslint": "^4.3.0",
"eslint-plugin-mocha": "^4.11.0",
"eslint-plugin-node": "^5.1.1",
"eslint-plugin-security": "^1.4.0",
"extract-loader": "^1.0.0",
"file-loader": "^0.11.2",
"git-rev-sync": "^1.9.1",
"jquery": "^3.2.1",
"jquery-circle-progress": "^1.2.2",
"html-loader": "^0.5.1",
"html-webpack-plugin": "^2.30.1",
"husky": "^0.14.3",
"l20n": "^5.0.0",
"lint-staged": "^4.0.3",
"mkdirp": "^0.5.1",
"mocha": "^3.4.2",
"npm-run-all": "^4.0.2",
"postcss-cli": "^4.1.0",
"postcss-loader": "^2.0.6",
"prettier": "^1.5.3",
"proxyquire": "^1.8.0",
"raven-js": "^3.17.0",
"rimraf": "^2.6.1",
"selenium-webdriver": "^3.5.0",
"sinon": "^2.3.8",
"stylelint": "^8.0.0",
"stylelint-config-standard": "^17.0.0",
"stylelint-no-unsupported-browser-features": "^1.0.0",
"supertest": "^3.0.0",
"testpilot-ga": "^0.3.0",
"webcrypto-liner": "^0.1.25",
"webpack": "^3.4.1"
"webpack": "^3.5.4",
"webpack-dev-middleware": "^1.12.0"
},
"engines": {
"node": ">=8.2.0"
@@ -53,6 +69,7 @@
"license": "MPL-2.0",
"repository": "mozilla/send",
"availableLanguages": [
"ast",
"az",
"ca",
"cs",
@@ -61,6 +78,7 @@
"dsb",
"el",
"en-US",
"es-AR",
"es-CL",
"es-ES",
"es-MX",
@@ -91,25 +109,38 @@
"zh-TW"
],
"scripts": {
"precommit": "lint-staged",
"clean": "rimraf dist",
"build": "npm-run-all build:*",
"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",
"dev": "npm run clean && npm run build && npm start",
"format": "prettier '{,frontend/src/,scripts/,server/,test/**/!(bundle)}*.{js,css}' --single-quote --write",
"get-prod-locales": "node scripts/get-prod-locales",
"get-prod-locales:write": "npm run get-prod-locales -- --write",
"lint": "npm-run-all lint:*",
"lint:css": "stylelint 'public/*.css'",
"lint:css": "stylelint 'frontend/src/*.css'",
"lint:js": "eslint .",
"lint-locales": "node scripts/lint-locales",
"lint-locales:dev": "npm run lint-locales",
"lint-locales:prod": "npm run lint-locales -- --production",
"start": "node server/server",
"test": "npm-run-all test:*",
"test": "cross-env NODE_ENV=test npm-run-all test:*",
"test:unit": "mocha test/unit",
"test:server": "mocha test/server",
"test--browser": "browserify test/frontend/frontend.bundle.js -o test/frontend/bundle.js -d && node test/frontend/driver.js"
},
"lint-staged": {
"*.js": [
"prettier --single-quote --write",
"eslint",
"git add"
],
"*.css": [
"prettier --single-quote --write",
"stylelint",
"git add"
]
}
}

15
postcss.config.js Normal file
View File

@@ -0,0 +1,15 @@
const autoprefixer = require('autoprefixer');
const cssnano = require('cssnano');
const mqpacker = require('css-mqpacker');
const conf = require('./server/config');
const options = {
plugins: [autoprefixer, mqpacker, cssnano]
};
if (conf.env === 'development') {
options.map = { inline: true };
}
module.exports = options;

View File

@@ -0,0 +1,99 @@
// Firefox Send is a brand name and should not be localized.
title = Firefox Send
siteSubtitle = esperimentu web
siteFeedback = Feedback
uploadPageHeader = Compartición privada y cifrada de ficheros
uploadPageExplainer = Unvia ficheros pente un enllaz seguru, priváu y cifráu que caduca automáticamente p'asegurar que les tos coses nun queden siempres na rede.
uploadPageLearnMore = Deprendi más
uploadPageDropMessage = Suelta equí'l to ficheru p'aniciar la xuba
uploadPageSizeMessage = Pal meyor funcionamientu, lo meyor ye que'l to ficheru seya menor de 1GB
uploadPageBrowseButton = Esbilla un ficheru nel to ordenador
.title = Esbilla un ficheru nel to ordenador
uploadPageBrowseButton1 = Esbilla un ficheru pa unviar
.title = Esbilla un ficheru pa unviar
uploadPageMultipleFilesAlert = Anguaño nun se sofita la xuba múltiple de ficheros o carpetes.
uploadPageBrowseButtonTitle = Xubir ficheru
uploadingPageProgress = Xubiendo { $filename } ({ $size })
importingFile = Importando...
verifyingFile = Verificando...
encryptingFile = Cifrando...
decryptingFile = Descifrando...
notifyUploadDone = Finó la to xuba.
uploadingPageMessage = Namái que'l ficheru xuba, sedrás a afitar les opciones de caducidá.
uploadingPageCancel = Encaboxar xuba
.title = Encaboxar xuba
uploadCancelNotification = Encaboxóse la to xuba.
uploadingPageLargeFileMessage = Esti ficheru ye grande y pue entardar daqué en xubir. ¡Paciencia!
uploadingFileNotification = Avísame cuando se complete la xuba.
uploadSuccessConfirmHeader = Preparáu pa unviar
uploadSvgAlt
.alt = Xubir
uploadSuccessTimingHeader = L'enllaz del to ficheru caducará dempués d'una descarga o en 24 hores.
copyUrlFormLabelWithName = Copia y comparti l'enllaz pa unviar el to ficheru: { $filename }
// Note: Title text for button should be the same.
copyUrlFormButton = Copiar al cartafueyu
.title = Copiar al cartafueyu
copiedUrl = ¡Copióse!
// Note: Title text for button should be the same.
deleteFileButton = Desaniciar ficheru
.title = Desaniciar ficheru
// Note: Title text for button should be the same.
sendAnotherFileLink = Unviar otru ficheru
.title = Unviar otru ficheru
// Alternative text used on the download link/button (indicates an action).
downloadAltText
.alt = Baxar
downloadFileName = Baxar { $filename }
downloadFileSize = ({ $size })
// Firefox Send is a brand name and should not be localized.
downloadMessage = El to collaciu unvióte un ficheru usando Firefox Send, un serviciu que te permite compartir ficheros con un enllaz seguru, priváu y cifráu que caduca automáticamente p'asegurar que les to coses nun queden siempres na rede.
// Text and title used on the download link/button (indicates an action).
downloadButtonLabel = Baxar
.title = Baxar
downloadNotification = Completóse la to descarga.
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 = Prueba Firefox Send
.title = Prueba Firefox Send
downloadingPageProgress = Baxando { $filename } ({ $size })
downloadingPageMessage = Dexa esta llingüeta abierta entrín vamos en cata del to ficheru y lu desciframos, por favor.
errorAltText
.alt = Fallu de xuba
errorPageHeader = ¡Daqué foi mal!
errorPageMessage = Hebo un fallu xubiendo'l ficheru.
errorPageLink = Unviar otru ficheru
fileTooBig = Esti ficheru ye mui grande como pa xubilu. Debería tener menos de { $size }.
linkExpiredAlt
.alt = Enllaz caducáu
expiredPageHeader = ¡Esti enllaz caducó o enxamás nun esistó!
notSupportedHeader = El to restolador nun ta sofitáu.
// Firefox Send is a brand name and should not be localized.
notSupportedDetail = Desafortunadamente esti restolador nun sofita la teunoloxía web qu'usa Firefox Send. Precisarás d'usar otru restolador. ¡Aconseyámoste Firefox!
notSupportedLink = ¿Por qué'l mio restolador nun ta sofitáu?
notSupportedOutdatedDetail = Desafortunadamente esta versión de Firefox nun sofita la teunoloxía web qu'usa Firefox Send. Precisarás d'anovar Firefox.
updateFirefox = Anovar Firefox
downloadFirefoxButtonSub = Descarga de baldre
uploadedFile = Ficheru
copyFileList = Copiar URL
// expiryFileList is used as a column header
expiryFileList = Caduca en
deleteFileList = Desaniciar
nevermindButton = Nun m'importa
legalHeader = Términos y privacidá
legalNoticeTestPilot = Anguaño Firefox Send ye un esperimentu de Test Pilot y ta suxetu a los <a>Términos de serviciu</a> y l'<a>Avisu de privacidá</a> de Test Pilot. <a>Equí</a> pues deprender más tocante a esti esperimentu y la so recoyida de datos.
legalNoticeMozilla = L'usu de Firefox Send tamién ta suxetu al <a>Avisu de privacidá</a> y a los <a>Términos d'usu de la páxina web</a> de Mozilla.
deletePopupText = ¿Desaniciar esti ficheru?
deletePopupYes = Sí
deletePopupCancel = Encaboxar
deleteButtonHover
.title = Desaniciar
copyUrlHover
.title = Copiar URL
footerLinkLegal = Llegal
// Test Pilot is a proper name and should not be localized.
footerLinkAbout = Tocante a Test Pilot
footerLinkPrivacy = Privacidá
footerLinkTerms = Términos
footerLinkCookies = Cookies

View File

@@ -9,9 +9,11 @@ uploadPageDropMessage = Yükləmək üçün faylınızı buraya daşıyın
uploadPageSizeMessage = Xidmətin daha yaxşı işləməsi üçün faylınız 1 GB-dan az olmalıdır
uploadPageBrowseButton = Kompüterinizdən fayl seçin
.title = Kompüterinizdən fayl seçin
uploadPageBrowseButton1 = Yüklənəcək faylı seçin
.title = Yüklənəcək faylı seçin
uploadPageMultipleFilesAlert = Birdən çox fayl və ya qovluq yükləmə hələlik dəstəklənmir.
uploadPageBrowseButtonTitle = Fayl yüklə
uploadingPageHeader = Faylınız yüklənir
uploadingPageProgress = { $filename } ({ $size }) yüklənir
importingFile = İdxal edilir…
verifyingFile = Təsdiqlənir…
encryptingFile = Şifrələnir...
@@ -50,6 +52,8 @@ downloadButtonLabel = Endir
.title = Endir
downloadNotification = Endirməniz tamamlandı.
downloadFinish = Endirmə Tamamlandı
// 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 Yoxla
.title = Firefox Send Yoxla

View File

@@ -0,0 +1,65 @@
// Firefox Send is a brand name and should not be localized.
title = Firefox Send
siteSubtitle = ওয়েব গবেষণা
siteFeedback = প্রতিক্রিয়া
uploadPageLearnMore = আরও জানুন
uploadPageBrowseButton = আপনার কম্পিউটারে ফাইল নির্বাচন করুন
.title = আপনার কম্পিউটারে ফাইল নির্বাচন করুন
uploadPageBrowseButtonTitle = ফাইল আপলোড
importingFile = ইম্পোর্ট হচ্ছে...
verifyingFile = যাচাই হচ্ছে...
encryptingFile = ইনক্রিপট হচ্ছে...
decryptingFile = ডিক্রিপট হচ্ছে...
notifyUploadDone = আপনার আপলোড সম্পন্ন হয়েছে।
uploadingPageCancel = আপলোড বাতিল করুন
.title = আপলোড বাতিল করুন
uploadCancelNotification = আপনার অাপলোড বাতিল করা হয়েছে।
uploadSuccessConfirmHeader = পাঠানোর জন্য প্রস্তুত
uploadSvgAlt
.alt = আপলোড
// 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 = ডাউনলোড সম্পন্ন
errorAltText
.alt = আপালোডে ত্রুটি
errorPageHeader = কোন সমস্যা হয়েছে!
errorPageLink = আরেকটি ফাইল পাঠান
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 = Test Pilot পরিচিতি
footerLinkPrivacy = গোপনীয়তা
footerLinkTerms = শর্তাবলী
footerLinkCookies = কুকি

View File

@@ -9,9 +9,11 @@ uploadPageDropMessage = Arrossegueu el fitxer aquí per començar a pujar-lo
uploadPageSizeMessage = Funciona millor quan els fitxers tenen menys d'1 GB
uploadPageBrowseButton = Trieu un fitxer de l'ordinador
.title = Trieu un fitxer de l'ordinador
uploadPageBrowseButton1 = Seleccioneu el fitxer que voleu pujar
.title = Seleccioneu el fitxer que voleu pujar
uploadPageMultipleFilesAlert = Actualment no es permet pujar diversos fitxers ni una carpeta.
uploadPageBrowseButtonTitle = Puja el fitxer
uploadingPageHeader = S'està pujant el fitxer
uploadingPageProgress = S'està pujant { $filename } ({ $size })
importingFile = S'està important…
verifyingFile = S'està verificant…
encryptingFile = S'està xifrant…
@@ -50,6 +52,8 @@ downloadButtonLabel = Baixa
.title = Baixa
downloadNotification = La baixada ha acabat.
downloadFinish = Ha acabat la baixada
// 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 = Proveu el Firefox Send
.title = Proveu el Firefox Send

View File

@@ -9,9 +9,11 @@ uploadPageDropMessage = Přesunutím souboru sem spustíte jeho nahrávání
uploadPageSizeMessage = Nahrávání funguje nejlépe pro soubory do velikosti 1 GB.
uploadPageBrowseButton = Vybrat soubor z počítače
.title = Výběr souboru z počítače
uploadPageBrowseButton1 = Zvolte soubor k nahrání
.title = Zvolte soubor k nahrání
uploadPageMultipleFilesAlert = Nahrávání více souborů najednou nebo celých složek zatím není podporováno.
uploadPageBrowseButtonTitle = Nahrát soubor
uploadingPageHeader = Nahrávání vašeho souboru
uploadingPageProgress = Nahrávání souboru { $filename } ({ $size })
importingFile = Probíhá import…
verifyingFile = Probíhá ověřování…
encryptingFile = Probíhá šifrování…
@@ -50,6 +52,8 @@ downloadButtonLabel = Stáhnout
.title = Stáhnout
downloadNotification = Stahování bylo dokončeno.
downloadFinish = Stahování dokončeno
// 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 = Vyzkoušejte Firefox Send
.title = Vyzkoušejte Firefox Send
@@ -67,6 +71,7 @@ expiredPageHeader = Platnost tohoto odkazu buď vypršela, nebo vůbec nikdy nee
notSupportedHeader = Váš prohlížeč není podporován.
// Firefox Send is a brand name and should not be localized.
notSupportedDetail = Bohužel tento prohlížeč nepodporuje technologii, kterou Firefox Send používá. Zkuste prosím jiný prohlížeč, doporučujeme Firefox!
notSupportedLink = Proč není můj prohlížeč podporovaný?
notSupportedOutdatedDetail = Tato verze Firefoxu bohužel nepodporuje webovou technologii, která pohání Firefox Send. Musíte aktualizovat svůj prohlížeč.
updateFirefox = Aktualizovat Firefox
downloadFirefoxButtonSub = Stáhnout zdarma

View File

@@ -9,9 +9,11 @@ uploadPageDropMessage = Gollyngwch eich ffeiliau yma i gychwyn llwytho i fyny
uploadPageSizeMessage = Mae'n well cadw maint y ffeiliau o dan 1GB er mwyn iddo weithio ar ei orau.
uploadPageBrowseButton = Dewiswch ffeil ar eich cyfrifiadur
.title = Dewiswch ffeil ar eich cyfrifiadur
uploadPageBrowseButton1 = Dewiswch ffeil i'w llwytho i fyny
.title = Dewiswch ffeil i'w llwytho i fyny
uploadPageMultipleFilesAlert = Nid yw llwytho nifer lluosog o ffeilia neu ffolder yn cael ei gynnal ar hyn o bryd.
uploadPageBrowseButtonTitle = Llwytho ffeil i fyny
uploadingPageHeader = Llwytho eich Ffeiliau i Fyny
uploadingPageProgress = Llwytho $filename} i fyny ({ $size })
importingFile = Mewnforio…
verifyingFile = Wrthi'n gwirio…
encryptingFile = Wrthi'n amgryptio…
@@ -50,6 +52,8 @@ downloadButtonLabel = Llwytho i Lawr
.title = Llwytho i Lawr
downloadNotification = Mae eich llwytho wedi gorffen
downloadFinish = Llwytho wedi Gorffen
// This message is displayed when uploading or downloading a file, e.g. "(1,3 MB of 10 MB)".
fileSizeProgress = ({ $partialSize } o { $totalSize })
// Firefox Send is a brand name and should not be localized. Title text for button should be the same.
sendYourFilesLink = Profwch Firefox Send
.title = Profwch Firefox Send
@@ -67,6 +71,7 @@ expiredPageHeader = Mae'r ddolen wedi dod i ben neu nad yw wedi bodoli erioed!
notSupportedHeader = Nid yw eich porwr yn cael ei gynnal.
// Firefox Send is a brand name and should not be localized.
notSupportedDetail = Yn anffodus nid yw'r porwr hwn yn cynnal y technoleg gwe sy'n cynnal Firefox Send. Bydd angen i chi ddefnyddio porwr arall. Rydym ni'n argymell Firefox!
notSupportedLink = Pam nad yw fy mhorwr yn cael ei gynnal?
notSupportedOutdatedDetail = Yn anffodus nid yw'r fersiwn yma o Firefox yn cynnal y technoleg gwe sy'n gyrru Firefox Send. Bydd angen i chi ddiweddaru eich porwr.
updateFirefox = Diweddaru Firefox
downloadFirefoxButtonSub = Llwytho i Lawr am Ddim

View File

@@ -9,6 +9,8 @@ uploadPageDropMessage = Ziehen Sie eine Datei zum Hochladen hierher
uploadPageSizeMessage = Dateien unter 1 GB sorgen für erhöhte Zuverlässigkeit des Betriebs
uploadPageBrowseButton = Wählen Sie eine Datei auf Ihrem Computer aus
.title = Wählen Sie eine Datei auf Ihrem Computer aus
uploadPageBrowseButton1 = Datei zum Hochladen auswählen
.title = Datei zum Hochladen auswählen
uploadPageMultipleFilesAlert = Hochladen mehrerer Dateien oder eines Ordners wird derzeit nicht unterstützt.
uploadPageBrowseButtonTitle = Datei hochladen
uploadingPageProgress = { $filename } ({ $size }) wird hochgeladen

View File

@@ -9,6 +9,8 @@ uploadPageDropMessage = Śěgniśo swóju dataju sem, aby ju nagrał
uploadPageSizeMessage = Wužywajśo nejlěpje dataje, kótarež su mjeńše ako 1 GB za lěpšu spušćobnosć.
uploadPageBrowseButton = Wubjeŕśo dataju na swójom licadle
.title = Wubjeŕśo dataju na swójom licadle
uploadPageBrowseButton1 = Wubjeŕśo dataju za nagraśe
.title = Wubjeŕśo dataju za nagraśe
uploadPageMultipleFilesAlert = Nagrawanje někotarych datajow abo zarědnika se tuchylu njepódpěra.
uploadPageBrowseButtonTitle = Dataju nagraś
uploadingPageProgress = { $filename } ({ $size }) se nagrawa

View File

@@ -9,9 +9,11 @@ uploadPageDropMessage = Εναποθέστε το αρχείο σας εδώ γ
uploadPageSizeMessage = Για περισσότερο αξιόπιστη λειτουργία, προτείνεται να διατηρήσετε το αρχείο κάτω από 1GB
uploadPageBrowseButton = Επιλέξτε αρχείο από τον υπολογιστή σας
.title = Επιλέξτε αρχείο από τον υπολογιστή σας
uploadPageBrowseButton1 = Επιλέξτε ένα αρχείο για μεταφόρτωση
.title = Επιλέξτε ένα αρχείο για μεταφόρτωση
uploadPageMultipleFilesAlert = Η μεταφόρτωση πολλαπλών αρχείων ή φακέλου δεν υποστηρίζεται αυτή τη στιγμή.
uploadPageBrowseButtonTitle = Μεταφόρτωση αρχείου
uploadingPageHeader = Γίνετε μεταφόρτωση του αρχείου σας
uploadingPageProgress = Μεταφόρτωση του { $filename } ({ $size })
importingFile = Εισαγωγή…
verifyingFile = Επαλήθευση...
encryptingFile = Κρυπτογράφηση…
@@ -50,6 +52,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

@@ -9,6 +9,8 @@ uploadPageDropMessage = Drop your file here to start uploading
uploadPageSizeMessage = For the most reliable operation, its best to keep your file under 1GB
uploadPageBrowseButton = Select a file on your computer
.title = Select a file on your computer
uploadPageBrowseButton1 = Select a file to upload
.title = Select a file to upload
uploadPageMultipleFilesAlert = Uploading multiple files or a folder is currently not supported.
uploadPageBrowseButtonTitle = Upload file
uploadingPageProgress = Uploading { $filename } ({ $size })

View File

@@ -9,6 +9,8 @@ uploadPageDropMessage = Arrastrá el archivo hasta acá para empezar a subir
uploadPageSizeMessage = Para una operación más confiable, es mejor que el archivo tenga menos de 1GB
uploadPageBrowseButton = Seleccioná un archivo en tu computadora
.title = Seleccioná un archivo en tu computadora
uploadPageBrowseButton1 = Seleccioná un archivo para subir
.title = Seleccioná un archivo para subir
uploadPageMultipleFilesAlert = Cargar múltiples archivos o una carpeta todavía no está soportado.
uploadPageBrowseButtonTitle = Subir archivo
uploadingPageProgress = Subiendo { $filename } ({ $size })
@@ -78,6 +80,7 @@ copyFileList = Copiar URL
// expiryFileList is used as a column header
expiryFileList = Expira en
deleteFileList = Borrar
nevermindButton = No importa
legalHeader = Términos y privacidad
legalNoticeTestPilot = Firefox Send es actualmente un experimento de Test Pilot y está sujeto a los <a>términos de servicio</a> y la <a>nota de privacidad</a> de Test Pilot. Podés conocer 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>nota de privacidad de sitios web</a> y los <a>términos de uso de sitios web</a> de Mozilla.

View File

@@ -9,9 +9,11 @@ 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
uploadPageBrowseButton1 = Selecciona un archivo a subir
.title = Selecciona un archivo a subir
uploadPageMultipleFilesAlert = Subir múltiples archivos o una carpeta actualmente no es posible.
uploadPageBrowseButtonTitle = Subir archivo
uploadingPageHeader = Subiendo tu archivo
uploadingPageProgress = Subiendo { $filename } ({ $size })
importingFile = Importando…
verifyingFile = Verificando…
encryptingFile = Cifrando…
@@ -50,6 +52,8 @@ downloadButtonLabel = Descargar
.title = Descargar
downloadNotification = Tu 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 = Probar Firefox Send
.title = Probar Firefox Send

View File

@@ -9,9 +9,11 @@ uploadPageDropMessage = Suelta aquí tu archivo para empezar a subirlo
uploadPageSizeMessage = Para que la operación sea más segura, el archivo debería ocupar menos de 1GB
uploadPageBrowseButton = Seleccionar un archivo en el equipo
.title = Seleccionar un archivo en el equipo
uploadPageBrowseButton1 = Seleccionar un archivo para subir
.title = Seleccionar un archivo para subir
uploadPageMultipleFilesAlert = Aún no se pueden subir varios archivos o una carpeta.
uploadPageBrowseButtonTitle = Subir archivo
uploadingPageHeader = Subiendo archivo
uploadingPageProgress = Subiendo { $filename } ({ $size })
importingFile = Imporando...
verifyingFile = Comprobando...
encryptingFile = Encriptando...
@@ -50,6 +52,8 @@ downloadButtonLabel = Descargar
.title = Descargar
downloadNotification = Se completó la descarga.
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 = Prueba Firefox Send
.title = Prueba Firefox Send

View File

@@ -9,9 +9,11 @@ uploadPageDropMessage = Suelta aquí tu archivo para empezar a subirlo
uploadPageSizeMessage = Para que la operación sea más segura, el archivo debería ocupar menos de 1GB
uploadPageBrowseButton = Selecciona un archivo de tu computadora
.title = Selecciona un archivo de tu computadora
uploadPageBrowseButton1 = Seleccionar un archivo para subir
.title = Seleccionar un archivo para subir
uploadPageMultipleFilesAlert = Aún no se pueden subir varios archivos o una carpeta.
uploadPageBrowseButtonTitle = Subir archivo
uploadingPageHeader = Subiendo tu archivo
uploadingPageProgress = Subiendo { $filename } ({ $size })
importingFile = Importando...
verifyingFile = Verificando...
encryptingFile = Encriptando...
@@ -50,6 +52,8 @@ downloadButtonLabel = Descargar
.title = Descargar
downloadNotification = Tu descarga se ha completado
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 = Prueba Firefox Send
.title = Prueba Firefox Send

View File

@@ -9,6 +9,8 @@ uploadPageDropMessage = Déposez votre fichier ici pour lenvoyer
uploadPageSizeMessage = Pour un résultat fiable, il est conseillé dutiliser des fichiers de taille inférieure à 1 Go
uploadPageBrowseButton = Sélectionner un fichier sur lordinateur
.title = Sélectionner un fichier sur lordinateur
uploadPageBrowseButton1 = Choisir un fichier à envoyer
.title = Choisir un fichier à envoyer
uploadPageMultipleFilesAlert = Lenvoi de plusieurs fichiers ou de dossiers nest pas pris en charge pour le moment.
uploadPageBrowseButtonTitle = Envoyer le fichier
uploadingPageProgress = Envoi en cours de { $filename } ({ $size })

View File

@@ -9,6 +9,8 @@ uploadPageDropMessage = Ćehńće swoju dataju sem, zo byšće ju nahrał
uploadPageSizeMessage = Wužiwajće najlěpje dataje, kotrež su mjeńše hač 1 GB za lěpšu spušćomnosć.
uploadPageBrowseButton = Wubjerće dataju na swojim ličaku
.title = Wubjerće dataju na swojim ličaku
uploadPageBrowseButton1 = Wubjerće dataju za nahraće
.title = Wubjerće dataju za nahraće
uploadPageMultipleFilesAlert = Nahrawanje wjacorych datajow abo rjadowaka so tuchwilu njepodpěruje.
uploadPageBrowseButtonTitle = Dataju nahrać
uploadingPageProgress = { $filename } ({ $size }) so nahrawa

View File

@@ -9,6 +9,8 @@ uploadPageDropMessage = Dobja ide a fájljait, és kezdjen feltölteni
uploadPageSizeMessage = A megbízható működés érdekében a legjobb, ha a fájlok 1 GB-nál kisebbek maradnak
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
uploadPageBrowseButton1 = Válassza ki a feltöltendő fájlt
.title = Válassza ki a feltöltendő fájlt
uploadPageMultipleFilesAlert = Több fájl vagy mappa feltöltése pillanatnyilag nem támogatott.
uploadPageBrowseButtonTitle = Fájl feltöltése
uploadingPageProgress = { $filename } ({ $size }) feltöltése

View File

@@ -9,6 +9,8 @@ uploadPageDropMessage = Trascina qui un file per caricarlo
uploadPageSizeMessage = Per evitare problemi è consigliabile caricare file di dimensione inferiore a 1 GB
uploadPageBrowseButton = Seleziona un file sul computer
.title = Seleziona un file sul computer
uploadPageBrowseButton1 = Seleziona un file da caricare
.title = Seleziona un file da caricare
uploadPageMultipleFilesAlert = Il caricamento di più file o cartelle non è attualmente supportato.
uploadPageBrowseButtonTitle = Carica file
uploadingPageProgress = Caricamento { $filename } ({ $size })

View File

@@ -9,6 +9,8 @@ uploadPageDropMessage = ここにファイルをドロップしてアップロ
uploadPageSizeMessage = 確実に処理できるよう、ファイルサイズは 1 GB 以下にすることを推奨します。
uploadPageBrowseButton = コンピューター上のファイルを選択
.title = コンピューター上のファイルを選択
uploadPageBrowseButton1 = アップロードするファイルを選択
.title = アップロードするファイルを選択
uploadPageMultipleFilesAlert = 今のところ複数ファイルやフォルダーのアップロードには対応していません。
uploadPageBrowseButtonTitle = ファイルをアップロード
uploadingPageProgress = { $filename } ({ $size }) をアップロード中

View File

@@ -9,9 +9,11 @@ uploadPageDropMessage = Zuɣeṛ afaylu-ik ar dagi akken ad tebduḍ asali
uploadPageSizeMessage = I ugmuḍ ufrin, yelha ad tesqedceḍ ifuyla daw n 1 GAṬ
uploadPageBrowseButton = Fren afaylu sef uselkim-ik
.title = Fren afaylu seg uselkim-ik
uploadPageBrowseButton1 = Fren afaylu ad tazneḍ
.title = Fren afaylu ad tazneḍ
uploadPageMultipleFilesAlert = Asali n ddeqs n ifuyla neɣ ikaramen ur ittusefrak ara yakan.
uploadPageBrowseButtonTitle = Sali ifuyla
uploadingPageHeader = Asali n ufaylu-ik
uploadingPageProgress = Tuzna n { $filename } ({ $size })
importingFile = Akter...
verifyingFile = Asenqed...
encryptingFile = Awgelhen...
@@ -50,6 +52,8 @@ downloadButtonLabel = Sider
.title = Sider
downloadNotification = Asider-ik yemmed.
downloadFinish = Asider yemmed
// This message is displayed when uploading or downloading a file, e.g. "(1,3 MB of 10 MB)".
fileSizeProgress = ({ $partialSize } seg { $totalSize })
// Firefox Send is a brand name and should not be localized. Title text for button should be the same.
sendYourFilesLink = Ɛreḍ Firefox Send
.title = Ɛreḍ Firefox Send

View File

@@ -9,6 +9,8 @@ uploadPageDropMessage = Letakkan fail anda di sini untuk mulakan memuat naik
uploadPageSizeMessage = Untuk operasi yang paling selamat, lebih baik pastikan fail anda itu kurang 1GB
uploadPageBrowseButton = Pilih fail dalam komputer anda
.title = Pilih fail dalam komputer anda
uploadPageBrowseButton1 = Pilih fail untuk dimuat naik
.title = Pilih fail untuk dimuat naik
uploadPageMultipleFilesAlert = Memuat naik pelbagai fail atau satu folder masih belum disokong.
uploadPageBrowseButtonTitle = Muat naik fail
uploadingPageProgress = Memuat naik { $filename } ({ $size })

View File

@@ -9,9 +9,11 @@ uploadPageDropMessage = Slipp din fil her for å starte opplastingen
uploadPageSizeMessage = For den mest problemfrie bruken, er det best å holde filen under 1 GB
uploadPageBrowseButton = Velg en fil på din datamaskin
.title = Velg en fil på din datamaskin
uploadPageBrowseButton1 = Velg en fil til å laste opp
.title = Velg en fil til å laste opp
uploadPageMultipleFilesAlert = Opplasting av flere filer eller en mappe støttes ikke for øyeblikket.
uploadPageBrowseButtonTitle = Last opp fil
uploadingPageHeader = Laster opp din fil
uploadingPageProgress = Laster opp { $filename } ({ $size })
importingFile = Importerer…
verifyingFile = Verifiserer...
encryptingFile = Krypterer...
@@ -50,6 +52,8 @@ downloadButtonLabel = Last ned
.title = Last ned
downloadNotification = Nedlastingen er fullført.
downloadFinish = Nedlastingen 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

@@ -9,6 +9,8 @@ uploadPageDropMessage = Sleep uw bestand hiernaartoe om het te uploaden
uploadPageSizeMessage = Voor de meest betrouwbare werking kunt u uw bestand het beste onder de 1 GB houden
uploadPageBrowseButton = Selecteer een bestand op uw computer
.title = Selecteer een bestand op uw computer
uploadPageBrowseButton1 = Selecteer een bestand om te uploaden
.title = Selecteer een bestand om te uploaden
uploadPageMultipleFilesAlert = Het uploaden van meerdere bestanden of een map wordt momenteel niet ondersteund.
uploadPageBrowseButtonTitle = bestand uploaden
uploadingPageProgress = { $filename } ({ $size }) wordt geüpload

View File

@@ -9,6 +9,8 @@ uploadPageDropMessage = Slepp fila di her for å starte opplastinga
uploadPageSizeMessage = For mest problemfrie bruk, er det best å halde fila under 1 GB
uploadPageBrowseButton = Vel ei fil på datamaskina di
.title = Vel ei fil på datamaskina di
uploadPageBrowseButton1 = Vel ei fil å laste opp
.title = Vel ei fil å laste opp
uploadPageMultipleFilesAlert = Opplasting av fleire filer eller ei mappe er for tida ikkje støtta.
uploadPageBrowseButtonTitle = Last opp fil
uploadingPageProgress = Lastar opp { $filename } ({ $size })

View File

@@ -9,6 +9,8 @@ uploadPageDropMessage = Arraste o arquivo para cá para iniciar o envio
uploadPageSizeMessage = Para uma operação mais confiável, é melhor manter seu arquivo menor que 1GB
uploadPageBrowseButton = Selecione um arquivo em seu computador
.title = Selecione um arquivo em seu computador
uploadPageBrowseButton1 = Selecione um arquivo para carregar
.title = Selecione um arquivo para carregar
uploadPageMultipleFilesAlert = Enviar múltiplos arquivos ou uma pasta ainda não é suportado.
uploadPageBrowseButtonTitle = Enviar arquivo
uploadingPageProgress = Enviando { $filename } ({ $size })

View File

@@ -9,6 +9,8 @@ uploadPageDropMessage = Largue o seu ficheiro aqui para começar a carregar
uploadPageSizeMessage = Para uma operação mais confiável, é melhor manter o seu ficheiro abaixo de 1GB
uploadPageBrowseButton = Selecionar um ficheiro no seu computador
.title = Selecionar um ficheiro no seu computador
uploadPageBrowseButton1 = Selecione um ficheiro a enviar
.title = Selecione um ficheiro a enviar
uploadPageMultipleFilesAlert = Carregar múltiplos ficheiros ou uma pasta não é atualmente suportado.
uploadPageBrowseButtonTitle = Carregar ficheiro
uploadingPageProgress = A carregar { $filename } ({ $size })

View File

@@ -9,9 +9,11 @@ uploadPageDropMessage = Перетащите свой файл сюда, что
uploadPageSizeMessage = Для более надёжной работы сервиса, размер вашего файла не должен превышать 1ГБ.
uploadPageBrowseButton = Выбрать файл с моего компьютера
.title = Выбрать файл с моего компьютера
uploadPageBrowseButton1 = Выбрать файл для загрузки
.title = Выбрать файл для загрузки
uploadPageMultipleFilesAlert = Загрузка нескольких файлов или папок в настоящее время не поддерживается.
uploadPageBrowseButtonTitle = Загрузить файл
uploadingPageHeader = Загрузка вашего файла
uploadingPageProgress = Загружаю { $filename } ({ $size })
importingFile = Импортирование...
verifyingFile = Проверка...
encryptingFile = Шифрование...
@@ -50,6 +52,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

@@ -9,6 +9,8 @@ uploadPageDropMessage = Presunutím súboru sem začnete nahrávanie
uploadPageSizeMessage = Pre zaistenie čo najväčšej spoľahlivosti vám odporúčame nahrávať súbory menšie než 1GB.
uploadPageBrowseButton = Vyberte súbor vo vašom počítači
.title = Vyberte súbor vo vašom počítači
uploadPageBrowseButton1 = Vyberte súbor na nahratie
.title = Vyberte súbor na nahratie
uploadPageMultipleFilesAlert = Nahrávanie viacerých súborov alebo priečinkov momentálne nie je podporované.
uploadPageBrowseButtonTitle = Nahrať súbor
uploadingPageProgress = Nahrávanie súboru { $filename } ({ $size })

View File

@@ -9,6 +9,8 @@ uploadPageDropMessage = Tukaj spustite datoteko za začetek nalaganja
uploadPageSizeMessage = Za zanesljivo delovanje je najbolje, da datoteka ne presega 1 GB
uploadPageBrowseButton = Izberite datoteko na računalniku
.title = Izberite datoteko na računalniku
uploadPageBrowseButton1 = Izberite datoteko za nalaganje
.title = Izberite datoteko za nalaganje
uploadPageMultipleFilesAlert = Nalaganje več datotek ali map trenutno ni podprto.
uploadPageBrowseButtonTitle = Naloži datoteko
uploadingPageProgress = Nalaganje { $filename } ({ $size })

View File

@@ -9,6 +9,8 @@ uploadPageDropMessage = Превуците ваше датотеке овде д
uploadPageSizeMessage = За бољи рад предлажемо да датотека не буде већа од 1GB
uploadPageBrowseButton = Изаберите датотеку на рачунару
.title = Изаберите датотеку на рачунару
uploadPageBrowseButton1 = Изаберите датотеку за отпремање
.title = Изаберите датотеку за отпремање
uploadPageMultipleFilesAlert = Отпремање фасцикли или више датотека тренутно није подржано.
uploadPageBrowseButtonTitle = Отпреми датотеку
uploadingPageProgress = Отпремам { $filename } ({ $size })

View File

@@ -9,9 +9,11 @@ uploadPageDropMessage = Släpp filen här för att börja ladda upp
uploadPageSizeMessage = För den mest tillförlitliga driften är det bäst att hålla din fil under 1 GB
uploadPageBrowseButton = Välj en fil på din dator
.title = Välj en fil på din dator
uploadPageBrowseButton1 = Välj en fil att ladda upp
.title = Välj en fil att ladda upp
uploadPageMultipleFilesAlert = Överföring av flera filer eller en mapp stöds för närvarande inte.
uploadPageBrowseButtonTitle = Ladda upp fil
uploadingPageHeader = Överför din fil
uploadingPageProgress = Laddar upp { $filename } ({ $size })
importingFile = Importerar…
verifyingFile = Verifierar…
encryptingFile = Krypterar…
@@ -50,6 +52,8 @@ downloadButtonLabel = Ladda ner
.title = Ladda ner
downloadNotification = Din nedladdning har slutförts.
downloadFinish = Nedladdning klar
// 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 = Testa Firefox Send
.title = Testa Firefox Send
@@ -67,7 +71,7 @@ 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!
notSupportedLink = Varför stödjs inte min webbläsare?
notSupportedLink = Varför stöds 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

View File

@@ -7,8 +7,10 @@ uploadPageDropMessage = ఎగుమతిని ప్రారంభించ
uploadPageSizeMessage = అత్యంత నమ్మకమైన కార్యం కోసం, మీ ఫైలును 1GB కంటే తక్కువగా ఉంచడం ఉత్తమం
uploadPageBrowseButton = మీ కంప్యూటర్లో ఒక ఫైలును ఎంచుకోండి
.title = మీ కంప్యూటర్లో ఒక ఫైలును ఎంచుకోండి
uploadPageBrowseButton1 = ఎక్కించటానికి ఒక ఫైలును ఎంచుకోండి
.title = ఎక్కించటానికి ఒక ఫైలును ఎంచుకోండి
uploadPageBrowseButtonTitle = ఫైలును ఎగుమతి చేయండి
uploadingPageHeader = మీ ఫైలు ఎగుమతి అవుతుంది
uploadingPageProgress = { $filename } ({ $size }) ఎక్కుతోంది
importingFile = దిగుమతవుతోంది...
verifyingFile = పరిశీలిస్తున్నది…
encryptingFile = గుప్తీకరిస్తోంది...
@@ -45,6 +47,8 @@ downloadButtonLabel = దిగుమతి
.title = దిగుమతి
downloadNotification = మీ దిగుమతి పూర్తయ్యింది.
downloadFinish = దిగుమతి పూర్తయింది
// This message is displayed when uploading or downloading a file, e.g. "(1,3 MB of 10 MB)".
fileSizeProgress = { $totalSize }) యొక్క ({ $partialSize }
// 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

@@ -9,9 +9,11 @@ uploadPageDropMessage = Yüklemeyi başlatmak için dosyanızı buraya bırakın
uploadPageSizeMessage = Sorun yaşamamak adına dosyanızın 1 GBden küçük olmasını öneririz
uploadPageBrowseButton = Bilgisayarınızdan bir dosya seçin
.title = Bilgisayarınızdan bir dosya seçin
uploadPageBrowseButton1 = Yüklenecek dosyayı seçin
.title = Yüklenecek dosyayı seçin
uploadPageMultipleFilesAlert = Birden fazla dosya veya klasör yükleme şimdilik desteklenmiyor.
uploadPageBrowseButtonTitle = Dosyayı yükle
uploadingPageHeader = Dosyanız yükleniyor
uploadingPageProgress = { $filename } yükleniyor ({ $size })
importingFile = İçe aktarılıyor…
verifyingFile = Doğrulanıyor…
encryptingFile = Şifreleniyor…
@@ -50,6 +52,8 @@ downloadButtonLabel = İndir
.title = İndir
downloadNotification = İndirme tamamlandı.
downloadFinish = İndirme tamamlandı
// 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 Sendi deneyin
.title = Firefox Sendi deneyin

View File

@@ -3,12 +3,14 @@ title = Firefox Send
siteSubtitle = веб-експеримент
siteFeedback = Відгуки
uploadPageHeader = Приватний, зашифрований обмін файлами
uploadPageExplainer = Надсилайте файли, використовуючи безпечні, приватні та зашифровані посилання, термін дії яких автоматично закінчується, щоб ваші файли не лишився в Інтернеті назавжди.
uploadPageExplainer = Надсилайте файли, використовуючи безпечні, приватні та зашифровані посилання, термін дії яких автоматично закінчується, щоб ваші файли не лишилися в Інтернеті назавжди.
uploadPageLearnMore = Докладніше
uploadPageDropMessage = Перетягніть свій файл сюди, щоб почати вивантаження
uploadPageSizeMessage = Для більш надійної роботи сервісу, розмір вашого файлу не має перевищувати 1ГБ.
uploadPageBrowseButton = Виберіть файл на комп'ютері
.title = Виберіть файл на комп'ютері
uploadPageBrowseButton1 = Виберіть файл для вивантаження
.title = Виберіть файл для вивантаження
uploadPageMultipleFilesAlert = Вивантаження кількох файлів чи тек на даний момент не підтримується.
uploadPageBrowseButtonTitle = Вивантажити файл
uploadingPageProgress = Вивантажуємо { $filename } ({ $size })

View File

@@ -9,6 +9,8 @@ uploadPageDropMessage = Kéo thả tập tin của bạn vào đây và bắt đ
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
uploadPageBrowseButton1 = Chọn tập tin để tải lên
.title = Chọn tập tin để tải lên
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 })

View File

@@ -9,6 +9,8 @@ uploadPageDropMessage = 拖放您的文件到此处以开始上传
uploadPageSizeMessage = 为保证运行稳定,建议文件大小不超过 1GB
uploadPageBrowseButton = 选择一个您电脑上的文件
.title = 选择一个您电脑上的文件
uploadPageBrowseButton1 = 选择一个要上传的文件
.title = 选择一个要上传的文件
uploadPageMultipleFilesAlert = 目前不支持上传多个文件或上传文件夹。
uploadPageBrowseButtonTitle = 上传文件
uploadingPageProgress = 正在上传 { $filename } ({ $size })

View File

@@ -9,6 +9,8 @@ uploadPageDropMessage = 將檔案放到此處開始上傳
uploadPageSizeMessage = 為了讓系統能最穩定地執行,請盡量將檔案控制在 1GB 以下。
uploadPageBrowseButton = 選擇您電腦上的檔案
.title = 選擇您電腦上的檔案
uploadPageBrowseButton1 = 選擇要上傳的檔案
.title = 選擇要上傳的檔案
uploadPageMultipleFilesAlert = 目前暫不支援上傳多個檔案或資料夾。
uploadPageBrowseButtonTitle = 上傳檔案
uploadingPageProgress = 正在上傳 { $filename }{ $size }

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

View File

@@ -1,6 +1,7 @@
const fs = require('fs');
const path = require('path');
const pkg = require('../package.json');
const mkdirp = require('mkdirp');
let commit;
@@ -10,11 +11,11 @@ try {
// Whatever...
}
const filename = path.join(__dirname, '..', 'public', 'version.json');
const filename = path.join(__dirname, '..', 'dist', 'public', 'version.json');
const filedata = {
commit,
source: pkg.homepage,
version: process.env.CIRCLE_TAG || `v${pkg.version}`
};
mkdirp.sync(path.dirname(filename));
fs.writeFileSync(filename, JSON.stringify(filedata, null, 2) + '\n');

View File

@@ -51,6 +51,11 @@ const conf = convict({
format: Boolean,
default: false,
env: 'L10N_DEV'
},
base_url: {
format: 'url',
default: 'https://send.firefox.com',
env: 'BASE_URL'
}
});

View File

@@ -9,7 +9,7 @@ const storage = require('./storage.js');
const Raven = require('raven');
const crypto = require('crypto');
const fs = require('fs');
const version = require('../public/version.json');
const version = require('../dist/public/version.json');
if (conf.sentry_dsn) {
Raven.config(conf.sentry_dsn).install();
@@ -19,7 +19,7 @@ const mozlog = require('./log.js');
const log = mozlog('send.server');
const STATIC_PATH = path.join(__dirname, '../public');
const STATIC_PATH = path.join(__dirname, '../dist/public');
const app = express();
@@ -38,14 +38,32 @@ function prodLangs() {
const availableLanguages = conf.l10n_dev ? allLangs() : prodLangs();
// dev middleware is broken at the moment because of how webpack builds the
// handlebars templates. Leaving the commented code here as a mark of shame.
// if (conf.env === 'development') {
// const webpack = require('webpack');
// const webpackDevMiddleware = require('webpack-dev-middleware');
// const config = require('../webpack.config.js');
// config.devtool = 'inline-source-map';
// const compiler = webpack(config);
// const wdm = webpackDevMiddleware(compiler, {
// publicPath: config.output.publicPath
// });
// app.use(wdm);
// }
app.set('views', 'dist/views/');
app.engine(
'handlebars',
exphbs({
defaultLayout: 'main',
partialsDir: 'views/partials/',
layoutsDir: 'dist/views/layouts',
helpers: {
availableLanguages,
l10nDev: conf.l10n_dev
baseUrl: conf.base_url,
title: 'Firefox Send',
description:
'Encrypt and send files with a link that automatically expires to ensure your important documents dont stay online forever.'
}
})
);
@@ -86,6 +104,14 @@ app.use(
})
);
app.use(bodyParser.json());
app.use(
'/resources',
express.static(path.join(STATIC_PATH, 'resources'), {
setHeaders: function(res) {
res.set('Cache-Control', 'public, max-age=31536000, immutable');
}
})
);
app.use(express.static(STATIC_PATH));
app.get('/', (req, res) => {
@@ -135,7 +161,7 @@ app.get('/exists/:id', async (req, res) => {
app.get('/download/:id', async (req, res) => {
const id = req.params.id;
if (!validateID(id)) {
res.sendStatus(404);
res.status(404).render('notfound');
return;
}
@@ -236,24 +262,27 @@ app.post('/upload', (req, res, next) => {
meta.delete = crypto.randomBytes(10).toString('hex');
req.pipe(req.busboy);
req.busboy.on('file', async (fieldname, file, filename) => {
try {
await storage.set(newId, file, filename, meta);
const protocol = conf.env === 'production' ? 'https' : req.protocol;
const url = `${protocol}://${req.get('host')}/download/${newId}/`;
res.json({
url,
delete: meta.delete,
id: newId
});
} catch (e) {
if (e.message === 'limit') {
return res.sendStatus(413);
req.busboy.on(
'file',
async (fieldname, file, filename, encoding, mimeType) => {
try {
meta.mimeType = mimeType || 'application/octet-stream';
await storage.set(newId, file, filename, meta);
const protocol = conf.env === 'production' ? 'https' : req.protocol;
const url = `${protocol}://${req.get('host')}/download/${newId}/`;
res.json({
url,
delete: meta.delete,
id: newId
});
} catch (e) {
if (e.message === 'limit') {
return res.sendStatus(413);
}
res.sendStatus(500);
}
res.sendStatus(500);
}
});
);
req.on('close', async err => {
try {

View File

@@ -1,22 +1,124 @@
const path = require('path');
const webpack = require('webpack');
const HtmlPlugin = require('html-webpack-plugin');
const CopyPlugin = require('copy-webpack-plugin');
module.exports = {
entry: {
vendor: ['babel-polyfill', 'raven-js'],
upload: ['./frontend/src/upload.js'],
download: ['./frontend/src/download.js']
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'public')
filename: 'resources/[name].[chunkhash].js',
path: path.resolve(__dirname, 'dist/public'),
publicPath: '/'
},
module: {
loaders: [
rules: [
{
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'] }
loader: 'babel-loader',
include: [
path.resolve(__dirname, 'frontend'),
path.resolve(__dirname, 'node_modules/testpilot-ga/src')
],
options: {
babelrc: false,
presets: [['es2015', { modules: false }], 'stage-2']
}
},
{
test: /\.(svg|png|jpg)$/,
loader: 'file-loader',
options: {
name: 'resources/[name].[hash].[ext]'
}
},
{
test: /\.css$/,
use: [
{
loader: 'file-loader',
options: {
name: 'resources/[name].[hash].[ext]'
}
},
'extract-loader',
{ loader: 'css-loader', options: { importLoaders: 1 } },
'postcss-loader'
]
},
{
test: /\.hbs$/,
use: [
{
loader: 'html-loader',
options: {
interpolate: 'require',
minimize: false
}
}
]
}
]
}
},
plugins: [
new CopyPlugin([
{
context: 'public',
from: 'locales/**/*.ftl'
},
{
context: 'public',
from: '*.*'
},
{
from: 'views/**',
to: '../'
},
{
context: 'node_modules/l20n/dist/web',
from: 'l20n.min.js'
}
]),
new HtmlPlugin({
filename: '../views/index.handlebars',
template: 'webpack/upload.hbs',
chunks: ['upload']
}),
new HtmlPlugin({
filename: '../views/download.handlebars',
template: 'webpack/download.hbs',
chunks: ['download']
}),
new HtmlPlugin({
filename: '../views/legal.handlebars',
template: 'webpack/legal.hbs',
inject: false
}),
new HtmlPlugin({
filename: '../views/notfound.handlebars',
template: 'webpack/notfound.hbs',
inject: false
}),
new HtmlPlugin({
filename: '../views/layouts/main.handlebars',
template: 'webpack/layout.hbs',
inject: 'head',
excludeChunks: ['upload', 'download']
}),
new HtmlPlugin({
filename: '../views/unsupported.handlebars',
template: 'webpack/unsupported.hbs',
inject: false
}),
new webpack.HashedModuleIdsPlugin(),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor'
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'runtime'
})
]
};

View File

@@ -1,5 +1,4 @@
<div id="download">
<script src="/download.js"></script>
<div id="download-page-one">
<div class="title">
<span id="dl-file"
@@ -11,17 +10,20 @@
<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"/>
<img src="../public/resources/illustration_download.svg" id="download-img" data-l10n-id="downloadAltText"/>
<div>
<button id="download-btn" data-l10n-id="downloadButtonLabel"></button>
<button id="download-btn" class="btn" data-l10n-id="downloadButtonLabel"></button>
</div>
</div>
<div id="download-progress" hidden="true">
<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">
<div class="progress-bar">
<svg id="progress" width="166" height="166" viewPort="0 0 166 166" version="1.1">
<circle r="73" cx="83" cy="83" fill="transparent"/>
<circle id="bar" r="73" cx="83" cy="83" fill="transparent" transform="rotate(-90 83 83)" stroke-dasharray="458.67" stroke-dashoffset="458.67"/>
</svg>
<div class="percentage">
<span class="percent-number"></span>
<span class="percent-sign">%</span>
@@ -32,5 +34,10 @@
</div>
</div>
<div id="upload-error" hidden="true">
<div class="title" data-l10n-id="errorPageHeader"></div>
<img id="upload-error-img" data-l10n-id="errorAltText" src="../public/resources/illustration_error.svg"/>
</div>
<a class="send-new" data-state="completed" data-l10n-id="sendYourFilesLink" href="/"></a>
</div>

View File

@@ -7,25 +7,34 @@
<meta name="defaultLanguage" content="en-US">
<meta name="availableLanguages" content="{{availableLanguages}}">
<title>Firefox Send</title>
<meta property="og:title" content="{{title}}"/>
<meta name="twitter:title" content="{{title}}"/>
<meta name="description" content="{{description}}"/>
<meta property="og:description" content="{{description}}"/>
<meta name="twitter:description" content="{{description}}"/>
<meta name="twitter:card" content="summary"/>
<meta property="og:image" content="{{baseUrl}}${require('../public/resources/send-fb.jpg')}"/>
<meta name="twitter:image" content="{{baseUrl}}${require('../public/resources/send-twitter.jpg')}"/>
<meta property="og:url" content="{{baseUrl}}"/>
<link rel="stylesheet" type="text/css" href="/main.css" />
<title>{{title}}</title>
<link rel="stylesheet" type="text/css" href="${require('../frontend/src/main.css')}" />
{{#if fira}}
<link rel="stylesheet" type="text/css" href="https://code.cdn.mozilla.net/fonts/fira.css" />
{{/if}}
<link rel="icon" type="image/png" href="/resources/favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="${require('../public/resources/favicon-32x32.png')}" sizes="32x32" />
<link rel="localization" href="/locales/{locale}/send.ftl">
<script src="/jsconfig.js"></script>
<script src="/polyfill.min.js"></script>
<script defer src="/l20n.min.js"></script>
</head>
<body>
<header class="header">
<div class="send-logo">
<a href="/">
<img src="/resources/send_logo.svg" alt="Send"/><h1 class="site-title">Send</h1>
<img src="../public/resources/send_logo.svg" alt="Send"/><h1 class="site-title">Send</h1>
</a>
<div class="site-subtitle">
<a href="https://testpilot.firefox.com">Firefox Test Pilot</a>
@@ -44,7 +53,7 @@
</div>
<div class="footer">
<div class="legal-links">
<a href="https://www.mozilla.org"><img class="mozilla-logo" src="/resources/mozilla-logo.svg"/></a>
<a href="https://www.mozilla.org" role="presentation"><img class="mozilla-logo" src="../public/resources/mozilla-logo.svg" alt="mozilla"/></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>
@@ -52,8 +61,8 @@
<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"><img class="github" src="/resources/github-icon.svg"/></a>
<a href="https://twitter.com/FxTestPilot"><img class="twitter" src="/resources/twitter-icon.svg"/></a>
<a href="https://github.com/mozilla/send" role="presentation"><img class="github" src="../public/resources/github-icon.svg" alt="github"/></a>
<a href="https://twitter.com/FxTestPilot" role="presentation"><img class="twitter" src="../public/resources/twitter-icon.svg" alt="twitter"/></a>
</div>
</div>
</body>

View File

@@ -1,7 +1,7 @@
<div id="download">
<div class="title" data-l10n-id="expiredPageHeader"></div>
<div class="share-window">
<img src="/resources/illustration_expired.svg" id="expired-img" data-l10n-id="linkExpiredAlt"/>
<img src="../public/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="/" data-state="notfound" data-l10n-id="sendYourFilesLink"></a>

View File

@@ -3,14 +3,14 @@
{{#if outdated}}
<div class="description" data-l10n-id="notSupportedOutdatedDetail">Unfortunately this version of Firefox does not support the web technology that powers Firefox Send. Youll need to update your browser.</div>
<a id="update-firefox" href="https://support.mozilla.org/kb/update-firefox-latest-version">
<img src="/resources/firefox_logo-only.svg" class="firefox-logo" alt="Firefox"/>
<img src="../public/resources/firefox_logo-only.svg" class="firefox-logo" alt="Firefox"/>
<div class="unsupported-button-text" data-l10n-id="updateFirefox">Update Firefox</div>
</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">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"/>
<img src="../public/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>
</div>

View File

@@ -1,17 +1,16 @@
<div id="page-one" hidden>
<script src="/upload.js"></script>
<div class="title" data-l10n-id="uploadPageHeader"></div>
<div class="description">
<div data-l10n-id="uploadPageExplainer"></div>
<a href="https://testpilot.firefox.com/experiments/send" class="link" data-l10n-id="uploadPageLearnMore"></a>
</div>
<div class="upload-window" >
<div id="upload-img"><img data-l10n-id="uploadSvgAlt" src="/resources/upload.svg"/></div>
<div id="upload-img"><img data-l10n-id="uploadSvgAlt" src="../public/resources/upload.svg"/></div>
<div id="upload-text" data-l10n-id="uploadPageDropMessage"></div>
<span id="file-size-msg"><em data-l10n-id="uploadPageSizeMessage"></em></span>
<form method="post" action="upload" enctype="multipart/form-data">
<label for="file-upload" id="browse"
data-l10n-id="uploadPageBrowseButton"></label>
data-l10n-id="uploadPageBrowseButton1" class="btn"></label>
<input id="file-upload" type="file" name="fileUploaded" />
</form>
</div>
@@ -37,8 +36,11 @@
<div id="upload-progress" hidden="true">
<div class="title" id="upload-filename"></div>
<div class="description"></div>
<!-- progress bar here -->
<div class="progress-bar" id="ul-progress">
<div class="progress-bar">
<svg id="progress" width="166" height="166" viewPort="0 0 166 166" version="1.1">
<circle r="73" cx="83" cy="83" fill="transparent"/>
<circle id="bar" r="73" cx="83" cy="83" fill="transparent" transform="rotate(-90 83 83)" stroke-dasharray="458.67" stroke-dashoffset="458.67"/>
</svg>
<div class="percentage">
<span class="percent-number">0</span>
<span class="percent-sign">%</span>
@@ -58,9 +60,9 @@
<div id="copy-text"></div>
<div id="copy">
<input id="link" type="url" value="" readonly/>
<button id="copy-btn" data-l10n-id="copyUrlFormButton"></button>
<button id="copy-btn" class="btn" data-l10n-id="copyUrlFormButton"></button>
</div>
<button id="delete-file" data-l10n-id="deleteFileButton"></button>
<button id="delete-file" class="btn" data-l10n-id="deleteFileButton"></button>
<a class="send-new" data-state="completed" data-l10n-id="sendAnotherFileLink" href="/"></a>
</div>
</div>
@@ -68,6 +70,6 @@
<div id="upload-error" hidden="true">
<div class="title" data-l10n-id="errorPageHeader"></div>
<div class="expired-description" data-l10n-id="errorPageMessage"></div>
<img id="upload-error-img" data-l10n-id="errorAltText" src="/resources/illustration_error.svg"/>
<a class="send-new" data-state="errored" data-l10n-id="sendAnotherFileLink"></a>
<img id="upload-error-img" data-l10n-id="errorAltText" src="../public/resources/illustration_error.svg"/>
<a class="send-new" href="/" data-state="errored" data-l10n-id="sendAnotherFileLink"></a>
</div>