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