mirror of
https://gitlab.com/timvisee/send.git
synced 2025-12-06 14:10:53 +03:00
Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
07a817266c | ||
|
|
706708876c | ||
|
|
fc4cbe4b74 | ||
|
|
9e8429cff3 | ||
|
|
f8db7ca923 | ||
|
|
70e4d9eeb0 | ||
|
|
410ec72fff | ||
|
|
a42a517896 | ||
|
|
d9c9d95b89 | ||
|
|
0ed10649ef | ||
|
|
4edf82cc21 | ||
|
|
775726ae6d | ||
|
|
5aeaf2e987 | ||
|
|
773244f320 | ||
|
|
5079d9a317 | ||
|
|
18e1609cb3 | ||
|
|
41796840c4 | ||
|
|
171b64bc98 | ||
|
|
cfc94fd9af | ||
|
|
914394054e | ||
|
|
7a237b9b68 | ||
|
|
80e9f129d8 | ||
|
|
fddf1c40dc | ||
|
|
557db53d39 | ||
|
|
c16e00e5af | ||
|
|
cd7da20024 |
10
CHANGELOG.md
10
CHANGELOG.md
@@ -1,6 +1,14 @@
|
||||
## Change Log
|
||||
|
||||
### upcoming (2018/02/27 01:52 +00:00)
|
||||
### upcoming (2018/03/08 19:27 +00:00)
|
||||
- [#782](https://github.com/mozilla/send/pull/782) updated docs (@dannycoates)
|
||||
- [#781](https://github.com/mozilla/send/pull/781) Don't translate URL-safe chars, b64 is doing it for us (@timvisee)
|
||||
- [#779](https://github.com/mozilla/send/pull/779) implemented crypto polyfills for ms edge (@dannycoates)
|
||||
|
||||
### v2.4.1 (2018/02/28 17:05 +00:00)
|
||||
- [#777](https://github.com/mozilla/send/pull/777) use a separate circle in the progress svg for indefinite progress (@dannycoates)
|
||||
|
||||
### v2.4.0 (2018/02/27 01:55 +00:00)
|
||||
- [#769](https://github.com/mozilla/send/pull/769) removed unsafe-inline styles via svgo-loader (@dannycoates)
|
||||
- [#767](https://github.com/mozilla/send/pull/767) added coverage artifact to circleci (@dannycoates)
|
||||
- [#766](https://github.com/mozilla/send/pull/766) Some frontend unit tests [WIP] (@dannycoates)
|
||||
|
||||
@@ -3,6 +3,7 @@ Abhinav Adduri
|
||||
Adnan Kičin
|
||||
Alberto Castro
|
||||
Alexander Slovesnik
|
||||
Alfredos-Panagiotis Damkalis
|
||||
Amin Mahmudian
|
||||
Andreas Pettersson
|
||||
Arash Mousavi
|
||||
@@ -33,6 +34,7 @@ Fjoerfoks
|
||||
Francesco Lodolo
|
||||
Francesco Lodolo [:flod]
|
||||
Frederick Villaluna
|
||||
Gabriela
|
||||
Gautam krishna.R
|
||||
Georgianizator
|
||||
Hyeonseok Shin
|
||||
@@ -111,6 +113,7 @@ Tymur Faradzhev
|
||||
Uccen Marzuq
|
||||
Varghese Thomas
|
||||
Victor Bychek
|
||||
Vitaliy Krutko
|
||||
Weihang Lo
|
||||
Wil Clouser
|
||||
YFdyh000
|
||||
@@ -143,6 +146,7 @@ shikhar-scs
|
||||
siparon
|
||||
skystar-p
|
||||
tiagomoraismorgado
|
||||
timvisee
|
||||
xcffl
|
||||
ybouhamam
|
||||
Μιχάλης
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
[](https://circleci.com/gh/mozilla/send)
|
||||
[](https://testpilot.firefox.com/experiments/send)
|
||||
|
||||
**Docs:** [Docker](docs/docker.md), [Metrics](docs/metrics.md)
|
||||
**Docs:** [FAQ](docs/faq.md), [Encryption](docs/encryption.md), [Build](docs/build.md), [Docker](docs/docker.md), [Metrics](docs/metrics.md), [More](docs/)
|
||||
|
||||
---
|
||||
|
||||
@@ -71,6 +71,8 @@ The server is configured with environment variables. See [server/config.js](serv
|
||||
|
||||
Firefox Send localization is managed via [Pontoon](https://pontoon.mozilla.org/projects/test-pilot-firefox-send/), not direct pull requests to the repository. If you want to fix a typo, add a new language, or simply know more about localization, please get in touch with the [existing localization team](https://pontoon.mozilla.org/teams/) for your language or Mozilla’s [l10n-drivers](https://wiki.mozilla.org/L10n:Mozilla_Team#Mozilla_Corporation) for guidance.
|
||||
|
||||
see also [docs/localization.md](docs/localization.md)
|
||||
|
||||
---
|
||||
|
||||
## Contributing
|
||||
|
||||
@@ -123,6 +123,7 @@ a {
|
||||
cursor: pointer;
|
||||
|
||||
/* Force flat button look */
|
||||
/* stylelint-disable-next-line plugin/no-unsupported-browser-features */
|
||||
appearance: none;
|
||||
font-size: 15px;
|
||||
padding-bottom: 3px;
|
||||
|
||||
@@ -19,6 +19,7 @@ export default function(state, emitter) {
|
||||
return;
|
||||
}
|
||||
if (target.files.length > 1) {
|
||||
// eslint-disable-next-line no-alert
|
||||
return alert(state.translate('uploadPageMultipleFilesAlert'));
|
||||
}
|
||||
const file = target.files[0];
|
||||
@@ -26,9 +27,8 @@ export default function(state, emitter) {
|
||||
return;
|
||||
}
|
||||
if (file.size > MAXFILESIZE) {
|
||||
window.alert(
|
||||
state.translate('fileTooBig', { size: bytes(MAXFILESIZE) })
|
||||
);
|
||||
// eslint-disable-next-line no-alert
|
||||
alert(state.translate('fileTooBig', { size: bytes(MAXFILESIZE) }));
|
||||
return;
|
||||
}
|
||||
emitter.emit('upload', { file, type: 'drop' });
|
||||
|
||||
@@ -104,13 +104,16 @@ export default function(state, emitter) {
|
||||
metrics.completedUpload(ownedFile);
|
||||
|
||||
state.storage.addFile(ownedFile);
|
||||
|
||||
document.getElementById('cancel-upload').hidden = 'hidden';
|
||||
const cancelBtn = document.getElementById('cancel-upload');
|
||||
if (cancelBtn) {
|
||||
cancelBtn.hidden = 'hidden';
|
||||
}
|
||||
await delay(1000);
|
||||
await fadeOut('.page');
|
||||
openLinksInNewTab(links, false);
|
||||
emitter.emit('pushState', `/share/${ownedFile.id}`);
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(err);
|
||||
|
||||
if (err.message === '0') {
|
||||
@@ -136,6 +139,7 @@ export default function(state, emitter) {
|
||||
metrics.addedPassword({ size: file.size });
|
||||
await delay(1000);
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(err);
|
||||
state.passwordSetError = err;
|
||||
} finally {
|
||||
@@ -186,6 +190,7 @@ export default function(state, emitter) {
|
||||
state.transfer.reset();
|
||||
return render();
|
||||
}
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(err);
|
||||
state.transfer = null;
|
||||
const location = err.message === '404' ? '/404' : '/error';
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import 'fast-text-encoding'; // MS Edge support
|
||||
import 'fluent-intl-polyfill';
|
||||
import app from './routes';
|
||||
import locale from '../common/locales';
|
||||
import fileManager from './fileManager';
|
||||
import dragManager from './dragManager';
|
||||
import { canHasSend } from './utils';
|
||||
import assets from '../common/assets';
|
||||
import storage from './storage';
|
||||
import metrics from './metrics';
|
||||
import experiments from './experiments';
|
||||
@@ -30,10 +30,7 @@ app.use((state, emitter) => {
|
||||
) {
|
||||
unsupportedReason = 'outdated';
|
||||
}
|
||||
if (/edge\/\d+/i.test(navigator.userAgent)) {
|
||||
unsupportedReason = 'edge';
|
||||
}
|
||||
const ok = await canHasSend(assets.get('cryptofill.js'));
|
||||
const ok = await canHasSend();
|
||||
if (!ok) {
|
||||
unsupportedReason = /firefox/i.test(navigator.userAgent)
|
||||
? 'outdated'
|
||||
|
||||
@@ -73,7 +73,8 @@ module.exports = function(state, emit) {
|
||||
return;
|
||||
}
|
||||
if (file.size > MAXFILESIZE) {
|
||||
window.alert(state.translate('fileTooBig', { size: bytes(MAXFILESIZE) }));
|
||||
// eslint-disable-next-line no-alert
|
||||
alert(state.translate('fileTooBig', { size: bytes(MAXFILESIZE) }));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
9
app/readme.md
Normal file
9
app/readme.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Application Code
|
||||
|
||||
`app/` contains the browser code that gets bundled into `app.[hash].js`. It's got all the logic, crypto, and UI. All of it gets used in the browser, and some of it by the server for server side rendering.
|
||||
|
||||
The main entrypoint for the browser is [main.js](./main.js) and on the server [routes/index.js](./routes/index.js) gets imported by [/server/routes/pages.js](../server/routes/pages.js)
|
||||
|
||||
- `pages` contains display logic an markup for pages
|
||||
- `routes` contains route definitions and logic
|
||||
- `templates` contains ui elements smaller than pages
|
||||
@@ -35,8 +35,7 @@
|
||||
}
|
||||
|
||||
@media (max-device-width: 520px), (max-width: 520px) {
|
||||
.passwordInput {
|
||||
.passwordInput__form {
|
||||
flex-direction: column;
|
||||
width: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
27
app/utils.js
27
app/utils.js
@@ -9,10 +9,9 @@ function arrayToB64(array) {
|
||||
}
|
||||
|
||||
function b64ToArray(str) {
|
||||
str = (str + '==='.slice((str.length + 3) % 4))
|
||||
.replace(/-/g, '+')
|
||||
.replace(/_/g, '/');
|
||||
return b64.toByteArray(str);
|
||||
return b64.toByteArray(
|
||||
str + '==='.slice((str.length + 3) % 4)
|
||||
);
|
||||
}
|
||||
|
||||
function loadShim(polyfill) {
|
||||
@@ -25,7 +24,7 @@ function loadShim(polyfill) {
|
||||
});
|
||||
}
|
||||
|
||||
async function canHasSend(polyfill) {
|
||||
async function canHasSend() {
|
||||
try {
|
||||
const key = await window.crypto.subtle.generateKey(
|
||||
{
|
||||
@@ -35,7 +34,6 @@ async function canHasSend(polyfill) {
|
||||
true,
|
||||
['encrypt', 'decrypt']
|
||||
);
|
||||
|
||||
await window.crypto.subtle.encrypt(
|
||||
{
|
||||
name: 'AES-GCM',
|
||||
@@ -45,9 +43,23 @@ async function canHasSend(polyfill) {
|
||||
key,
|
||||
new ArrayBuffer(8)
|
||||
);
|
||||
await window.crypto.subtle.importKey(
|
||||
'raw',
|
||||
window.crypto.getRandomValues(new Uint8Array(16)),
|
||||
'PBKDF2',
|
||||
false,
|
||||
['deriveKey']
|
||||
);
|
||||
await window.crypto.subtle.importKey(
|
||||
'raw',
|
||||
window.crypto.getRandomValues(new Uint8Array(16)),
|
||||
'HKDF',
|
||||
false,
|
||||
['deriveKey']
|
||||
);
|
||||
return true;
|
||||
} catch (err) {
|
||||
return loadShim(polyfill);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,6 +179,7 @@ module.exports = {
|
||||
copyToClipboard,
|
||||
arrayToB64,
|
||||
b64ToArray,
|
||||
loadShim,
|
||||
canHasSend,
|
||||
isFile,
|
||||
openLinksInNewTab
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,4 +1,5 @@
|
||||
last 2 chrome versions
|
||||
last 2 firefox versions
|
||||
last 2 safari versions
|
||||
last 2 edge versions
|
||||
firefox esr
|
||||
safari > 9
|
||||
|
||||
@@ -42,20 +42,22 @@ if (typeof window === 'undefined') {
|
||||
require('babel-polyfill');
|
||||
var fluent = require('fluent/compat');
|
||||
}
|
||||
var fluentContext = new fluent.MessageContext('${locale}', {useIsolating: false});
|
||||
fluentContext._messages = new Map(${toJSON(merged)});
|
||||
function translate(id, data) {
|
||||
var msg = fluentContext.getMessage(id);
|
||||
if (typeof(msg) !== 'string' && !msg.val && msg.attrs) {
|
||||
msg = msg.attrs.title || msg.attrs.alt
|
||||
(function () {
|
||||
var ctx = new fluent.MessageContext('${locale}', {useIsolating: false});
|
||||
ctx._messages = new Map(${toJSON(merged)});
|
||||
function translate(id, data) {
|
||||
var msg = ctx.getMessage(id);
|
||||
if (typeof(msg) !== 'string' && !msg.val && msg.attrs) {
|
||||
msg = msg.attrs.title || msg.attrs.alt
|
||||
}
|
||||
return ctx.format(msg, data);
|
||||
}
|
||||
return fluentContext.format(msg, data);
|
||||
}
|
||||
if (typeof window === 'undefined') {
|
||||
module.exports = translate;
|
||||
}
|
||||
else {
|
||||
window.translate = translate;
|
||||
}
|
||||
if (typeof window === 'undefined') {
|
||||
module.exports = translate;
|
||||
}
|
||||
else {
|
||||
window.translate = translate;
|
||||
}
|
||||
})();
|
||||
\``;
|
||||
};
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
/*
|
||||
This code is included by both the server and frontend via
|
||||
common/assets.js
|
||||
|
||||
When included from the server the export will be the function.
|
||||
|
||||
When included from the frontend (via webpack) the export will
|
||||
be an object mapping file names to hashed file names. Example:
|
||||
"send_logo.svg": "send_logo.5fcfdf0e.svg"
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
@@ -14,6 +25,6 @@ module.exports = function() {
|
||||
return {
|
||||
code,
|
||||
dependencies: files.map(f => require.resolve('../assets/' + f)),
|
||||
cacheable: false
|
||||
cacheable: true
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
/*
|
||||
This code is included by both the server and frontend via
|
||||
common/locales.js
|
||||
|
||||
When included from the server the export will be the function.
|
||||
|
||||
When included from the frontend (via webpack) the export will
|
||||
be an object mapping ftl files to js files. Example:
|
||||
"public/locales/en-US/send.ftl":"public/locales/en-US/send.6b4f8354.js"
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
@@ -17,6 +28,6 @@ module.exports = function() {
|
||||
dependencies: dirs.map(d =>
|
||||
require.resolve(`../public/locales/${d}/send.ftl`)
|
||||
),
|
||||
cacheable: false
|
||||
cacheable: true
|
||||
};
|
||||
};
|
||||
|
||||
26
build/readme.md
Normal file
26
build/readme.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Custom Loaders
|
||||
|
||||
## Fluent Loader
|
||||
|
||||
The fluent loader "compiles" `.ftl` files into `.js` files directly usable by both the frontend and server for localization.
|
||||
|
||||
## Generate Asset Map
|
||||
|
||||
This loader enumerates all the files in `assets/` so that `common/assets.js` can provide mappings from the source filename to the hashed filename used on the site.
|
||||
|
||||
## Generate L10N Map
|
||||
|
||||
This loader enumerates all the ftl files in `public/locales` so that the fluent loader can create it's js files.
|
||||
|
||||
## Package.json Loader
|
||||
|
||||
This loader creates a `version.json` file that gets exposed by the `/__version__` route from the `package.json` file and current git commit hash.
|
||||
|
||||
## Version Loader
|
||||
|
||||
This loader substitutes the string "VERSION" for the version string specified in `package.json`. This is a workaround because `package.json` already uses the `package_json_loader`. See [app/templates/header/index.js](../app/templates/header/index.js) for more info.
|
||||
|
||||
# See Also
|
||||
|
||||
- [docs/build.md](../docs/build.md)
|
||||
- [webpack.config.js](../webpack.config.js)
|
||||
@@ -4,7 +4,7 @@ const isServer = typeof gen === 'function';
|
||||
const prefix = isServer ? '/' : '';
|
||||
let manifest = {};
|
||||
try {
|
||||
//eslint-disable-next-line node/no-missing-require
|
||||
// eslint-disable-next-line node/no-missing-require
|
||||
manifest = require('../dist/manifest.json');
|
||||
} catch (e) {
|
||||
// use middleware
|
||||
@@ -17,6 +17,7 @@ function getLocale(name) {
|
||||
}
|
||||
|
||||
function serverTranslator(name) {
|
||||
// eslint-disable-next-line security/detect-non-literal-require
|
||||
return require(`../dist/${locales[`public/locales/${name}/send.ftl`]}`);
|
||||
}
|
||||
|
||||
|
||||
3
common/readme.md
Normal file
3
common/readme.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Common Code
|
||||
|
||||
This directory contains code loaded by both the frontend `app` and backend `server`. The code here can be challenging to understand at first because the contexts for the two (three counting the dev server) environments that include them are quite different, but the purpose of these modules are quite simple, to provide mappings from the source assets (`copy-16.png`) to the concrete production assets (`copy-16.db66e0bf.svg`), similarly for localizations.
|
||||
22
docs/build.md
Normal file
22
docs/build.md
Normal file
@@ -0,0 +1,22 @@
|
||||
Send has two build configurations, development and production. Both can be run via `npm` scripts, `npm start` for development and `npm run build` for production. Webpack is our only build tool and all configuration lives in [webpack.config.js](../webpack.config.js).
|
||||
|
||||
# Development
|
||||
|
||||
`npm start` launches a `webpack-dev-server` on port 8080 that compiles the assets and watches files for changes. It also serves the backend API and frontend unit tests via the `server/dev.js` entrypoint. The frontend tests can be run in the browser by navigating to http://localhost:8080/test and will rerun automatically as the watched files are saved with changes.
|
||||
|
||||
# Production
|
||||
|
||||
`npm run build` compiles the assets and writes the files to the `dist/` directory. `npm run prod` launches an Express server on port 1443 that serves the backend API and frontend static assets from `dist/` via the `server/prod.js` entrypoint.
|
||||
|
||||
# Notable differences
|
||||
|
||||
- Development compiles assets in memory, so no `dist/` directory is generated
|
||||
- Development does not enable CSP headers
|
||||
- Development frontend source is instrumented for code coverage
|
||||
- Only development includes sourcemaps
|
||||
- Only development exposes the `/test` route
|
||||
- Production sets Cache-Control immutable headers on the hashed static assets
|
||||
|
||||
# Custom Loaders
|
||||
|
||||
The `build/` directory contains custom webpack loaders specific to Send. See [build/readme.md](../build/readme.md) for details on each loader.
|
||||
46
docs/encryption.md
Normal file
46
docs/encryption.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# File Encryption
|
||||
|
||||
Send use 128-bit AES-GCM encryption via the [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API) to encrypt files in the browser before uploading them to the server. The code is in [app/keychain.js](../app/keychain.js).
|
||||
|
||||
## Steps
|
||||
|
||||
### Uploading
|
||||
|
||||
1. A new secret key is generated with `crypto.getRandomValues`
|
||||
2. The secret key is used to derive 3 more keys via HKDF SHA-256
|
||||
- an encryption key for the file (AES-GCM)
|
||||
- an encryption key for the file metadata (AES-GCM)
|
||||
- a signing key for request authentication (HMAC SHA-256)
|
||||
3. The file and metadata are encrypted with their corresponding keys
|
||||
4. The encrypted data and signing key are uploaded to the server
|
||||
5. An owner token and the share url are returned by the server and stored in local storage
|
||||
6. The secret key is appended to the share url as a [#fragment](https://en.wikipedia.org/wiki/Fragment_identifier) and presented to the UI
|
||||
|
||||
### Downloading
|
||||
|
||||
1. The browser loads the share url page, which includes an authentication nonce
|
||||
2. The browser imports the secret key from the url fragment
|
||||
3. The same 3 keys as above are derived
|
||||
4. The browser signs the nonce with it's signing key and requests the metadata
|
||||
5. The encrypted metadata is decrypted and presented on the page
|
||||
6. The browser makes another authenticated request to download the encrypted file
|
||||
7. The browser downloads and decrypts the file
|
||||
8. The file prompts the save dialog or automatically saves depending on the browser settings
|
||||
|
||||
### Passwords
|
||||
|
||||
A password may optionally be set to authenticate the download request. When a password is set the following steps occur.
|
||||
|
||||
#### Sender
|
||||
|
||||
1. The original signing key derived from the secret key is discarded
|
||||
2. A new signing key is generated via PBKDF2 from the user entered password and the full share url (including secret key fragment)
|
||||
3. The new key is sent to the server, authenticated by the owner token
|
||||
4. The server stores the new key and marks the record as needing a password
|
||||
|
||||
#### Downloader
|
||||
|
||||
1. The browser loads the share url page, which includes an authentication nonce and indicator that the file requires a password
|
||||
2. The user is prompted for the password and the signing key is derived
|
||||
3. The browser requests the metadata using the key to sign the nonce
|
||||
4. If the password was correct the metadata is returned, otherwise a 401
|
||||
84
docs/experiments.md
Normal file
84
docs/experiments.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# A/B experiment testing
|
||||
|
||||
We're using Google Analytics Experiments for A/B testing.
|
||||
|
||||
## Creating an experiment
|
||||
|
||||
Navigate to the Behavior > Experiments section of Google Analytics and click the "Create experiment" button.
|
||||
|
||||
The "Objective for this experiment" is the most complicated part. See the "Promo click (Goal ID 4 / Goal Set 1)" for an example.
|
||||
|
||||
In step 2 add as many variants as you plan to test. The urls are not important since we aren't using their js library to choose the variants. The name will show up in the report so choose good ones. "Original page" becomes variant 0 and each variant increments by one. We'll use the numbers in our `app/experiments.js` code.
|
||||
|
||||
Step 3 contains some script that we'll ignore. The important thing here is the **Experiment ID**. This is the value we need to name our experiment in `app/experiments.js`. Save the changes so far and wait until the code containing the experiment has been deployed to production **before** starting the experiment.
|
||||
|
||||
## Experiment code
|
||||
|
||||
Code for experiments live in [app/experiments.js](../app/experiments.js). There's an `experiments` object that contains the logic for deciding whether an experiment should run, which variant to use, and what to do. Each object needs to have these functions:
|
||||
|
||||
### `eligible` function
|
||||
|
||||
This function returns a boolean of whether this experiment should be active for this session. Any data available to the page can be used determine the result.
|
||||
|
||||
### `variant` function
|
||||
|
||||
This function returns which experimental group this session is placed in. The variant values need to match the values set up in Google Analytics, usually 0 thru N-1. This value is usually picked at random based on what percentage of each variant is desired.
|
||||
|
||||
### `run` function
|
||||
|
||||
This function gets the `variant` value chosen by the variant function and the `state` and `emitter` objects from the app. This function can do anything needed to change the app based on the experiment. A common pattern is to set or change a value on `state` that will be picked up by other parts of the app, like ui templates, to change how it looks or behaves.
|
||||
|
||||
### Example
|
||||
|
||||
Here's a full example of the experiment object:
|
||||
|
||||
```js
|
||||
const experiments = {
|
||||
S9wqVl2SQ4ab2yZtqDI3Dw: { // The Experiment ID from Google Analytics
|
||||
id: 'S9wqVl2SQ4ab2yZtqDI3Dw',
|
||||
run: function(variant, state, emitter) {
|
||||
switch (variant) {
|
||||
case 1:
|
||||
state.promo = 'blue';
|
||||
break;
|
||||
case 2:
|
||||
state.promo = 'pink';
|
||||
break;
|
||||
default:
|
||||
state.promo = 'grey';
|
||||
}
|
||||
emitter.emit('render');
|
||||
},
|
||||
eligible: function() {
|
||||
return (
|
||||
!/firefox|fxios/i.test(navigator.userAgent) &&
|
||||
document.querySelector('html').lang === 'en-US'
|
||||
);
|
||||
},
|
||||
variant: function(state) {
|
||||
const n = this.luckyNumber(state);
|
||||
if (n < 0.33) {
|
||||
return 0;
|
||||
}
|
||||
return n < 0.66 ? 1 : 2;
|
||||
},
|
||||
luckyNumber: function(state) {
|
||||
return luckyNumber(
|
||||
`${this.id}:${state.storage.get('testpilot_ga__cid')}`
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Reporting results
|
||||
|
||||
All metrics pings will include the variant and experiment id, but it's usually important to trigger a specific event to be counted as the experiment goal (the "Objective for this experiment" part from setup). Use an 'experiment' event to do this. For example:
|
||||
|
||||
```js
|
||||
emit('experiment', { cd3: 'promo' });
|
||||
```
|
||||
|
||||
where `emit` is the app emitter function passed to the [route handler](https://github.com/choojs/choo#approuteroutename-handlerstate-emit)
|
||||
|
||||
The second argument can be an object with any additional parameters. It usually includes a custom dimension that we chose to filter on while creating the experiment in Google Analytics.
|
||||
29
docs/localization.md
Normal file
29
docs/localization.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Localization
|
||||
|
||||
Send is localized in over 50 languages. We use the [fluent](http://projectfluent.org/) library and store our translations in [FTL](http://projectfluent.org/fluent/guide/) files in `public/locales/`. `en-US` is our base language, and other languages are managed by [pontoon](https://pontoon.mozilla.org/projects/test-pilot-firefox-send/).
|
||||
|
||||
## Process
|
||||
|
||||
Strings are added or removed from [public/locales/en-US/send.ftl] as needed. Strings **MUST NOT** be *changed* after they've been commited and pushed to master. Changing a string requires creating a new ID with a new name (preferably descriptive instead of incremented) and deletion of the obsolete ID. It's often useful to add a comment above the string with info about how and where the string is used.
|
||||
|
||||
Once new strings are commited to master they are available for translators in Pontoon. All languages other than `en-US` should be edited via Pontoon. Translations get automatically commited to the github master branch.
|
||||
|
||||
### Activation
|
||||
|
||||
The development environment includes all locales in `public/locales` via the `L10N_DEV` environment variable. Production uses `package.json` as the list of locales to use. Once a locale has enough string coverage it should be added to `package.json`.
|
||||
|
||||
## Code
|
||||
|
||||
In `app/` we use the `state.translate()` function to translate strings to the best matching language base on the user's `Accept-Language` header. It's a wrapper around fluent's [MessageContext.format](http://projectfluent.org/fluent.js/fluent/MessageContext.html). It works the same for both server and client side rendering.
|
||||
|
||||
### Examples
|
||||
|
||||
```js
|
||||
// simple string
|
||||
const finishedString = state.translate('downloadFinish')
|
||||
// with parameters
|
||||
const progressString = state.translate('downloadingPageProgress', {
|
||||
filename: state.fileInfo.name,
|
||||
size: bytes(state.fileInfo.size)
|
||||
})
|
||||
```
|
||||
324
package-lock.json
generated
324
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "firefox-send",
|
||||
"version": "2.4.1",
|
||||
"version": "2.5.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -417,6 +417,12 @@
|
||||
"integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
|
||||
"dev": true
|
||||
},
|
||||
"asmcrypto.js": {
|
||||
"version": "0.22.0",
|
||||
"resolved": "https://registry.npmjs.org/asmcrypto.js/-/asmcrypto.js-0.22.0.tgz",
|
||||
"integrity": "sha512-usgMoyXjMbx/ZPdzTSXExhMPur2FTdz/Vo5PVx2gIaBcdAAJNOFlsdgqveM8Cff7W0v+xrf9BwjOV26JSAF9qA==",
|
||||
"dev": true
|
||||
},
|
||||
"asn1": {
|
||||
"version": "0.1.11",
|
||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz",
|
||||
@@ -521,9 +527,9 @@
|
||||
}
|
||||
},
|
||||
"aws-sdk": {
|
||||
"version": "2.202.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.202.0.tgz",
|
||||
"integrity": "sha1-wb2QwdwL/oNVIv6B8pz4u3G/AMw=",
|
||||
"version": "2.206.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.206.0.tgz",
|
||||
"integrity": "sha1-I1yhCVdsiI8kZ9HBDDOvfjQtO+E=",
|
||||
"requires": {
|
||||
"buffer": "4.9.1",
|
||||
"events": "1.1.1",
|
||||
@@ -769,9 +775,9 @@
|
||||
}
|
||||
},
|
||||
"babel-loader": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-7.1.3.tgz",
|
||||
"integrity": "sha512-PeN29YvOynPMvNk7QCzsHqxpmfXwKAC+uxkiSNFQsmXBBVltzEkVWmv/Ip3tx7yk149dQUwk497bTXNu+DZjLA==",
|
||||
"version": "7.1.4",
|
||||
"resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-7.1.4.tgz",
|
||||
"integrity": "sha512-/hbyEvPzBJuGpk9o80R0ZyTej6heEOr59GoEUtn8qFKbnx4cJm9FWES6J/iv644sYgrtVw9JJQkjaLW/bqb5gw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"find-cache-dir": "1.0.0",
|
||||
@@ -1442,9 +1448,9 @@
|
||||
}
|
||||
},
|
||||
"bel": {
|
||||
"version": "5.1.6",
|
||||
"resolved": "https://registry.npmjs.org/bel/-/bel-5.1.6.tgz",
|
||||
"integrity": "sha512-qEjYYQgbMFbBwtrt+gVBk+IzdBNqyCgWjm7jtBqptcFlwaXRG/HgyHuzUucReYy155TB9UvIeF4P6/J2GpX80A==",
|
||||
"version": "5.1.7",
|
||||
"resolved": "https://registry.npmjs.org/bel/-/bel-5.1.7.tgz",
|
||||
"integrity": "sha512-f3aAzoq2ilK3ErDsv47uo2qK9NTKHkguI7j6sJi6ynKS9vPG180QGU8BzHas6xPnIsKj+m7YXlDSm9BmaZHNrg==",
|
||||
"requires": {
|
||||
"hyperx": "2.3.3",
|
||||
"is-electron": "2.1.0",
|
||||
@@ -1617,9 +1623,9 @@
|
||||
}
|
||||
},
|
||||
"browser-stdout": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz",
|
||||
"integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=",
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
|
||||
"integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
|
||||
"dev": true
|
||||
},
|
||||
"browserify-aes": {
|
||||
@@ -2002,11 +2008,11 @@
|
||||
}
|
||||
},
|
||||
"choo": {
|
||||
"version": "6.8.0",
|
||||
"resolved": "https://registry.npmjs.org/choo/-/choo-6.8.0.tgz",
|
||||
"integrity": "sha512-yQkUqeqNtHdSeRik4yjPYIC0JRghdvG0AXxGGkqEgLqqOTCVVK8Fnu/x5FKOQ+CEGfodxIrWLIEHpx1AotmCGg==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/choo/-/choo-6.10.0.tgz",
|
||||
"integrity": "sha512-5dSzBXh1PeyzKgzTTVMiIyO+2J/THwft/O65WDBnmMwwM2AveM2iDrHMcdzMm1Pr4U+44+hqAprBJbPigPYy5Q==",
|
||||
"requires": {
|
||||
"bel": "5.1.6",
|
||||
"bel": "5.1.7",
|
||||
"document-ready": "2.0.1",
|
||||
"nanobus": "4.3.3",
|
||||
"nanohref": "3.0.1",
|
||||
@@ -2601,33 +2607,19 @@
|
||||
"dev": true
|
||||
},
|
||||
"copy-webpack-plugin": {
|
||||
"version": "4.4.2",
|
||||
"resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.4.2.tgz",
|
||||
"integrity": "sha512-tf1XKKQ5h+BPvXJ5/zx2xKVdF0/6J8XNvhB6fdmIReMnAfQGMbzph8F7ok2QF9kqWMfIgkCxwzk1zXkYqcLIqg==",
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.5.0.tgz",
|
||||
"integrity": "sha512-ROQ85fWKuhJfUkBTdHvfV+Zv6Ltm3G/vPVFdLPFwzWzd9RUY1yLw3rt6FmKK2PaeNQCNvmwgFhuarkjuV4PVDQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cacache": "10.0.4",
|
||||
"find-cache-dir": "1.0.0",
|
||||
"globby": "7.1.1",
|
||||
"is-glob": "4.0.0",
|
||||
"loader-utils": "0.2.17",
|
||||
"loader-utils": "1.1.0",
|
||||
"minimatch": "3.0.4",
|
||||
"p-limit": "1.2.0",
|
||||
"serialize-javascript": "1.4.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"loader-utils": {
|
||||
"version": "0.2.17",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz",
|
||||
"integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"big.js": "3.2.0",
|
||||
"emojis-list": "2.1.0",
|
||||
"json5": "0.5.1",
|
||||
"object-assign": "4.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"core-js": {
|
||||
@@ -3658,21 +3650,21 @@
|
||||
}
|
||||
},
|
||||
"duplexify": {
|
||||
"version": "3.5.3",
|
||||
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.3.tgz",
|
||||
"integrity": "sha512-g8ID9OroF9hKt2POf8YLayy+9594PzmM3scI00/uBXocX3TWNgoB67hjzkFe9ITAbQOne/lLdBxHXvYUM4ZgGA==",
|
||||
"version": "3.5.4",
|
||||
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.4.tgz",
|
||||
"integrity": "sha512-JzYSLYMhoVVBe8+mbHQ4KgpvHpm0DZpJuL8PY93Vyv1fW7jYJ90LoXa1di/CVbJM+TgMs91rbDapE/RNIfnJsA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"end-of-stream": "1.4.1",
|
||||
"inherits": "2.0.3",
|
||||
"readable-stream": "2.3.4",
|
||||
"readable-stream": "2.3.5",
|
||||
"stream-shift": "1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"readable-stream": {
|
||||
"version": "2.3.4",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.4.tgz",
|
||||
"integrity": "sha512-vuYxeWYM+fde14+rajzqgeohAI7YoJcHE7kXDAc4Nk0EbuKnJfqtY9YtRkLo/tqkuF7MsBQRhPnPeyjYITp3ZQ==",
|
||||
"version": "2.3.5",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.5.tgz",
|
||||
"integrity": "sha512-tK0yDhrkygt/knjowCUiWP9YdV7c5R+8cR0r/kt9ZhBU906Fs6RpQJCEilamRJj1Nx2rWI6LkW9gKqjTkshhEw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"core-util-is": "1.0.2",
|
||||
@@ -3956,21 +3948,21 @@
|
||||
}
|
||||
},
|
||||
"eslint": {
|
||||
"version": "4.18.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-4.18.1.tgz",
|
||||
"integrity": "sha512-gPSfpSRCHre1GLxGmO68tZNxOlL2y7xBd95VcLD+Eo4S2js31YoMum3CAQIOaxY24hqYOMksMvW38xuuWKQTgw==",
|
||||
"version": "4.18.2",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-4.18.2.tgz",
|
||||
"integrity": "sha512-qy4i3wODqKMYfz9LUI8N2qYDkHkoieTbiHpMrYUI/WbjhXJQr7lI4VngixTgaG+yHX+NBCv7nW4hA0ShbvaNKw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ajv": "5.5.2",
|
||||
"babel-code-frame": "6.26.0",
|
||||
"chalk": "2.3.1",
|
||||
"chalk": "2.3.2",
|
||||
"concat-stream": "1.6.0",
|
||||
"cross-spawn": "5.1.0",
|
||||
"debug": "3.1.0",
|
||||
"doctrine": "2.1.0",
|
||||
"eslint-scope": "3.7.1",
|
||||
"eslint-visitor-keys": "1.0.0",
|
||||
"espree": "3.5.3",
|
||||
"espree": "3.5.4",
|
||||
"esquery": "1.0.0",
|
||||
"esutils": "2.0.2",
|
||||
"file-entry-cache": "2.0.0",
|
||||
@@ -3981,7 +3973,7 @@
|
||||
"imurmurhash": "0.1.4",
|
||||
"inquirer": "3.3.0",
|
||||
"is-resolvable": "1.1.0",
|
||||
"js-yaml": "3.10.0",
|
||||
"js-yaml": "3.11.0",
|
||||
"json-stable-stringify-without-jsonify": "1.0.1",
|
||||
"levn": "0.3.0",
|
||||
"lodash": "4.17.5",
|
||||
@@ -3996,10 +3988,16 @@
|
||||
"semver": "5.5.0",
|
||||
"strip-ansi": "4.0.0",
|
||||
"strip-json-comments": "2.0.1",
|
||||
"table": "4.0.3",
|
||||
"table": "4.0.2",
|
||||
"text-table": "0.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv-keywords": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz",
|
||||
"integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
|
||||
@@ -4007,23 +4005,23 @@
|
||||
"dev": true
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz",
|
||||
"integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==",
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "1.9.1"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz",
|
||||
"integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==",
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.2.tgz",
|
||||
"integrity": "sha512-ZM4j2/ld/YZDc3Ma8PgN7gyAk+kHMMMyzLNryCPGhWrsfAuDVeuid5bpRFTDgMH9JBK2lA4dyyAkkZYF/WcqDQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "3.2.0",
|
||||
"ansi-styles": "3.2.1",
|
||||
"escape-string-regexp": "1.0.5",
|
||||
"supports-color": "5.2.0"
|
||||
"supports-color": "5.3.0"
|
||||
}
|
||||
},
|
||||
"debug": {
|
||||
@@ -4054,9 +4052,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"js-yaml": {
|
||||
"version": "3.10.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz",
|
||||
"integrity": "sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA==",
|
||||
"version": "3.11.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.11.0.tgz",
|
||||
"integrity": "sha512-saJstZWv7oNeOyBh3+Dx1qWzhW0+e6/8eDzo7p5rDFqxntSztloLtuKu+Ejhtq82jsilwOIZYsCz+lIjthg1Hw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"argparse": "1.0.10",
|
||||
@@ -4073,23 +4071,37 @@
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz",
|
||||
"integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==",
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.3.0.tgz",
|
||||
"integrity": "sha512-0aP01LLIskjKs3lq52EC0aGBAJhLq7B2Rd8HC/DR/PtNNpcLilNmHC12O+hu0usQpo7wtHNRqtrhBwtDb0+dNg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "3.0.0"
|
||||
}
|
||||
},
|
||||
"table": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz",
|
||||
"integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ajv": "5.5.2",
|
||||
"ajv-keywords": "2.1.1",
|
||||
"chalk": "2.3.2",
|
||||
"lodash": "4.17.5",
|
||||
"slice-ansi": "1.0.0",
|
||||
"string-width": "2.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"eslint-plugin-mocha": {
|
||||
"version": "4.11.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-4.11.0.tgz",
|
||||
"integrity": "sha1-kRk6L1XiCl41l0BUoAidMBmO5Xg=",
|
||||
"version": "4.12.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-4.12.1.tgz",
|
||||
"integrity": "sha512-hxWtYHvLA0p/PKymRfDYh9Mxt5dYkg2Goy1vZDarTEEYfELP9ksga7kKG1NUKSQy27C8Qjc7YrSWTLUhOEOksA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ramda": "0.24.1"
|
||||
"ramda": "0.25.0"
|
||||
}
|
||||
},
|
||||
"eslint-plugin-node": {
|
||||
@@ -4130,13 +4142,21 @@
|
||||
"dev": true
|
||||
},
|
||||
"espree": {
|
||||
"version": "3.5.3",
|
||||
"resolved": "https://registry.npmjs.org/espree/-/espree-3.5.3.tgz",
|
||||
"integrity": "sha512-Zy3tAJDORxQZLl2baguiRU1syPERAIg0L+JB2MWorORgTu/CplzvxS9WWA7Xh4+Q+eOQihNs/1o1Xep8cvCxWQ==",
|
||||
"version": "3.5.4",
|
||||
"resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz",
|
||||
"integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"acorn": "5.4.1",
|
||||
"acorn": "5.5.3",
|
||||
"acorn-jsx": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"acorn": {
|
||||
"version": "5.5.3",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz",
|
||||
"integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"esprima": {
|
||||
@@ -4501,6 +4521,12 @@
|
||||
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
|
||||
"dev": true
|
||||
},
|
||||
"fast-text-encoding": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.0.tgz",
|
||||
"integrity": "sha512-R9bHCvweUxxwkDwhjav5vxpFvdPGlVngtqmx4pIZfSUhM/Q4NiIUHB456BAf+Q1Nwu3HEZYONtu+Rya+af4jiQ==",
|
||||
"dev": true
|
||||
},
|
||||
"fastparse": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.1.tgz",
|
||||
@@ -4545,9 +4571,9 @@
|
||||
}
|
||||
},
|
||||
"file-loader": {
|
||||
"version": "1.1.9",
|
||||
"resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.9.tgz",
|
||||
"integrity": "sha512-6ql03hOSoJHBkTB+3De/f7NJse+JXkUwvAf3y4Q5rIcTD0kqJiE3btvLnDcZT+P4t1QYLb9dJ9EI4auzfo3wFA==",
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz",
|
||||
"integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"loader-utils": "1.1.0",
|
||||
@@ -4555,9 +4581,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv": {
|
||||
"version": "6.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.1.1.tgz",
|
||||
"integrity": "sha1-l41Zf7wrfQ5aXD3esUmmgvKr+g4=",
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.2.1.tgz",
|
||||
"integrity": "sha1-KKarxJOiq+D7TIUHrK7bQ/pVBnE=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "1.0.0",
|
||||
@@ -4571,7 +4597,7 @@
|
||||
"integrity": "sha512-yYrjb9TX2k/J1Y5UNy3KYdZq10xhYcF8nMpAW6o3hy6Q8WSIEf9lJHG/ePnOBfziPM3fvQwfOwa13U/Fh8qTfA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ajv": "6.1.1",
|
||||
"ajv": "6.2.1",
|
||||
"ajv-keywords": "3.1.0"
|
||||
}
|
||||
}
|
||||
@@ -4705,13 +4731,13 @@
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"inherits": "2.0.3",
|
||||
"readable-stream": "2.3.4"
|
||||
"readable-stream": "2.3.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"readable-stream": {
|
||||
"version": "2.3.4",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.4.tgz",
|
||||
"integrity": "sha512-vuYxeWYM+fde14+rajzqgeohAI7YoJcHE7kXDAc4Nk0EbuKnJfqtY9YtRkLo/tqkuF7MsBQRhPnPeyjYITp3ZQ==",
|
||||
"version": "2.3.5",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.5.tgz",
|
||||
"integrity": "sha512-tK0yDhrkygt/knjowCUiWP9YdV7c5R+8cR0r/kt9ZhBU906Fs6RpQJCEilamRJj1Nx2rWI6LkW9gKqjTkshhEw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"core-util-is": "1.0.2",
|
||||
@@ -4823,13 +4849,13 @@
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"inherits": "2.0.3",
|
||||
"readable-stream": "2.3.4"
|
||||
"readable-stream": "2.3.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"readable-stream": {
|
||||
"version": "2.3.4",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.4.tgz",
|
||||
"integrity": "sha512-vuYxeWYM+fde14+rajzqgeohAI7YoJcHE7kXDAc4Nk0EbuKnJfqtY9YtRkLo/tqkuF7MsBQRhPnPeyjYITp3ZQ==",
|
||||
"version": "2.3.5",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.5.tgz",
|
||||
"integrity": "sha512-tK0yDhrkygt/knjowCUiWP9YdV7c5R+8cR0r/kt9ZhBU906Fs6RpQJCEilamRJj1Nx2rWI6LkW9gKqjTkshhEw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"core-util-is": "1.0.2",
|
||||
@@ -6209,9 +6235,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"helmet": {
|
||||
"version": "3.11.0",
|
||||
"resolved": "https://registry.npmjs.org/helmet/-/helmet-3.11.0.tgz",
|
||||
"integrity": "sha512-Xqf6VXmjpZoyH4reGyeBCO5nHH0NVeRQnx23LFj6AK9ocPRgZJfSH6zZ8SvNO2tB+fKhsqy1RSjIjWHVvH1X+w==",
|
||||
"version": "3.12.0",
|
||||
"resolved": "https://registry.npmjs.org/helmet/-/helmet-3.12.0.tgz",
|
||||
"integrity": "sha512-CgkctpvreQLL6X3EL2Igs/92+75ZFIsrob9/Rdwf2hQCBGH/DxLk4xFPxAAl6jYnnus/YXfFEVXHEJf8TJTwlA==",
|
||||
"requires": {
|
||||
"dns-prefetch-control": "0.1.0",
|
||||
"dont-sniff-mimetype": "1.0.0",
|
||||
@@ -6224,7 +6250,7 @@
|
||||
"ienoopen": "1.0.0",
|
||||
"nocache": "2.0.0",
|
||||
"referrer-policy": "1.1.0",
|
||||
"x-xss-protection": "1.0.0"
|
||||
"x-xss-protection": "1.1.0"
|
||||
}
|
||||
},
|
||||
"helmet-csp": {
|
||||
@@ -9647,7 +9673,7 @@
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"concat-stream": "1.6.0",
|
||||
"duplexify": "3.5.3",
|
||||
"duplexify": "3.5.4",
|
||||
"end-of-stream": "1.4.1",
|
||||
"flush-write-stream": "1.0.2",
|
||||
"from2": "2.3.0",
|
||||
@@ -9688,15 +9714,15 @@
|
||||
}
|
||||
},
|
||||
"mocha": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mocha/-/mocha-5.0.1.tgz",
|
||||
"integrity": "sha512-SpwyojlnE/WRBNGtvJSNfllfm5PqEDFxcWluSIgLeSBJtXG4DmoX2NNAeEA7rP5kK+79VgtVq8nG6HskaL1ykg==",
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/mocha/-/mocha-5.0.4.tgz",
|
||||
"integrity": "sha512-nMOpAPFosU1B4Ix1jdhx5e3q7XO55ic5a8cgYvW27CequcEY+BabS0kUVL1Cw1V5PuVHZWeNRWFLmEPexo79VA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"browser-stdout": "1.3.0",
|
||||
"browser-stdout": "1.3.1",
|
||||
"commander": "2.11.0",
|
||||
"debug": "3.1.0",
|
||||
"diff": "3.3.1",
|
||||
"diff": "3.5.0",
|
||||
"escape-string-regexp": "1.0.5",
|
||||
"glob": "7.1.2",
|
||||
"growl": "1.10.3",
|
||||
@@ -9720,6 +9746,12 @@
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"diff": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
|
||||
"integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
|
||||
"dev": true
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz",
|
||||
@@ -12305,13 +12337,13 @@
|
||||
"requires": {
|
||||
"cyclist": "0.2.2",
|
||||
"inherits": "2.0.3",
|
||||
"readable-stream": "2.3.4"
|
||||
"readable-stream": "2.3.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"readable-stream": {
|
||||
"version": "2.3.4",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.4.tgz",
|
||||
"integrity": "sha512-vuYxeWYM+fde14+rajzqgeohAI7YoJcHE7kXDAc4Nk0EbuKnJfqtY9YtRkLo/tqkuF7MsBQRhPnPeyjYITp3ZQ==",
|
||||
"version": "2.3.5",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.5.tgz",
|
||||
"integrity": "sha512-tK0yDhrkygt/knjowCUiWP9YdV7c5R+8cR0r/kt9ZhBU906Fs6RpQJCEilamRJj1Nx2rWI6LkW9gKqjTkshhEw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"core-util-is": "1.0.2",
|
||||
@@ -14389,21 +14421,21 @@
|
||||
}
|
||||
},
|
||||
"postcss-loader": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-2.1.0.tgz",
|
||||
"integrity": "sha512-S/dKzpDwGFmP9g8eyCu9sUIV+/+3UooeTpYlsKf23qKDdrhHuA4pTSfytVu0rEJ0iDqUavXrgtOPq5KhNyNMOw==",
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-2.1.1.tgz",
|
||||
"integrity": "sha512-f0J/DWE/hyO9/LH0WHpXkny/ZZ238sSaG3p1SRBtVZnFWUtD7GXIEgHoBg8cnAeRbmEvUxHQptY46zWfwNYj/w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"loader-utils": "1.1.0",
|
||||
"postcss": "6.0.18",
|
||||
"postcss": "6.0.19",
|
||||
"postcss-load-config": "1.2.0",
|
||||
"schema-utils": "0.4.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv": {
|
||||
"version": "6.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.1.1.tgz",
|
||||
"integrity": "sha1-l41Zf7wrfQ5aXD3esUmmgvKr+g4=",
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.2.1.tgz",
|
||||
"integrity": "sha1-KKarxJOiq+D7TIUHrK7bQ/pVBnE=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "1.0.0",
|
||||
@@ -14411,30 +14443,24 @@
|
||||
"json-schema-traverse": "0.3.1"
|
||||
}
|
||||
},
|
||||
"ajv-keywords": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.1.0.tgz",
|
||||
"integrity": "sha1-rCsnk5xUPpXSwG5/f1wnvkqlQ74=",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz",
|
||||
"integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==",
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "1.9.1"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz",
|
||||
"integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==",
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.2.tgz",
|
||||
"integrity": "sha512-ZM4j2/ld/YZDc3Ma8PgN7gyAk+kHMMMyzLNryCPGhWrsfAuDVeuid5bpRFTDgMH9JBK2lA4dyyAkkZYF/WcqDQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "3.2.0",
|
||||
"ansi-styles": "3.2.1",
|
||||
"escape-string-regexp": "1.0.5",
|
||||
"supports-color": "5.2.0"
|
||||
"supports-color": "5.3.0"
|
||||
}
|
||||
},
|
||||
"has-flag": {
|
||||
@@ -14444,14 +14470,14 @@
|
||||
"dev": true
|
||||
},
|
||||
"postcss": {
|
||||
"version": "6.0.18",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.18.tgz",
|
||||
"integrity": "sha512-X8MyLi3OYI1o71u0SsefWLpGBo5xnGiK1Pn+nrZFplc671Ts7L8aPwEbPIO8AWpulK5wuaVzyM9Rw6R8o7hYBw==",
|
||||
"version": "6.0.19",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.19.tgz",
|
||||
"integrity": "sha512-f13HRz0HtVwVaEuW6J6cOUCBLFtymhgyLPV7t4QEk2UD3twRI9IluDcQNdzQdBpiixkXj2OmzejhhTbSbDxNTg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "2.3.1",
|
||||
"chalk": "2.3.2",
|
||||
"source-map": "0.6.1",
|
||||
"supports-color": "5.2.0"
|
||||
"supports-color": "5.3.0"
|
||||
}
|
||||
},
|
||||
"schema-utils": {
|
||||
@@ -14460,7 +14486,7 @@
|
||||
"integrity": "sha512-yYrjb9TX2k/J1Y5UNy3KYdZq10xhYcF8nMpAW6o3hy6Q8WSIEf9lJHG/ePnOBfziPM3fvQwfOwa13U/Fh8qTfA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ajv": "6.1.1",
|
||||
"ajv": "6.2.1",
|
||||
"ajv-keywords": "3.1.0"
|
||||
}
|
||||
},
|
||||
@@ -14471,9 +14497,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz",
|
||||
"integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==",
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.3.0.tgz",
|
||||
"integrity": "sha512-0aP01LLIskjKs3lq52EC0aGBAJhLq7B2Rd8HC/DR/PtNNpcLilNmHC12O+hu0usQpo7wtHNRqtrhBwtDb0+dNg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "3.0.0"
|
||||
@@ -15723,9 +15749,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"prettier": {
|
||||
"version": "1.10.2",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-1.10.2.tgz",
|
||||
"integrity": "sha512-TcdNoQIWFoHblurqqU6d1ysopjq7UX0oRcT/hJ8qvBAELiYWn+Ugf0AXdnzISEJ7vuhNnQ98N8jR8Sh53x4IZg==",
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-1.11.1.tgz",
|
||||
"integrity": "sha512-T/KD65Ot0PB97xTrG8afQ46x3oiVhnfGjGESSI9NWYcG92+OUPZKkwHqGWXH2t9jK1crnQjubECW0FuOth+hxw==",
|
||||
"dev": true
|
||||
},
|
||||
"pretty-format": {
|
||||
@@ -15869,7 +15895,7 @@
|
||||
"integrity": "sha512-2kmNR9ry+Pf45opRVirpNuIFotsxUGLaYqxIwuR77AYrYRMuFCz9eryHBS52L360O+NcR383CL4QYlMKPq4zYA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"duplexify": "3.5.3",
|
||||
"duplexify": "3.5.4",
|
||||
"inherits": "2.0.3",
|
||||
"pump": "2.0.1"
|
||||
}
|
||||
@@ -15940,9 +15966,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"ramda": {
|
||||
"version": "0.24.1",
|
||||
"resolved": "https://registry.npmjs.org/ramda/-/ramda-0.24.1.tgz",
|
||||
"integrity": "sha1-w7d1UZfzW43DUCIoJixMkd22uFc=",
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/ramda/-/ramda-0.25.0.tgz",
|
||||
"integrity": "sha512-GXpfrYVPwx3K7RQ6aYT8KPS8XViSXUVJT1ONhoKPE9VAleW42YE+U+8VEyGWt41EnEQW7gwecYJriTI0pKoecQ==",
|
||||
"dev": true
|
||||
},
|
||||
"randomatic": {
|
||||
@@ -16011,9 +16037,9 @@
|
||||
"integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4="
|
||||
},
|
||||
"raven": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/raven/-/raven-2.4.1.tgz",
|
||||
"integrity": "sha512-LLLS8bOJC1q33qszBsLaEtEg7X8G8hYLGcKO4s6EifAce2BN6cTRdBXNvwVNv4kNk82YUZYrj53yEbL4kCmjjw==",
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/raven/-/raven-2.4.2.tgz",
|
||||
"integrity": "sha1-ASnircMHiGRv1TC2fQioziXU9tw=",
|
||||
"requires": {
|
||||
"cookie": "0.3.1",
|
||||
"md5": "2.2.1",
|
||||
@@ -16035,9 +16061,9 @@
|
||||
}
|
||||
},
|
||||
"raven-js": {
|
||||
"version": "3.22.3",
|
||||
"resolved": "https://registry.npmjs.org/raven-js/-/raven-js-3.22.3.tgz",
|
||||
"integrity": "sha512-pIzHpAggyTOGJE3ruAKdZNK5qhO4V21kR7lwpdUM875yHpq1cqeGzvs78/RufF3g7NaAvVmMPCbaV9uUhQzJ3A==",
|
||||
"version": "3.23.1",
|
||||
"resolved": "https://registry.npmjs.org/raven-js/-/raven-js-3.23.1.tgz",
|
||||
"integrity": "sha512-cfkGRgz1TkFmEM5ahPWkIEav2+3zr32qMaBvKizzxN7fZapLbCrxMHMLDn7LSVGN0+dyPKY18imv3i0dkLRoKg==",
|
||||
"dev": true
|
||||
},
|
||||
"raw-body": {
|
||||
@@ -17963,9 +17989,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"stylelint-config-standard": {
|
||||
"version": "18.1.0",
|
||||
"resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-18.1.0.tgz",
|
||||
"integrity": "sha512-kjpxnt1fu56ZJk+8wETz0Hr/3vvpj5KfQoBRLVeAJcFtCZBxF5kUDXGqsn4h2ZVyvNTf+2lOfX1dfok5ZOltqg==",
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-18.2.0.tgz",
|
||||
"integrity": "sha512-07x0TaSIzvXlbOioUU4ORkCIM07kyIuojkbSVCyFWNVgXMXYHfhnQSCkqu+oHWJf3YADAnPGWzdJ53NxkoJ7RA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"stylelint-config-recommended": "2.1.0"
|
||||
@@ -19946,9 +19972,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"x-xss-protection": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/x-xss-protection/-/x-xss-protection-1.0.0.tgz",
|
||||
"integrity": "sha1-iYr7k4abJGYc+cUvnujbjtB2Tdk="
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/x-xss-protection/-/x-xss-protection-1.1.0.tgz",
|
||||
"integrity": "sha512-rx3GzJlgEeZ08MIcDsU2vY2B1QEriUKJTSiNHHUIem6eg9pzVOr2TL3Y4Pd6TMAM5D5azGjcxqI62piITBDHVg=="
|
||||
},
|
||||
"xml-char-classes": {
|
||||
"version": "1.0.0",
|
||||
|
||||
39
package.json
39
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "firefox-send",
|
||||
"description": "File Sharing Experiment",
|
||||
"version": "2.4.1",
|
||||
"version": "2.5.0",
|
||||
"author": "Mozilla (https://mozilla.org)",
|
||||
"repository": "mozilla/send",
|
||||
"homepage": "https://github.com/mozilla/send/",
|
||||
@@ -26,11 +26,10 @@
|
||||
"contributors": "git shortlog -s | awk -F\\t '{print $2}' > CONTRIBUTORS",
|
||||
"release": "npm-run-all contributors changelog",
|
||||
"test": "npm-run-all test:*",
|
||||
"test:backend": "nyc mocha --reporter=min test/unit",
|
||||
"test:backend": "nyc mocha --reporter=min test/backend",
|
||||
"test:frontend": "cross-env NODE_ENV=development node test/frontend/runner.js && nyc report --reporter=html",
|
||||
"start": "cross-env NODE_ENV=development webpack-dev-server",
|
||||
"prod": "node server/prod.js",
|
||||
"cover": "nyc --reporter=html mocha test/unit"
|
||||
"start": "npm run clean && cross-env NODE_ENV=development webpack-dev-server",
|
||||
"prod": "node server/prod.js"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.js": [
|
||||
@@ -54,8 +53,9 @@
|
||||
"node": ">=8.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"asmcrypto.js": "^0.22.0",
|
||||
"babel-core": "^6.26.0",
|
||||
"babel-loader": "^7.1.3",
|
||||
"babel-loader": "^7.1.4",
|
||||
"babel-plugin-istanbul": "^4.1.5",
|
||||
"babel-plugin-yo-yoify": "^1.0.2",
|
||||
"babel-preset-env": "^1.6.1",
|
||||
@@ -63,25 +63,26 @@
|
||||
"babel-preset-stage-2": "^6.24.1",
|
||||
"babel-preset-stage-3": "^6.24.1",
|
||||
"base64-js": "^1.2.3",
|
||||
"copy-webpack-plugin": "^4.4.2",
|
||||
"copy-webpack-plugin": "^4.5.0",
|
||||
"cross-env": "^5.1.3",
|
||||
"css-loader": "^0.28.10",
|
||||
"css-mqpacker": "^6.0.2",
|
||||
"eslint": "^4.18.1",
|
||||
"eslint-plugin-mocha": "^4.11.0",
|
||||
"eslint": "^4.18.2",
|
||||
"eslint-plugin-mocha": "^4.12.1",
|
||||
"eslint-plugin-node": "^6.0.1",
|
||||
"eslint-plugin-security": "^1.4.0",
|
||||
"expose-loader": "^0.7.4",
|
||||
"extract-loader": "^1.0.2",
|
||||
"extract-text-webpack-plugin": "^3.0.2",
|
||||
"file-loader": "^1.1.9",
|
||||
"fast-text-encoding": "^1.0.0",
|
||||
"file-loader": "^1.1.11",
|
||||
"fluent-intl-polyfill": "^0.1.0",
|
||||
"git-rev-sync": "^1.10.0",
|
||||
"github-changes": "^1.1.2",
|
||||
"html-loader": "^0.5.5",
|
||||
"husky": "^0.14.3",
|
||||
"lint-staged": "^7.0.0",
|
||||
"mocha": "^5.0.0",
|
||||
"mocha": "^5.0.4",
|
||||
"nanobus": "^4.3.2",
|
||||
"nanotiming": "^7.3.0",
|
||||
"npm-run-all": "^4.1.2",
|
||||
@@ -89,18 +90,18 @@
|
||||
"nyc": "^11.5.0",
|
||||
"postcss-cssnext": "^3.1.0",
|
||||
"postcss-import": "^11.1.0",
|
||||
"postcss-loader": "^2.1.0",
|
||||
"prettier": "^1.10.2",
|
||||
"postcss-loader": "^2.1.1",
|
||||
"prettier": "^1.11.1",
|
||||
"proxyquire": "^1.8.0",
|
||||
"puppeteer": "^1.1.1",
|
||||
"raven-js": "^3.22.2",
|
||||
"raven-js": "^3.23.1",
|
||||
"redis-mock": "^0.21.0",
|
||||
"require-from-string": "^2.0.1",
|
||||
"rimraf": "^2.6.2",
|
||||
"sinon": "^4.4.2",
|
||||
"string-hash": "^1.1.3",
|
||||
"stylelint": "^9.1.1",
|
||||
"stylelint-config-standard": "^18.1.0",
|
||||
"stylelint-config-standard": "^18.2.0",
|
||||
"stylelint-no-unsupported-browser-features": "^2.0.0",
|
||||
"svgo": "^1.0.5",
|
||||
"svgo-loader": "^2.1.0",
|
||||
@@ -113,19 +114,19 @@
|
||||
"webpack-unassert-loader": "^1.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"aws-sdk": "^2.202.0",
|
||||
"aws-sdk": "^2.206.0",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"choo": "^6.7.0",
|
||||
"choo": "^6.10.0",
|
||||
"cldr-core": "^32.0.0",
|
||||
"connect-busboy": "0.0.2",
|
||||
"convict": "^4.0.1",
|
||||
"express": "^4.16.2",
|
||||
"fluent": "^0.6.3",
|
||||
"fluent-langneg": "^0.1.0",
|
||||
"helmet": "^3.11.0",
|
||||
"helmet": "^3.12.0",
|
||||
"mkdirp": "^0.5.1",
|
||||
"mozlog": "^2.2.0",
|
||||
"raven": "^2.4.1",
|
||||
"raven": "^2.4.2",
|
||||
"redis": "^2.8.0"
|
||||
},
|
||||
"availableLanguages": [
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
# Firefox Send is a brand name and should not be localized.
|
||||
title = Firefox Send
|
||||
siteSubtitle = web eksperiment
|
||||
siteFeedback = Geri dönüş
|
||||
@@ -39,29 +39,29 @@ copyUrlFormButton = Buferə köçür
|
||||
copiedUrl = Köçürüldü!
|
||||
deleteFileButton = Faylı sil
|
||||
sendAnotherFileLink = Başqa fayl göndər
|
||||
// Alternative text used on the download link/button (indicates an action).
|
||||
# Alternative text used on the download link/button (indicates an action).
|
||||
downloadAltText = Endir
|
||||
downloadsFileList = Endirmələr
|
||||
// Used as header in a column indicating the amount of time left before a
|
||||
// download link expires (e.g. "10h 5m")
|
||||
# Used as header in a column indicating the amount of time left before a
|
||||
# download link expires (e.g. "10h 5m")
|
||||
timeFileList = Vaxt
|
||||
// Used as header in a column indicating the number of times a file has been
|
||||
// downloaded
|
||||
# Used as header in a column indicating the number of times a file has been
|
||||
# downloaded
|
||||
downloadFileName = { $filename } faylını endir
|
||||
downloadFileSize = ({ $size })
|
||||
unlockInputLabel = Parol daxil edin
|
||||
unlockInputPlaceholder = Parol
|
||||
unlockButtonLabel = Aç
|
||||
downloadFileTitle = Şifrələnmiş Faylı Endir
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
# Firefox Send is a brand name and should not be localized.
|
||||
downloadMessage = Yoldaşınız Firefox Send ilə sizə fayl göndərir, fayllarınızı təhlükəsiz, məxfi, şifrələnmiş və daima onlayn qalmaması üçün avtomatik silən fayl göndərmə xidməti.
|
||||
// Text and title used on the download link/button (indicates an action).
|
||||
# Text and title used on the download link/button (indicates an action).
|
||||
downloadButtonLabel = 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)".
|
||||
# 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.
|
||||
# Firefox Send is a brand name and should not be localized.
|
||||
sendYourFilesLink = Firefox Send Yoxla
|
||||
downloadingPageProgress = { $filename } faylı ({ $size }) endirilir
|
||||
downloadingPageMessage = Lütfən faylı endirib şifrəsini açarkən vərəqi açıq buraxın.
|
||||
@@ -73,7 +73,7 @@ fileTooBig = Fayl yükləmək üçün çox böyükdür. Fayl { $size }-dan az ol
|
||||
linkExpiredAlt = Keçidin vaxtı çıxıb
|
||||
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.
|
||||
# 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.
|
||||
@@ -81,7 +81,7 @@ updateFirefox = Firefox-u Yenilə
|
||||
downloadFirefoxButtonSub = Pulsuz Endir
|
||||
uploadedFile = Fayl
|
||||
copyFileList = Keçidi Köçürt
|
||||
// expiryFileList is used as a column header
|
||||
# expiryFileList is used as a column header
|
||||
expiryFileList = Vaxtı çıxma tarixi
|
||||
deleteFileList = Sil
|
||||
nevermindButton = Vacib deyil
|
||||
@@ -94,7 +94,7 @@ deletePopupCancel = Ləğv et
|
||||
deleteButtonHover = Sil
|
||||
copyUrlHover = Keçidi Köçürt
|
||||
footerLinkLegal = Hüquqi
|
||||
// Test Pilot is a proper name and should not be localized.
|
||||
# Test Pilot is a proper name and should not be localized.
|
||||
footerLinkAbout = Test Pilot Haqqında
|
||||
footerLinkPrivacy = Məxfilik
|
||||
footerLinkTerms = Şərtlər
|
||||
@@ -103,13 +103,17 @@ requirePasswordCheckbox = Bu faylı endirmək üçün parol tələb et
|
||||
addPasswordButton = Parol əlavə et
|
||||
changePasswordButton = Dəyişdir
|
||||
passwordTryAgain = Səhv parol. Təkrar yoxlayın.
|
||||
// This label is followed by the password needed to download a file
|
||||
passwordResult = Parol: { $password }
|
||||
reportIPInfringement = Əqli-mülkiyyət pozuntusu bildir
|
||||
javascriptRequired = Firefox Send üçün JavaScript lazımdır
|
||||
whyJavascript = Firefox Send niyə JavaScript tələb edir?
|
||||
enableJavascript = Lütfən JavaScript-i aktiv edib təkrar yoxlayın.
|
||||
// A short representation of a countdown timer containing the number of hours and minutes remaining as digits, example "13h 47m"
|
||||
# A short representation of a countdown timer containing the number of hours and minutes remaining as digits, example "13h 47m"
|
||||
expiresHoursMinutes = { $hours } saat { $minutes } dəq
|
||||
// A short representation of a countdown timer containing the number of minutes remaining as digits, example "56m"
|
||||
# A short representation of a countdown timer containing the number of minutes remaining as digits, example "56m"
|
||||
expiresMinutes = { $minutes } dəq
|
||||
# A short status message shown when a password is successfully set
|
||||
passwordIsSet = Parol quruldu
|
||||
# A short status message shown when the user enters a long password
|
||||
maxPasswordLength = Maksimum parol uzunluğu: { $length }
|
||||
# A short status message shown when there was an error setting the password
|
||||
passwordSetError = Parol qurula bilmədi
|
||||
|
||||
@@ -32,9 +32,9 @@ downloadCount = { $num ->
|
||||
*[other] { $num } staženích
|
||||
}
|
||||
timespanHours = { $num ->
|
||||
[one] hodina
|
||||
[few] hodiny
|
||||
*[other] hodin
|
||||
[one] jedné hodině
|
||||
[few] { $num } hodinách
|
||||
*[other] { $num } hodinách
|
||||
}
|
||||
copyUrlFormLabelWithName = Zkopírujte a sdílejte odkaz na váš soubor: { $filename }
|
||||
copyUrlFormButton = Zkopírovat do schránky
|
||||
@@ -110,9 +110,9 @@ javascriptRequired = Firefox Send vyžaduje povolený JavaScript
|
||||
whyJavascript = Proč Firefox Send vyžaduje povolený JavaScript?
|
||||
enableJavascript = Povolte JavaScript a zkuste to znovu.
|
||||
# A short representation of a countdown timer containing the number of hours and minutes remaining as digits, example "13h 47m"
|
||||
expiresHoursMinutes = { $hours }h { $minutes }m
|
||||
expiresHoursMinutes = { $hours } h { $minutes } m
|
||||
# A short representation of a countdown timer containing the number of minutes remaining as digits, example "56m"
|
||||
expiresMinutes = { $minutes }m
|
||||
expiresMinutes = { $minutes } m
|
||||
# A short status message shown when a password is successfully set
|
||||
passwordIsSet = Heslo nastaveno
|
||||
# A short status message shown when the user enters a long password
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
# Firefox Send is a brand name and should not be localized.
|
||||
title = Firefox Send
|
||||
siteSubtitle = πείραμα διαδικτύου
|
||||
siteFeedback = Σχόλια
|
||||
@@ -39,29 +39,29 @@ copyUrlFormButton = Αντιγραφή στο πρόχειρο
|
||||
copiedUrl = Αντιγράφτηκε!
|
||||
deleteFileButton = Διαγραφή αρχείου
|
||||
sendAnotherFileLink = Αποστολή άλλου αρχείου
|
||||
// Alternative text used on the download link/button (indicates an action).
|
||||
# Alternative text used on the download link/button (indicates an action).
|
||||
downloadAltText = Λήψη
|
||||
downloadsFileList = Λήψεις
|
||||
// Used as header in a column indicating the amount of time left before a
|
||||
// download link expires (e.g. "10h 5m")
|
||||
# Used as header in a column indicating the amount of time left before a
|
||||
# download link expires (e.g. "10h 5m")
|
||||
timeFileList = Ώρα
|
||||
// Used as header in a column indicating the number of times a file has been
|
||||
// downloaded
|
||||
# Used as header in a column indicating the number of times a file has been
|
||||
# downloaded
|
||||
downloadFileName = Λήψη του { $filename }
|
||||
downloadFileSize = ({ $size })
|
||||
unlockInputLabel = Εισαγωγή κωδικού πρόσβασης
|
||||
unlockInputPlaceholder = Κωδικός πρόσβασης
|
||||
unlockButtonLabel = Ξεκλείδωμα
|
||||
downloadFileTitle = Λήψη κρυπτογραφημένου αρχείου
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
# 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).
|
||||
# Text and title used on the download link/button (indicates an action).
|
||||
downloadButtonLabel = Λήψη
|
||||
downloadNotification = Η λήψη σας ολοκληρώθηκε.
|
||||
downloadFinish = Η λήψη ολοκληρώθηκε
|
||||
// This message is displayed when uploading or downloading a file, e.g. "(1,3 MB of 10 MB)".
|
||||
# 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.
|
||||
# Firefox Send is a brand name and should not be localized.
|
||||
sendYourFilesLink = Δοκιμάστε το Firefox Send
|
||||
downloadingPageProgress = Γίνεται λήψη του { $filename } ({ $size })
|
||||
downloadingPageMessage = Παρακαλώ αφήστε ανοικτή αυτή την καρτέλα όσο λαμβάνουμε και αποκρυπτογραφούμε το αρχείο σας.
|
||||
@@ -73,7 +73,7 @@ fileTooBig = Αυτό το αρχείο είναι πολύ μεγάλο για
|
||||
linkExpiredAlt = Ο σύνδεσμος έληξε
|
||||
expiredPageHeader = Αυτός ο σύνδεσμος έχει λήξει ή δεν υπήρξε ποτέ!
|
||||
notSupportedHeader = Το πρόγραμμα περιήγησής σας δεν υποστηρίζεται.
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
# Firefox Send is a brand name and should not be localized.
|
||||
notSupportedDetail = Δυστυχώς, αυτό το πρόγραμμα περιήγησης δεν υποστηρίζει την τεχνολογία ιστού στην οποία βασίζεται το Firefox Send. Θα πρέπει να δοκιμάσετε ένα άλλο πρόγραμμα περιήγησης. Προτείνουμε το Firefox!
|
||||
notSupportedLink = Γιατί δεν υποστηρίζεται το πρόγραμμα περιήγησής μου;
|
||||
notSupportedOutdatedDetail = Δυστυχώς, αυτή η έκδοση του Firefox δεν υποστηρίζει την τεχνολογία ιστού στην οποία βασίζεται το Firefox Send. Πρέπει να ενημερώσετε το πρόγραμμα περιήγησής σας.
|
||||
@@ -81,7 +81,7 @@ updateFirefox = Ενημέρωση Firefox
|
||||
downloadFirefoxButtonSub = Δωρεάν λήψη
|
||||
uploadedFile = Αρχείο
|
||||
copyFileList = Αντιγραφή URL
|
||||
// expiryFileList is used as a column header
|
||||
# expiryFileList is used as a column header
|
||||
expiryFileList = Λήγει σε
|
||||
deleteFileList = Διαγραφή
|
||||
nevermindButton = Μην ανησυχείτε
|
||||
@@ -94,7 +94,7 @@ deletePopupCancel = Ακύρωση
|
||||
deleteButtonHover = Διαγραφή
|
||||
copyUrlHover = Αντιγραφή URL
|
||||
footerLinkLegal = Νομικά
|
||||
// Test Pilot is a proper name and should not be localized.
|
||||
# Test Pilot is a proper name and should not be localized.
|
||||
footerLinkAbout = Σχετικά με το Test Pilot
|
||||
footerLinkPrivacy = Απόρρητο
|
||||
footerLinkTerms = Όροι
|
||||
@@ -103,13 +103,17 @@ requirePasswordCheckbox = Απαίτηση κωδικού πρόσβασης γ
|
||||
addPasswordButton = Προσθήκη κωδικού πρόσβασης
|
||||
changePasswordButton = Αλλαγή
|
||||
passwordTryAgain = Λάθος κωδικός πρόσβασης. Δοκιμάστε ξανά.
|
||||
// This label is followed by the password needed to download a file
|
||||
passwordResult = Κωδικός πρόσβασης: { $password }
|
||||
reportIPInfringement = Αναφορά παραβίασης IP
|
||||
javascriptRequired = Το Firefox Send απαιτεί JavaScript
|
||||
whyJavascript = Γιατί το Firefox Send απαιτεί JavaScript;
|
||||
enableJavascript = Παρακαλώ ενεργοποιήστε το JavaScript και δοκιμάστε ξανά.
|
||||
// A short representation of a countdown timer containing the number of hours and minutes remaining as digits, example "13h 47m"
|
||||
# A short representation of a countdown timer containing the number of hours and minutes remaining as digits, example "13h 47m"
|
||||
expiresHoursMinutes = { $hours }ώ { $minutes }λ
|
||||
// A short representation of a countdown timer containing the number of minutes remaining as digits, example "56m"
|
||||
# A short representation of a countdown timer containing the number of minutes remaining as digits, example "56m"
|
||||
expiresMinutes = { $minutes }λ
|
||||
# A short status message shown when a password is successfully set
|
||||
passwordIsSet = Επιτυχής ορισμός κωδικού
|
||||
# A short status message shown when the user enters a long password
|
||||
maxPasswordLength = Μέγιστο μήκος κωδικού: { $length }
|
||||
# A short status message shown when there was an error setting the password
|
||||
passwordSetError = Δεν ήταν δυνατός ο ορισμός αυτού του κωδικού
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
# Firefox Send is a brand name and should not be localized.
|
||||
title = Firefox Send
|
||||
siteSubtitle = experimento web
|
||||
siteFeedback = Opinión
|
||||
@@ -39,29 +39,29 @@ copyUrlFormButton = Copiar al portapapeles
|
||||
copiedUrl = ¡Copiado!
|
||||
deleteFileButton = Borrar archivo
|
||||
sendAnotherFileLink = Enviar otro archivo
|
||||
// Alternative text used on the download link/button (indicates an action).
|
||||
# Alternative text used on the download link/button (indicates an action).
|
||||
downloadAltText = Descargar
|
||||
downloadsFileList = Descargas
|
||||
// Used as header in a column indicating the amount of time left before a
|
||||
// download link expires (e.g. "10h 5m")
|
||||
# Used as header in a column indicating the amount of time left before a
|
||||
# download link expires (e.g. "10h 5m")
|
||||
timeFileList = Tiempo
|
||||
// Used as header in a column indicating the number of times a file has been
|
||||
// downloaded
|
||||
# Used as header in a column indicating the number of times a file has been
|
||||
# downloaded
|
||||
downloadFileName = Descargar { $filename }
|
||||
downloadFileSize = ({ $size })
|
||||
unlockInputLabel = Ingresar contraseña
|
||||
unlockInputPlaceholder = Contraseña
|
||||
unlockButtonLabel = Desbloquear
|
||||
downloadFileTitle = Descargar archivo cifrado
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
# Firefox Send is a brand name and should not be localized.
|
||||
downloadMessage = Tu amigo te está enviando un archivo con Firefox Send, un servicio que permite compartir archivos con un enlace cifrado, seguro y privado que expira automáticamente para asegurar que tus datos no quedan en línea para siempre.
|
||||
// Text and title used on the download link/button (indicates an action).
|
||||
# Text and title used on the download link/button (indicates an action).
|
||||
downloadButtonLabel = 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)".
|
||||
# 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.
|
||||
# Firefox Send is a brand name and should not be localized.
|
||||
sendYourFilesLink = Probá Firefox Send
|
||||
downloadingPageProgress = Descargando { $filename } ({ $size })
|
||||
downloadingPageMessage = Dejá esta pestaña abierta mientras descargamos el archivo y lo desciframos.
|
||||
@@ -73,7 +73,7 @@ fileTooBig = El archivo es demasiado grande para subir. Debería tener menos de
|
||||
linkExpiredAlt = Enlace explirado
|
||||
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.
|
||||
# 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.
|
||||
@@ -81,7 +81,7 @@ updateFirefox = Actualizar Firefox
|
||||
downloadFirefoxButtonSub = Descarga gratuita
|
||||
uploadedFile = Archivo
|
||||
copyFileList = Copiar URL
|
||||
// expiryFileList is used as a column header
|
||||
# expiryFileList is used as a column header
|
||||
expiryFileList = Expira en
|
||||
deleteFileList = Borrar
|
||||
nevermindButton = No importa
|
||||
@@ -94,7 +94,7 @@ deletePopupCancel = Cancelar
|
||||
deleteButtonHover = Borrar
|
||||
copyUrlHover = Copiar URL
|
||||
footerLinkLegal = Legales
|
||||
// Test Pilot is a proper name and should not be localized.
|
||||
# Test Pilot is a proper name and should not be localized.
|
||||
footerLinkAbout = Acerca de Test Pilot
|
||||
footerLinkPrivacy = Privacidad
|
||||
footerLinkTerms = Términos
|
||||
@@ -103,13 +103,17 @@ requirePasswordCheckbox = Requerir contraseña para descargar este archivo
|
||||
addPasswordButton = Agregar contraseña
|
||||
changePasswordButton = Cambiar
|
||||
passwordTryAgain = Contraseña incorrecta. Intentá nuevamente.
|
||||
// This label is followed by the password needed to download a file
|
||||
passwordResult = Contraseña: { $password }
|
||||
reportIPInfringement = Informar violación de propiedad intelectual
|
||||
javascriptRequired = Firefox Send requiere JavaScript
|
||||
whyJavascript = ¿Por qué Firefox Send requiere Java Script?
|
||||
enableJavascript = Por favor habilite JavaScript y pruebe de nuevo.
|
||||
// A short representation of a countdown timer containing the number of hours and minutes remaining as digits, example "13h 47m"
|
||||
# A short representation of a countdown timer containing the number of hours and minutes remaining as digits, example "13h 47m"
|
||||
expiresHoursMinutes = h { $hours } m { $minutes }
|
||||
// A short representation of a countdown timer containing the number of minutes remaining as digits, example "56m"
|
||||
# A short representation of a countdown timer containing the number of minutes remaining as digits, example "56m"
|
||||
expiresMinutes = m { $minutes }
|
||||
# A short status message shown when a password is successfully set
|
||||
passwordIsSet = Contraseña establecida
|
||||
# A short status message shown when the user enters a long password
|
||||
maxPasswordLength = Longitud máxima de la contraseña: { $length }
|
||||
# A short status message shown when there was an error setting the password
|
||||
passwordSetError = No se pudo establecer la contraseña
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
# Firefox Send is a brand name and should not be localized.
|
||||
title = Firefox Send
|
||||
siteSubtitle = experimento web
|
||||
siteFeedback = Comentarios
|
||||
@@ -39,29 +39,29 @@ copyUrlFormButton = Copiar al portapapeles
|
||||
copiedUrl = ¡Copiado!
|
||||
deleteFileButton = Eliminar archivo
|
||||
sendAnotherFileLink = Enviar otro archivo
|
||||
// Alternative text used on the download link/button (indicates an action).
|
||||
# Alternative text used on the download link/button (indicates an action).
|
||||
downloadAltText = Descargar
|
||||
downloadsFileList = Descargas
|
||||
// Used as header in a column indicating the amount of time left before a
|
||||
// download link expires (e.g. "10h 5m")
|
||||
# Used as header in a column indicating the amount of time left before a
|
||||
# download link expires (e.g. "10h 5m")
|
||||
timeFileList = Tiempo
|
||||
// Used as header in a column indicating the number of times a file has been
|
||||
// downloaded
|
||||
# Used as header in a column indicating the number of times a file has been
|
||||
# downloaded
|
||||
downloadFileName = Descargar { $filename }
|
||||
downloadFileSize = ({ $size })
|
||||
unlockInputLabel = Ingresar contraseña
|
||||
unlockInputPlaceholder = Contraseña
|
||||
unlockButtonLabel = Desbloquear
|
||||
downloadFileTitle = Bajar archivo cifrado
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
# Firefox Send is a brand name and should not be localized.
|
||||
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).
|
||||
# Text and title used on the download link/button (indicates an action).
|
||||
downloadButtonLabel = 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)".
|
||||
# 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.
|
||||
# Firefox Send is a brand name and should not be localized.
|
||||
sendYourFilesLink = Probar Firefox Send
|
||||
downloadingPageProgress = Descargando { $filename } ({ $size })
|
||||
downloadingPageMessage = Por favor, deja esta pestaña abierta mientras recibimos tu archivo y lo desciframos.
|
||||
@@ -73,7 +73,7 @@ fileTooBig = Ese archivo es muy grande para ser subido. Debiera tener un tamaño
|
||||
linkExpiredAlt = Enlace expirado
|
||||
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.
|
||||
# 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.
|
||||
@@ -81,7 +81,7 @@ updateFirefox = Actualizar Firefox
|
||||
downloadFirefoxButtonSub = Descarga gratuita
|
||||
uploadedFile = Archivo
|
||||
copyFileList = Copiar URL
|
||||
// expiryFileList is used as a column header
|
||||
# expiryFileList is used as a column header
|
||||
expiryFileList = Expira en
|
||||
deleteFileList = Eliminar
|
||||
nevermindButton = Da lo mismo
|
||||
@@ -94,7 +94,7 @@ deletePopupCancel = Cancelar
|
||||
deleteButtonHover = Eliminar
|
||||
copyUrlHover = Copiar URL
|
||||
footerLinkLegal = Legal
|
||||
// Test Pilot is a proper name and should not be localized.
|
||||
# Test Pilot is a proper name and should not be localized.
|
||||
footerLinkAbout = Acerca de Test Pilot
|
||||
footerLinkPrivacy = Privacidad
|
||||
footerLinkTerms = Términos
|
||||
@@ -103,13 +103,17 @@ requirePasswordCheckbox = Requerir una contraseña para descargar este archivo
|
||||
addPasswordButton = Añadir contraseña
|
||||
changePasswordButton = Cambiar
|
||||
passwordTryAgain = Contraseña incorrecta. Vuelve a intentarlo.
|
||||
// This label is followed by the password needed to download a file
|
||||
passwordResult = Contraseña: { $password }
|
||||
reportIPInfringement = Reportar infracción de PI
|
||||
javascriptRequired = Firefox Send requiere JavaScript.
|
||||
whyJavascript = ¿Por qué Firefox Send requiere JavaScript?
|
||||
enableJavascript = Por favor, activa JavaScript y vuelve a intentarlo.
|
||||
// A short representation of a countdown timer containing the number of hours and minutes remaining as digits, example "13h 47m"
|
||||
# A short representation of a countdown timer containing the number of hours and minutes remaining as digits, example "13h 47m"
|
||||
expiresHoursMinutes = { $hours }h { $minutes }m
|
||||
// A short representation of a countdown timer containing the number of minutes remaining as digits, example "56m"
|
||||
# A short representation of a countdown timer containing the number of minutes remaining as digits, example "56m"
|
||||
expiresMinutes = { $minutes }m
|
||||
# A short status message shown when a password is successfully set
|
||||
passwordIsSet = Contraseña establecida
|
||||
# A short status message shown when the user enters a long password
|
||||
maxPasswordLength = Longitud máxima de la contraseña: { $length }
|
||||
# A short status message shown when there was an error setting the password
|
||||
passwordSetError = Esta contraseña no pudo ser establecida
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
# Firefox Send is a brand name and should not be localized.
|
||||
title = Firefox Send
|
||||
siteSubtitle = veebieksperiment
|
||||
siteFeedback = Tagasiside
|
||||
@@ -39,29 +39,29 @@ copyUrlFormButton = Kopeeri vahemällu
|
||||
copiedUrl = Kopeeritud!
|
||||
deleteFileButton = Kustuta fail
|
||||
sendAnotherFileLink = Saada järgmine fail
|
||||
// Alternative text used on the download link/button (indicates an action).
|
||||
# Alternative text used on the download link/button (indicates an action).
|
||||
downloadAltText = Laadi alla
|
||||
downloadsFileList = Allalaadimised
|
||||
// Used as header in a column indicating the amount of time left before a
|
||||
// download link expires (e.g. "10h 5m")
|
||||
# Used as header in a column indicating the amount of time left before a
|
||||
# download link expires (e.g. "10h 5m")
|
||||
timeFileList = Aega jäänud
|
||||
// Used as header in a column indicating the number of times a file has been
|
||||
// downloaded
|
||||
# Used as header in a column indicating the number of times a file has been
|
||||
# downloaded
|
||||
downloadFileName = Laadi fail { $filename } alla
|
||||
downloadFileSize = ({ $size })
|
||||
unlockInputLabel = Sisesta parool
|
||||
unlockInputPlaceholder = Parool
|
||||
unlockButtonLabel = Ava
|
||||
downloadFileTitle = Krüptitud faili allalaadimine
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
# Firefox Send is a brand name and should not be localized.
|
||||
downloadMessage = Sulle on saadetud fail Firefox Sendiga - teenusega, mis lubab faile ohutult, privaatselt ja krüpteeritult jagada. Failid kustutatakse automaatselt, et need ei jääks internetti igaveseks.
|
||||
// Text and title used on the download link/button (indicates an action).
|
||||
# Text and title used on the download link/button (indicates an action).
|
||||
downloadButtonLabel = Laadi alla
|
||||
downloadNotification = Allalaadimine on lõpetatud.
|
||||
downloadFinish = Allalaadimine lõpetati
|
||||
// This message is displayed when uploading or downloading a file, e.g. "(1,3 MB of 10 MB)".
|
||||
# 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.
|
||||
# Firefox Send is a brand name and should not be localized.
|
||||
sendYourFilesLink = Proovi Firefox Sendi
|
||||
downloadingPageProgress = Faili { $filename } ({ $size }) allalaadimine
|
||||
downloadingPageMessage = Palun jäta see kaart lahti, kuni fail on alla laaditud ja dekrüptitud.
|
||||
@@ -73,7 +73,7 @@ fileTooBig = Fail on üleslaadimiseks liiga suur. See peaks olema väiksem kui {
|
||||
linkExpiredAlt = Link on aegunud
|
||||
expiredPageHeader = See link on aegunud või seda pole kunagi olnudki!
|
||||
notSupportedHeader = Sinu brauser pole toetatud.
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
# Firefox Send is a brand name and should not be localized.
|
||||
notSupportedDetail = Kahjuks ei toeta see brauser veebitehnoloogiaid, mis teevad Firefox Sendi toimimise võimalikuks. Sa pead proovima teise brauseriga. Me soovitame Firefoxi!
|
||||
notSupportedLink = Miks mu brauser toetatud pole?
|
||||
notSupportedOutdatedDetail = Kahjuks ei toeta see Firefoxi versioon veebitehnoloogiaid, mis teevad Firefox Sendi toimimise võimalikuks. Sa pead oma brauserit uuendama.
|
||||
@@ -81,7 +81,7 @@ updateFirefox = Uuenda Firefox
|
||||
downloadFirefoxButtonSub = Laadi alla tasuta
|
||||
uploadedFile = Fail
|
||||
copyFileList = Kopeeri URL
|
||||
// expiryFileList is used as a column header
|
||||
# expiryFileList is used as a column header
|
||||
expiryFileList = Aegub
|
||||
deleteFileList = Kustuta
|
||||
nevermindButton = Ära pane tähele
|
||||
@@ -94,7 +94,7 @@ deletePopupCancel = Loobu
|
||||
deleteButtonHover = Kustuta
|
||||
copyUrlHover = Kopeeri URL
|
||||
footerLinkLegal = Õiguslik teave
|
||||
// Test Pilot is a proper name and should not be localized.
|
||||
# Test Pilot is a proper name and should not be localized.
|
||||
footerLinkAbout = Test Pilotist
|
||||
footerLinkPrivacy = Privaatsusest
|
||||
footerLinkTerms = Teenusetingimused
|
||||
@@ -103,13 +103,17 @@ requirePasswordCheckbox = Selle faili allalaadimiseks nõutakse parooli
|
||||
addPasswordButton = Lisa parool
|
||||
changePasswordButton = Muuda
|
||||
passwordTryAgain = Vale parool. Palun proovi uuesti.
|
||||
// This label is followed by the password needed to download a file
|
||||
passwordResult = Parool: { $password }
|
||||
reportIPInfringement = Intellektuaalomandi keelatud kasutamise raporteerimine
|
||||
javascriptRequired = Firefox Send'i kasutamiseks tuleb JavaScript lubada
|
||||
whyJavascript = Miks Firefox Send JavaScripti vajab?
|
||||
enableJavascript = Palun luba JavaScript ja proovi uuesti.
|
||||
// A short representation of a countdown timer containing the number of hours and minutes remaining as digits, example "13h 47m"
|
||||
# A short representation of a countdown timer containing the number of hours and minutes remaining as digits, example "13h 47m"
|
||||
expiresHoursMinutes = { $hours }t { $minutes }m
|
||||
// A short representation of a countdown timer containing the number of minutes remaining as digits, example "56m"
|
||||
# A short representation of a countdown timer containing the number of minutes remaining as digits, example "56m"
|
||||
expiresMinutes = { $minutes }m
|
||||
# A short status message shown when a password is successfully set
|
||||
passwordIsSet = Parool on muudetud
|
||||
# A short status message shown when the user enters a long password
|
||||
maxPasswordLength = Maksimaalne parooli pikkus: { $length }
|
||||
# A short status message shown when there was an error setting the password
|
||||
passwordSetError = Parooli muutmine ebaõnnestus
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
# Firefox Send is a brand name and should not be localized.
|
||||
title = Firefox Send
|
||||
siteSubtitle = آزمایش وب
|
||||
siteFeedback = بازخورد
|
||||
@@ -37,29 +37,29 @@ copyUrlFormButton = رونوشت به کلیپبورد
|
||||
copiedUrl = رونوشت شد!
|
||||
deleteFileButton = حذف پرونده
|
||||
sendAnotherFileLink = ارسال پرونده دیگر
|
||||
// Alternative text used on the download link/button (indicates an action).
|
||||
# Alternative text used on the download link/button (indicates an action).
|
||||
downloadAltText = دریافت
|
||||
downloadsFileList = دریافتها
|
||||
// Used as header in a column indicating the amount of time left before a
|
||||
// download link expires (e.g. "10h 5m")
|
||||
# Used as header in a column indicating the amount of time left before a
|
||||
# download link expires (e.g. "10h 5m")
|
||||
timeFileList = زمان
|
||||
// Used as header in a column indicating the number of times a file has been
|
||||
// downloaded
|
||||
# Used as header in a column indicating the number of times a file has been
|
||||
# downloaded
|
||||
downloadFileName = بارگیری { $filename }
|
||||
downloadFileSize = ({ $size })
|
||||
unlockInputLabel = گذرواژه را وارد کنید
|
||||
unlockInputPlaceholder = گذرواژه
|
||||
unlockButtonLabel = باز کردن
|
||||
downloadFileTitle = دریافت پروندهٔ رمزنگاری شده
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
# 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).
|
||||
# Text and title used on the download link/button (indicates an action).
|
||||
downloadButtonLabel = بارگیری
|
||||
downloadNotification = بارگیری شما کامل شد.
|
||||
downloadFinish = بارگیری کامل شد
|
||||
// This message is displayed when uploading or downloading a file, e.g. "(1,3 MB of 10 MB)".
|
||||
# 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.
|
||||
# Firefox Send is a brand name and should not be localized.
|
||||
sendYourFilesLink = Firefox Send را امتحان کنید
|
||||
downloadingPageProgress = دریافت { $filename } ({ $size })
|
||||
downloadingPageMessage = لطفا این زبانه را باز بگذارید در حالی که ما فایل شما را دریافت میکنیم و کدگذاری میکنیم.
|
||||
@@ -71,7 +71,7 @@ fileTooBig = این پرونده بسیار حجیم است. حجم آن می
|
||||
linkExpiredAlt = پیوند منقضی شده است
|
||||
expiredPageHeader = پیوند منقضی شده است یا در از همان ابتدا وجود نداشته است!
|
||||
notSupportedHeader = مرورگر شما پشتیبانی نمیکند.
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
# Firefox Send is a brand name and should not be localized.
|
||||
notSupportedDetail = متاسفانه این مرورگر این تکنولوژی وب را که به Firefox Send قدرت میبخشد را پشتیبانی نمیکند. شما بایستی مرورگری دیگری را امتحان کنید. پیشنهاد ما به شما فایرفاکس است !
|
||||
notSupportedLink = چرا مرورگر من پشتیبانی نمیکند؟
|
||||
notSupportedOutdatedDetail = متاسفانه این نسخه از فایرفاکس این تکنولوژی وب که به Firefox Send قدرت میبخشد را پشتیبانی نمیکند. شما نیاز دارید تا مرورگر خود را بروز کنید.
|
||||
@@ -79,7 +79,7 @@ updateFirefox = بروزرسانی فایرفاکس
|
||||
downloadFirefoxButtonSub = دریافت رایگان
|
||||
uploadedFile = پرونده
|
||||
copyFileList = رونوشت از نشانی
|
||||
// expiryFileList is used as a column header
|
||||
# expiryFileList is used as a column header
|
||||
expiryFileList = زمان انقضا
|
||||
deleteFileList = حذف
|
||||
nevermindButton = بیخیال
|
||||
@@ -92,7 +92,7 @@ deletePopupCancel = انصراف
|
||||
deleteButtonHover = حذف
|
||||
copyUrlHover = رونوشت از نشانی
|
||||
footerLinkLegal = ملاحظات حقوقی
|
||||
// Test Pilot is a proper name and should not be localized.
|
||||
# Test Pilot is a proper name and should not be localized.
|
||||
footerLinkAbout = درباره Test Pilot
|
||||
footerLinkPrivacy = حریمخصوصی
|
||||
footerLinkTerms = شرایط
|
||||
@@ -101,13 +101,17 @@ requirePasswordCheckbox = دریافت این پرونده نیاز به گذر
|
||||
addPasswordButton = افزودن گذرواژه
|
||||
changePasswordButton = تغییر
|
||||
passwordTryAgain = کلمه عبور اشتباه است. مجدد تلاش کنید.
|
||||
// This label is followed by the password needed to download a file
|
||||
passwordResult = گذرواژه: { $password }
|
||||
reportIPInfringement = گزارش تخلف IP
|
||||
javascriptRequired = Firefox Send نیازمند جاوااسکریپت است
|
||||
whyJavascript = چراFirefox Send جاوااسکریپت لازم دارد؟
|
||||
enableJavascript = لطفا جاوااسکریپت را فعال کنید و مجددا تلاش کنید.
|
||||
// A short representation of a countdown timer containing the number of hours and minutes remaining as digits, example "13h 47m"
|
||||
# A short representation of a countdown timer containing the number of hours and minutes remaining as digits, example "13h 47m"
|
||||
expiresHoursMinutes = { $hours }ساعت { $minutes }دقیقه
|
||||
// A short representation of a countdown timer containing the number of minutes remaining as digits, example "56m"
|
||||
# A short representation of a countdown timer containing the number of minutes remaining as digits, example "56m"
|
||||
expiresMinutes = { $minutes } دقیقه
|
||||
# A short status message shown when a password is successfully set
|
||||
passwordIsSet = گذرواژه تنظیم شد
|
||||
# A short status message shown when the user enters a long password
|
||||
maxPasswordLength = حداکثر اندازهٔ گذرواژه: { $length }
|
||||
# A short status message shown when there was an error setting the password
|
||||
passwordSetError = امکان ثبت این گذواژه نیست
|
||||
|
||||
@@ -103,8 +103,6 @@ requirePasswordCheckbox = Om dit bestân te downloaden is in wachtwurd fereaske
|
||||
addPasswordButton = Wachtwurd tafoegje
|
||||
changePasswordButton = Wizigje
|
||||
passwordTryAgain = Net krekt wachtwurd. Probearje it opnij.
|
||||
# This label is followed by the password needed to download a file
|
||||
passwordResult = Wachtwurd: { $password }
|
||||
reportIPInfringement = IP-ynbrek melde
|
||||
javascriptRequired = Firefox Send fereasket JavaScript.
|
||||
whyJavascript = Werom hat Firefox Send JavaScript nedich?
|
||||
@@ -115,3 +113,7 @@ expiresHoursMinutes = { $hours }o { $minutes }m
|
||||
expiresMinutes = { $minutes }m
|
||||
# A short status message shown when a password is successfully set
|
||||
passwordIsSet = Wachtwurd ynsteld
|
||||
# A short status message shown when the user enters a long password
|
||||
maxPasswordLength = Maksimale wachtwurdlingte: { $length }
|
||||
# A short status message shown when there was an error setting the password
|
||||
passwordSetError = Dit wachtwurd koe net ynsteld wurde
|
||||
|
||||
@@ -103,8 +103,6 @@ requirePasswordCheckbox = Krever et passord for å laste ned denne filen
|
||||
addPasswordButton = Legg til passord
|
||||
changePasswordButton = Endre
|
||||
passwordTryAgain = Feil passord. Prøv igjen.
|
||||
# This label is followed by the password needed to download a file
|
||||
passwordResult = Passord: { $password }
|
||||
reportIPInfringement = Rapporter brudd på åndsverk
|
||||
javascriptRequired = Firefox Send krever JavaScript.
|
||||
whyJavascript = Hvorfor krever Firefox Send JavaScript?
|
||||
@@ -115,3 +113,7 @@ expiresHoursMinutes = { $hours }t { $minutes }m
|
||||
expiresMinutes = { $minutes }m
|
||||
# A short status message shown when a password is successfully set
|
||||
passwordIsSet = Passord satt
|
||||
# A short status message shown when the user enters a long password
|
||||
maxPasswordLength = Maksimum passordlengde: { $length }
|
||||
# A short status message shown when there was an error setting the password
|
||||
passwordSetError = Dette passordet kunne ikke settes
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
# Firefox Send is a brand name and should not be localized.
|
||||
title = Firefox Send
|
||||
siteSubtitle = un experiment web
|
||||
siteFeedback = Feedback
|
||||
@@ -25,6 +25,11 @@ uploadSuccessConfirmHeader = Pregătit pentru trimitere
|
||||
uploadSvgAlt = Încarcă
|
||||
uploadSuccessTimingHeader = Linkul către fișierul tău va expira după 1 descărcare sau în 24 de ore.
|
||||
expireInfo = Linkul la fișier va expira după { $downloadCount } sau { $timespan }.
|
||||
downloadCount = { $num ->
|
||||
[one] 1 descărcare
|
||||
[few] { $num } descărcări
|
||||
*[other] { $num } de descărcări
|
||||
}
|
||||
timespanHours = { $num ->
|
||||
[one] 1 oră
|
||||
[few] ore
|
||||
@@ -35,29 +40,29 @@ copyUrlFormButton = Copiază în clipboard
|
||||
copiedUrl = Copiat!
|
||||
deleteFileButton = Șterge fișierul
|
||||
sendAnotherFileLink = Trimite un alt fișier
|
||||
// Alternative text used on the download link/button (indicates an action).
|
||||
# Alternative text used on the download link/button (indicates an action).
|
||||
downloadAltText = Descarcă
|
||||
downloadsFileList = Descărcări
|
||||
// Used as header in a column indicating the amount of time left before a
|
||||
// download link expires (e.g. "10h 5m")
|
||||
# Used as header in a column indicating the amount of time left before a
|
||||
# download link expires (e.g. "10h 5m")
|
||||
timeFileList = Timp
|
||||
// Used as header in a column indicating the number of times a file has been
|
||||
// downloaded
|
||||
# Used as header in a column indicating the number of times a file has been
|
||||
# downloaded
|
||||
downloadFileName = Descarcă { $filename }
|
||||
downloadFileSize = ({ $size })
|
||||
unlockInputLabel = Introdu parola
|
||||
unlockInputPlaceholder = Parolă
|
||||
unlockButtonLabel = Deblochează
|
||||
downloadFileTitle = Descarcă fișierul criptat
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
# Firefox Send is a brand name and should not be localized.
|
||||
downloadMessage = Un prieten îți trimite un fișier prin Firefox Send, un serviciu care îți permite să împărtășești un fișier printr-un link sigur, privat și criptat care expiră automat pentru a păstra informațiile tale online doar temporar.
|
||||
// Text and title used on the download link/button (indicates an action).
|
||||
# Text and title used on the download link/button (indicates an action).
|
||||
downloadButtonLabel = Descarcă
|
||||
downloadNotification = Descărcarea s-a încheiat.
|
||||
downloadFinish = Descărcare încheiată
|
||||
// This message is displayed when uploading or downloading a file, e.g. "(1,3 MB of 10 MB)".
|
||||
# This message is displayed when uploading or downloading a file, e.g. "(1,3 MB of 10 MB)".
|
||||
fileSizeProgress = ({ $partialSize } din { $totalSize })
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
# Firefox Send is a brand name and should not be localized.
|
||||
sendYourFilesLink = Încearcă Firefox Send
|
||||
downloadingPageProgress = Se descarcă { $filename } ({ $size })
|
||||
downloadingPageMessage = Te rugăm să păstrezi această file deschisă în timp ce preluăm fișierul și îl decriptăm.
|
||||
@@ -69,7 +74,7 @@ fileTooBig = Acest fișier este prea mare. Trebuie să fie sub { $size }.
|
||||
linkExpiredAlt = Link expirat
|
||||
expiredPageHeader = Acest link a expirat sau nu a existat de la bun început!
|
||||
notSupportedHeader = Browserul tău nu este suportat.
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
# Firefox Send is a brand name and should not be localized.
|
||||
notSupportedDetail = Din păcate acest browser nu suportă tehnologii web precum Firefox Send. Trebuie să încerci alt browser. Îți recomandăm Firefox!
|
||||
notSupportedLink = De ce browserul meu nu este suportat?
|
||||
notSupportedOutdatedDetail = Din păcate această versiune de Firefox nu suportă tehnologiile web din spatele Firefox Sent. Îți recomandăm să actualizezi browserul.
|
||||
@@ -77,7 +82,7 @@ updateFirefox = Actualizează Firefox
|
||||
downloadFirefoxButtonSub = Descărcare gratuită
|
||||
uploadedFile = Fișier
|
||||
copyFileList = Copiază URL-ul
|
||||
// expiryFileList is used as a column header
|
||||
# expiryFileList is used as a column header
|
||||
expiryFileList = Expiră în
|
||||
deleteFileList = Șterge
|
||||
nevermindButton = Uită
|
||||
@@ -90,7 +95,7 @@ deletePopupCancel = Renunță
|
||||
deleteButtonHover = Șterge
|
||||
copyUrlHover = Copiază URL-ul
|
||||
footerLinkLegal = Mențiuni legale
|
||||
// Test Pilot is a proper name and should not be localized.
|
||||
# Test Pilot is a proper name and should not be localized.
|
||||
footerLinkAbout = Despre Test Pilot
|
||||
footerLinkPrivacy = Confidențialitate
|
||||
footerLinkTerms = Termeni
|
||||
@@ -99,6 +104,4 @@ requirePasswordCheckbox = Este necesară o parolă pentru a descărca acest fiș
|
||||
addPasswordButton = Adaugă parolă
|
||||
changePasswordButton = Modifică
|
||||
passwordTryAgain = Parola este incorectă. Încearcă din nou.
|
||||
// This label is followed by the password needed to download a file
|
||||
passwordResult = Parola: { $password }
|
||||
reportIPInfringement = Raportează încălcarea proprietății intelectuale
|
||||
|
||||
@@ -107,8 +107,6 @@ requirePasswordCheckbox = Zahtevaj geslo za prenos te datoteke
|
||||
addPasswordButton = Dodaj geslo
|
||||
changePasswordButton = Spremeni
|
||||
passwordTryAgain = Napačno geslo. Poskusite znova.
|
||||
# This label is followed by the password needed to download a file
|
||||
passwordResult = Geslo: { $password }
|
||||
reportIPInfringement = Prijavite kršitev naslova IP
|
||||
javascriptRequired = Firefox Send zahteva JavaScript
|
||||
whyJavascript = Zakaj Firefox Send zahteva JavaScript?
|
||||
@@ -119,3 +117,7 @@ expiresHoursMinutes = { $hours }h { $minutes }m
|
||||
expiresMinutes = { $minutes }m
|
||||
# A short status message shown when a password is successfully set
|
||||
passwordIsSet = Geslo je nastavljeno
|
||||
# A short status message shown when the user enters a long password
|
||||
maxPasswordLength = Največja dolžina gesla: { $length }
|
||||
# A short status message shown when there was an error setting the password
|
||||
passwordSetError = Gesla ni mogoče nastaviti
|
||||
|
||||
@@ -102,8 +102,6 @@ requirePasswordCheckbox = Mangailangan ng isang password upang i-download ang fi
|
||||
addPasswordButton = Magdagdag ng password
|
||||
changePasswordButton = Palitan
|
||||
passwordTryAgain = Maling password. Subukan muli.
|
||||
# This label is followed by the password needed to download a file
|
||||
passwordResult = Password: { $password }
|
||||
reportIPInfringement = Report IP Infringement
|
||||
javascriptRequired = Nangangailangan ang JavaScript sa JavaScript
|
||||
whyJavascript = Bakit ang JavaScript ay nangangailangan ng JavaScript?
|
||||
@@ -114,3 +112,7 @@ expiresHoursMinutes = { $hours }h { $minutes }m
|
||||
expiresMinutes = { $minutes }m
|
||||
# A short status message shown when a password is successfully set
|
||||
passwordIsSet = I-set ang password
|
||||
# A short status message shown when the user enters a long password
|
||||
maxPasswordLength = Pinakamataas na haba ng password: { $length }
|
||||
# A short status message shown when there was an error setting the password
|
||||
passwordSetError = Hindi maitakda ang password na ito
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
# Firefox Send is a brand name and should not be localized.
|
||||
title = Firefox Send
|
||||
siteSubtitle = веб-експеримент
|
||||
siteFeedback = Відгуки
|
||||
@@ -41,23 +41,29 @@ copyUrlFormButton = Копіювати у буфер обміну
|
||||
copiedUrl = Скопійовано!
|
||||
deleteFileButton = Видалити файл
|
||||
sendAnotherFileLink = Надіслати інший файл
|
||||
// Alternative text used on the download link/button (indicates an action).
|
||||
# Alternative text used on the download link/button (indicates an action).
|
||||
downloadAltText = Завантаживи
|
||||
downloadsFileList = Завантажень
|
||||
# Used as header in a column indicating the amount of time left before a
|
||||
# download link expires (e.g. "10h 5m")
|
||||
timeFileList = Дійсний
|
||||
# Used as header in a column indicating the number of times a file has been
|
||||
# downloaded
|
||||
downloadFileName = Завантажити { $filename }
|
||||
downloadFileSize = ({ $size })
|
||||
unlockInputLabel = Введіть пароль
|
||||
unlockInputPlaceholder = Пароль
|
||||
unlockButtonLabel = Розблокувати
|
||||
downloadFileTitle = Завантажити зашифрований файл
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
# 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).
|
||||
# Text and title used on the download link/button (indicates an action).
|
||||
downloadButtonLabel = Завантажити
|
||||
downloadNotification = Ваше завантаження готово.
|
||||
downloadFinish = Завантаження готово
|
||||
// This message is displayed when uploading or downloading a file, e.g. "(1,3 MB of 10 MB)".
|
||||
# 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.
|
||||
# Firefox Send is a brand name and should not be localized.
|
||||
sendYourFilesLink = Спробуйте Firefox Send
|
||||
downloadingPageProgress = Завантаження { $filename } ({ $size })
|
||||
downloadingPageMessage = Будь ласка, залиште цю вкладку відкритою, поки ми завантажуємо ваш файл і розшифровуємо його.
|
||||
@@ -69,7 +75,7 @@ fileTooBig = Цей файл завеликий для вивантаження.
|
||||
linkExpiredAlt = Час дії посилання минув
|
||||
expiredPageHeader = Посилання не існує, або час його дії минув!
|
||||
notSupportedHeader = Ваш браузер не підтримується.
|
||||
// Firefox Send is a brand name and should not be localized.
|
||||
# Firefox Send is a brand name and should not be localized.
|
||||
notSupportedDetail = На жаль, цей браузер не підтримує веб-технологію, завдяки якій працює Firefox Send. Вам треба скористатися іншим браузером. Ми рекомендуємо Firefox!
|
||||
notSupportedLink = Чому мій браузер не підтримується?
|
||||
notSupportedOutdatedDetail = На жаль, ця версія Firefox не підтримує веб-технологію, завдяки якій працює Firefox Send. Вам потрібно оновити свій браузер.
|
||||
@@ -77,7 +83,7 @@ updateFirefox = Оновити Firefox
|
||||
downloadFirefoxButtonSub = Безкоштовне завантаження
|
||||
uploadedFile = Файл
|
||||
copyFileList = Копіювати URL
|
||||
// expiryFileList is used as a column header
|
||||
# expiryFileList is used as a column header
|
||||
expiryFileList = Термін дії закінчується
|
||||
deleteFileList = Видалити
|
||||
nevermindButton = Не важливо
|
||||
@@ -90,14 +96,26 @@ deletePopupCancel = Скасувати
|
||||
deleteButtonHover = Видалити
|
||||
copyUrlHover = Копіювати URL
|
||||
footerLinkLegal = Права
|
||||
// Test Pilot is a proper name and should not be localized.
|
||||
# Test Pilot is a proper name and should not be localized.
|
||||
footerLinkAbout = Про Test Pilot
|
||||
footerLinkPrivacy = Приватність
|
||||
footerLinkTerms = Умови
|
||||
footerLinkCookies = Куки
|
||||
requirePasswordCheckbox = Вимагати пароль для завантаження цього файлу
|
||||
addPasswordButton = Додати пароль
|
||||
changePasswordButton = Змінити
|
||||
passwordTryAgain = Невірний пароль. Спробуйте знову.
|
||||
// This label is followed by the password needed to download a file
|
||||
passwordResult = Пароль: { $password }
|
||||
reportIPInfringement = Повідомити про порушення прав на інтелектуальну власність
|
||||
javascriptRequired = Firefox Send потребує JavaScript
|
||||
whyJavascript = Чому для Firefox Send потрібен JavaScript?
|
||||
enableJavascript = Будь ласка, увімкніть JavaScript та спробуйте знову.
|
||||
# A short representation of a countdown timer containing the number of hours and minutes remaining as digits, example "13h 47m"
|
||||
expiresHoursMinutes = { $hours } год. { $minutes } хв.
|
||||
# A short representation of a countdown timer containing the number of minutes remaining as digits, example "56m"
|
||||
expiresMinutes = { $minutes } хв.
|
||||
# A short status message shown when a password is successfully set
|
||||
passwordIsSet = Пароль встановлено
|
||||
# A short status message shown when the user enters a long password
|
||||
maxPasswordLength = Найбільша довжина паролю: { $length }
|
||||
# A short status message shown when there was an error setting the password
|
||||
passwordSetError = Неможливо встановити цей пароль
|
||||
|
||||
@@ -69,6 +69,7 @@ module.exports = function(state, body = '') {
|
||||
<script defer src="${assets.get('runtime.js')}"></script>
|
||||
<script defer src="${assets.get('vendor.js')}"></script>
|
||||
<script defer src="${locales.get(state.locale)}"></script>
|
||||
<script defer src="${assets.get('cryptofill.js')}"></script>
|
||||
<script defer src="${assets.get('app.js')}"></script>
|
||||
</head>
|
||||
${body}
|
||||
|
||||
@@ -4,6 +4,10 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { negotiateLanguages } = require('fluent-langneg');
|
||||
const langData = require('cldr-core/supplemental/likelySubtags.json');
|
||||
|
||||
// We return early in the middleware if the lang header is long.
|
||||
// If that ever changes we should re-evaluate this regex.
|
||||
// eslint-disable-next-line security/detect-unsafe-regex
|
||||
const acceptLanguages = /(([a-zA-Z]+(-[a-zA-Z0-9]+){0,2})|\*)(;q=[0-1](\.[0-9]+)?)?/g;
|
||||
|
||||
function allLangs() {
|
||||
|
||||
19
server/readme.md
Normal file
19
server/readme.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# Server Code
|
||||
|
||||
The server provides the API, serves static assets, and renders the pages for Send. The production entrypoint is [prod.js](./prod.js) and the development entrypoint is [dev.js](./dev.js) via `webpack-dev-server`.
|
||||
|
||||
## Server configuration
|
||||
|
||||
[config.js](./config.js) contains the schema for our configuration options. Environment variables are the preferred method for setting configuration.
|
||||
|
||||
## Middleware
|
||||
|
||||
Contains authentication and localization middleware.
|
||||
|
||||
## Routes
|
||||
|
||||
Contains all the server routes and handlers for the API and pages
|
||||
|
||||
## Storage
|
||||
|
||||
Contains implementations of possible storage engines for the files and metadata
|
||||
@@ -7,10 +7,10 @@ function kv(f) {
|
||||
|
||||
module.exports = function() {
|
||||
const files = fs.readdirSync(path.join(__dirname, 'tests'));
|
||||
const code = files.map(kv).join(';\n');
|
||||
const code = "require('fast-text-encoding');\n" + files.map(kv).join(';\n');
|
||||
return {
|
||||
code,
|
||||
dependencies: files.map(f => require.resolve('./tests/' + f)),
|
||||
cacheable: false
|
||||
cacheable: true
|
||||
};
|
||||
};
|
||||
|
||||
@@ -29,6 +29,7 @@ module.exports = function(app) {
|
||||
})
|
||||
</script>
|
||||
<script src="/jsconfig.js"></script>
|
||||
<script src="${assets.get('cryptofill.js')}"></script>
|
||||
<script src="${assets.get('runtime.js')}"></script>
|
||||
<script src="${assets.get('vendor.js')}"></script>
|
||||
<script src="${assets.get('tests.js')}"></script>
|
||||
|
||||
122
test/frontend/tests/crypto-tests.js
Normal file
122
test/frontend/tests/crypto-tests.js
Normal file
@@ -0,0 +1,122 @@
|
||||
import assert from 'assert';
|
||||
import { arrayToB64, b64ToArray } from '../../../app/utils';
|
||||
|
||||
describe('webcrypto', function() {
|
||||
it('can do it', async function() {
|
||||
const encoder = new TextEncoder();
|
||||
const x = b64ToArray('SPIfAlwbnncIFw3hEHYihw');
|
||||
const a = await crypto.subtle.importKey('raw', x, 'PBKDF2', false, [
|
||||
'deriveKey'
|
||||
]);
|
||||
|
||||
const ad = await crypto.subtle.deriveKey(
|
||||
{
|
||||
name: 'PBKDF2',
|
||||
salt: encoder.encode('metadata'),
|
||||
iterations: 100,
|
||||
hash: 'SHA-256'
|
||||
},
|
||||
a,
|
||||
{
|
||||
name: 'AES-GCM',
|
||||
length: 128
|
||||
},
|
||||
false,
|
||||
['encrypt', 'decrypt']
|
||||
);
|
||||
|
||||
const ae = await crypto.subtle.encrypt(
|
||||
{
|
||||
name: 'AES-GCM',
|
||||
iv: new Uint8Array(12),
|
||||
tagLength: 128
|
||||
},
|
||||
ad,
|
||||
encoder.encode('hello world!')
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
arrayToB64(new Uint8Array(ae)),
|
||||
'UXQQ4yVf55TRk9AZtz5QCwFofRvh-HdWJyxSCQ'
|
||||
);
|
||||
|
||||
const ah = await crypto.subtle.deriveKey(
|
||||
{
|
||||
name: 'PBKDF2',
|
||||
salt: encoder.encode('authentication'),
|
||||
iterations: 100,
|
||||
hash: 'SHA-256'
|
||||
},
|
||||
a,
|
||||
{
|
||||
name: 'HMAC',
|
||||
hash: { name: 'SHA-256' }
|
||||
},
|
||||
true,
|
||||
['sign']
|
||||
);
|
||||
const ahx = await crypto.subtle.exportKey('raw', ah);
|
||||
assert.equal(
|
||||
arrayToB64(new Uint8Array(ahx)),
|
||||
'wxXDmHgmMgrcDVD8zbDLRl2yNa8jSAQgsaeIBZ4vueygpxzaTK6ZE_6X-XHvllBly6pSuFNbSxcve0ZHhVdcEA'
|
||||
);
|
||||
// const jwk = await crypto.subtle.exportKey('jwk', ah)
|
||||
// console.error(jwk)
|
||||
const as = await crypto.subtle.sign(
|
||||
{
|
||||
name: 'HMAC'
|
||||
},
|
||||
ah,
|
||||
encoder.encode('test')
|
||||
);
|
||||
assert.equal(
|
||||
arrayToB64(new Uint8Array(as)),
|
||||
'AOi4HcoCJxQ4nUYxlmHB1rlcxQBn-zVjrSHz-VW7S-I'
|
||||
);
|
||||
|
||||
const b = await crypto.subtle.importKey('raw', x, 'HKDF', false, [
|
||||
'deriveKey'
|
||||
]);
|
||||
const bd = await crypto.subtle.deriveKey(
|
||||
{
|
||||
name: 'HKDF',
|
||||
salt: new Uint8Array(),
|
||||
info: encoder.encode('encryption'),
|
||||
hash: 'SHA-256'
|
||||
},
|
||||
b,
|
||||
{
|
||||
name: 'AES-GCM',
|
||||
length: 128
|
||||
},
|
||||
true,
|
||||
['encrypt', 'decrypt']
|
||||
);
|
||||
const bdx = await crypto.subtle.exportKey('raw', bd);
|
||||
|
||||
assert.equal(arrayToB64(new Uint8Array(bdx)), 'g7okjWWO9yueDz16-owShQ');
|
||||
|
||||
const bh = await crypto.subtle.deriveKey(
|
||||
{
|
||||
name: 'HKDF',
|
||||
salt: new Uint8Array(),
|
||||
info: encoder.encode('authentication'),
|
||||
hash: 'SHA-256'
|
||||
},
|
||||
b,
|
||||
{
|
||||
name: 'HMAC',
|
||||
hash: { name: 'SHA-256' }
|
||||
},
|
||||
true,
|
||||
['sign']
|
||||
);
|
||||
|
||||
const bhx = await crypto.subtle.exportKey('raw', bh);
|
||||
|
||||
assert.equal(
|
||||
arrayToB64(new Uint8Array(bhx)),
|
||||
'TQOGtmQ8-ZfnWu6Iq-U1IAVBVREFuI17xqsW1shiC8eMCa-a5qeYTvoX3-5kCoCha8R59ycnPDnTz75clLBmbQ'
|
||||
);
|
||||
});
|
||||
});
|
||||
17
test/readme.md
Normal file
17
test/readme.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# Tests
|
||||
|
||||
To run all the tests use `npm test`. This will run the tests and produce a code coverage report at [coverage/index.html](../coverage/index.html). The full test suite is run as a hook on each `git push`. [Mocha](https://mochajs.org) is our preferred test runner.
|
||||
|
||||
## Frontend
|
||||
|
||||
Unit tests reside in `test/frontend/tests`.
|
||||
|
||||
Frontend tests can be ran in the browser by running `npm start` and then browsing to http://localhost:8080/test. Doing it this way will watch for changes and rerun the suite automatically.
|
||||
|
||||
You can also run them in headless Chrome by using `npm run test:frontend`. The results will be printed to the console.
|
||||
|
||||
## Backend
|
||||
|
||||
Unit tests reside in `test/backend`
|
||||
|
||||
Backend test can be run with `npm run test:backend`. [Sinon](http://sinonjs.org/) and [proxyquire](https://github.com/thlorenz/proxyquire) are used for mocking.
|
||||
@@ -1 +0,0 @@
|
||||
This is a test.
|
||||
@@ -8,10 +8,13 @@ const IS_DEV = process.env.NODE_ENV === 'development';
|
||||
const regularJSOptions = {
|
||||
babelrc: false,
|
||||
presets: [['env', { modules: false }], 'stage-2'],
|
||||
// yo-yoify converts html template strings to direct dom api calls
|
||||
plugins: ['yo-yoify']
|
||||
};
|
||||
|
||||
const entry = {
|
||||
// babel-polyfill and fluent are directly included in vendor
|
||||
// because they are not explicitly referenced by app
|
||||
vendor: ['babel-polyfill', 'fluent'],
|
||||
app: ['./app/main.js'],
|
||||
style: ['./app/main.css']
|
||||
@@ -19,6 +22,7 @@ const entry = {
|
||||
|
||||
if (IS_DEV) {
|
||||
entry.tests = ['./test/frontend/index.js'];
|
||||
// istanbul instruments the source for code coverage
|
||||
regularJSOptions.plugins.push('istanbul');
|
||||
}
|
||||
|
||||
@@ -47,6 +51,7 @@ module.exports = {
|
||||
]
|
||||
},
|
||||
{
|
||||
// inlines version from package.json into header/index.js
|
||||
include: require.resolve('./app/templates/header'),
|
||||
use: [
|
||||
{
|
||||
@@ -57,6 +62,8 @@ module.exports = {
|
||||
]
|
||||
},
|
||||
{
|
||||
// fluent gets exposed as a global so that each language script
|
||||
// can load independently and share it.
|
||||
include: [path.dirname(require.resolve('fluent'))],
|
||||
use: [
|
||||
{
|
||||
@@ -76,6 +83,8 @@ module.exports = {
|
||||
include: [
|
||||
path.resolve(__dirname, 'app'),
|
||||
path.resolve(__dirname, 'common'),
|
||||
// some dependencies need to get re-babeled because we
|
||||
// have different targets than their default configs
|
||||
path.resolve(__dirname, 'node_modules/testpilot-ga/src'),
|
||||
path.resolve(__dirname, 'node_modules/fluent-intl-polyfill'),
|
||||
path.resolve(__dirname, 'node_modules/intl-pluralrules')
|
||||
@@ -83,6 +92,7 @@ module.exports = {
|
||||
options: regularJSOptions
|
||||
},
|
||||
{
|
||||
// Strip asserts from our deps, mainly choojs family
|
||||
include: [path.resolve(__dirname, 'node_modules')],
|
||||
loader: 'webpack-unassert-loader'
|
||||
}
|
||||
@@ -108,15 +118,16 @@ module.exports = {
|
||||
loader: 'svgo-loader',
|
||||
options: {
|
||||
plugins: [
|
||||
{ removeViewBox: false },
|
||||
{ convertStyleToAttrs: true },
|
||||
{ removeTitle: true }
|
||||
{ removeViewBox: false }, // true causes stretched images
|
||||
{ convertStyleToAttrs: true }, // for CSP, no unsafe-eval
|
||||
{ removeTitle: true } // for smallness
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
// creates style.css with all styles
|
||||
test: /\.css$/,
|
||||
use: ExtractTextPlugin.extract({
|
||||
use: [
|
||||
@@ -129,6 +140,7 @@ module.exports = {
|
||||
})
|
||||
},
|
||||
{
|
||||
// creates version.json for /__version__ from package.json
|
||||
test: require.resolve('./package.json'),
|
||||
use: [
|
||||
{
|
||||
@@ -142,6 +154,7 @@ module.exports = {
|
||||
]
|
||||
},
|
||||
{
|
||||
// creates a js script for each ftl
|
||||
test: /\.ftl$/,
|
||||
use: [
|
||||
{
|
||||
@@ -155,14 +168,17 @@ module.exports = {
|
||||
]
|
||||
},
|
||||
{
|
||||
// creates test.js for /test
|
||||
test: require.resolve('./test/frontend/index.js'),
|
||||
use: ['babel-loader', 'val-loader']
|
||||
},
|
||||
{
|
||||
// loads all assets from assets/ for use by common/assets.js
|
||||
test: require.resolve('./build/generate_asset_map.js'),
|
||||
use: ['babel-loader', 'val-loader']
|
||||
},
|
||||
{
|
||||
// loads all the ftl from public/locales for use by common/locales.js
|
||||
test: require.resolve('./build/generate_l10n_map.js'),
|
||||
use: ['babel-loader', 'val-loader']
|
||||
}
|
||||
@@ -175,8 +191,8 @@ module.exports = {
|
||||
from: '*.*'
|
||||
}
|
||||
]),
|
||||
new webpack.IgnorePlugin(/dist/),
|
||||
new webpack.IgnorePlugin(/require-from-string/),
|
||||
new webpack.IgnorePlugin(/dist/), // used in common/*.js
|
||||
new webpack.IgnorePlugin(/require-from-string/), // used in common/locales.js
|
||||
new webpack.HashedModuleIdsPlugin(),
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
name: 'vendor',
|
||||
@@ -188,10 +204,11 @@ module.exports = {
|
||||
new ExtractTextPlugin({
|
||||
filename: 'style.[contenthash:8].css'
|
||||
}),
|
||||
new ManifestPlugin()
|
||||
new ManifestPlugin() // used by server side to resolve hashed assets
|
||||
],
|
||||
devServer: {
|
||||
compress: true,
|
||||
host: '0.0.0.0',
|
||||
before: IS_DEV ? require('./server/dev') : undefined
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user