mirror of
https://gitlab.com/timvisee/send.git
synced 2025-12-06 14:10:53 +03:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
490a1e88eb | ||
|
|
2f8a3c9904 | ||
|
|
e7fdf76120 | ||
|
|
d0d41b743a | ||
|
|
a2995411d6 | ||
|
|
3246c4a621 | ||
|
|
48faf929a4 | ||
|
|
b7f922a999 | ||
|
|
bfcdf9340d | ||
|
|
4ed515f5a3 | ||
|
|
84b2737ffb | ||
|
|
deabca5a94 | ||
|
|
e9a49e23e8 |
@@ -1,6 +1,28 @@
|
||||
import hash from 'string-hash';
|
||||
|
||||
const experiments = {};
|
||||
const experiments = {
|
||||
'SyI-hI7gT9agiH-f3f0BYg': {
|
||||
id: 'SyI-hI7gT9agiH-f3f0BYg',
|
||||
run: function(variant, state, emitter) {
|
||||
state.promo = variant === 1 ? 'body' : 'header';
|
||||
emitter.emit('render');
|
||||
},
|
||||
eligible: function() {
|
||||
return (
|
||||
!/firefox/i.test(navigator.userAgent) &&
|
||||
document.querySelector('html').lang === 'en-US'
|
||||
);
|
||||
},
|
||||
variant: function(state) {
|
||||
return this.luckyNumber(state) > 0.5 ? 1 : 0;
|
||||
},
|
||||
luckyNumber: function(state) {
|
||||
return luckyNumber(
|
||||
`${this.id}:${state.storage.get('testpilot_ga__cid')}`
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//Returns a number between 0 and 1
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
@@ -32,12 +54,12 @@ export default function initialize(state, emitter) {
|
||||
checkExperiments(state, emitter);
|
||||
});
|
||||
} else {
|
||||
const enrolled = state.storage.enrolled;
|
||||
enrolled.forEach(([id, variant]) => {
|
||||
const enrolled = state.storage.enrolled.filter(([id, variant]) => {
|
||||
const xp = experiments[id];
|
||||
if (xp) {
|
||||
xp.run(variant, state, emitter);
|
||||
}
|
||||
return !!xp;
|
||||
});
|
||||
// single experiment per session for now
|
||||
if (enrolled.length === 0) {
|
||||
|
||||
@@ -47,4 +47,4 @@ app.use(fileManager);
|
||||
app.use(dragManager);
|
||||
app.use(experiments);
|
||||
|
||||
app.mount('#page-one');
|
||||
app.mount('body');
|
||||
|
||||
@@ -20,7 +20,7 @@ let experiment = null;
|
||||
export default function initialize(state, emitter) {
|
||||
appState = state;
|
||||
emitter.on('DOMContentLoaded', () => {
|
||||
addExitHandlers();
|
||||
// addExitHandlers();
|
||||
experiment = storage.enrolled[0];
|
||||
sendEvent(category(), 'visit', {
|
||||
cm5: storage.totalUploads,
|
||||
@@ -29,6 +29,9 @@ export default function initialize(state, emitter) {
|
||||
});
|
||||
//TODO restart handlers... somewhere
|
||||
});
|
||||
emitter.on('exit', evt => {
|
||||
exitEvent(evt);
|
||||
});
|
||||
}
|
||||
|
||||
function category() {
|
||||
@@ -81,6 +84,8 @@ function urlToMetric(url) {
|
||||
case 'https://testpilot.firefox.com/':
|
||||
case 'https://testpilot.firefox.com/experiments/send':
|
||||
return 'testpilot';
|
||||
case 'https://www.mozilla.org/firefox/new/?utm_campaign=send-acquisition&utm_medium=referral&utm_source=send.firefox.com':
|
||||
return 'promo';
|
||||
default:
|
||||
return 'other';
|
||||
}
|
||||
@@ -244,6 +249,7 @@ function exitEvent(target) {
|
||||
});
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function addExitHandlers() {
|
||||
const links = Array.from(document.querySelectorAll('a'));
|
||||
links.forEach(l => {
|
||||
|
||||
@@ -1,17 +1,43 @@
|
||||
const choo = require('choo');
|
||||
const html = require('choo/html');
|
||||
const download = require('./download');
|
||||
const header = require('../templates/header');
|
||||
const footer = require('../templates/footer');
|
||||
const fxPromo = require('../templates/fxPromo');
|
||||
|
||||
const app = choo();
|
||||
|
||||
app.route('/', require('./home'));
|
||||
app.route('/share/:id', require('../templates/share'));
|
||||
app.route('/download/:id', download);
|
||||
app.route('/download/:id/:key', download);
|
||||
app.route('/completed', require('../templates/completed'));
|
||||
app.route('/unsupported/:reason', require('../templates/unsupported'));
|
||||
app.route('/legal', require('../templates/legal'));
|
||||
app.route('/error', require('../templates/error'));
|
||||
app.route('/blank', require('../templates/blank'));
|
||||
app.route('*', require('../templates/notFound'));
|
||||
function body(template) {
|
||||
return function(state, emit) {
|
||||
const b = html`<body>
|
||||
${state.promo === 'header' ? fxPromo(state, emit) : ''}
|
||||
${header(state)}
|
||||
<div class="all">
|
||||
<noscript>
|
||||
<h2>Firefox Send requires JavaScript</h2>
|
||||
<p><a href="https://github.com/mozilla/send/blob/master/docs/faq.md#why-does-firefox-send-require-javascript">Why does Firefox Send require JavaScript?</a></p>
|
||||
<p>Please enable JavaScript and try again.</p>
|
||||
</noscript>
|
||||
${template(state, emit)}
|
||||
</div>
|
||||
${footer(state)}
|
||||
</body>`;
|
||||
if (state.layout) {
|
||||
return state.layout(state, b);
|
||||
}
|
||||
return b;
|
||||
};
|
||||
}
|
||||
|
||||
app.route('/', body(require('./home')));
|
||||
app.route('/share/:id', body(require('../templates/share')));
|
||||
app.route('/download/:id', body(download));
|
||||
app.route('/download/:id/:key', body(download));
|
||||
app.route('/completed', body(require('../templates/completed')));
|
||||
app.route('/unsupported/:reason', body(require('../templates/unsupported')));
|
||||
app.route('/legal', body(require('../templates/legal')));
|
||||
app.route('/error', body(require('../templates/error')));
|
||||
app.route('/blank', body(require('../templates/blank')));
|
||||
app.route('*', body(require('../templates/notFound')));
|
||||
|
||||
module.exports = app;
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
const html = require('choo/html');
|
||||
|
||||
module.exports = function(state) {
|
||||
module.exports = function() {
|
||||
const div = html`<div id="page-one"></div>`;
|
||||
if (state.layout) {
|
||||
return state.layout(state, div);
|
||||
}
|
||||
return div;
|
||||
};
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
const html = require('choo/html');
|
||||
const progress = require('./progress');
|
||||
const { fadeOut } = require('../utils');
|
||||
const fxPromo = require('./fxPromo');
|
||||
|
||||
module.exports = function(state, emit) {
|
||||
const div = html`
|
||||
<div id="page-one">
|
||||
<div id="download" class="fadeIn">
|
||||
<div id="download-progress">
|
||||
<div id="dl-title" class="title">${state.translate(
|
||||
@@ -15,9 +17,11 @@ module.exports = function(state, emit) {
|
||||
<div class="progress-text"></div>
|
||||
</div>
|
||||
</div>
|
||||
<a class="send-new" data-state="completed" href="/" onclick=${sendNew}>${state.translate(
|
||||
'sendYourFilesLink'
|
||||
)}</a>
|
||||
<a class="send-new" data-state="completed" href="/" onclick=${
|
||||
sendNew
|
||||
}>${state.translate('sendYourFilesLink')}</a>
|
||||
</div>
|
||||
${state.promo === 'body' ? fxPromo(state, emit) : ''}
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
const html = require('choo/html');
|
||||
const progress = require('./progress');
|
||||
const { bytes } = require('../utils');
|
||||
const fxPromo = require('./fxPromo');
|
||||
|
||||
module.exports = function(state) {
|
||||
module.exports = function(state, emit) {
|
||||
const transfer = state.transfer;
|
||||
const div = html`
|
||||
<div id="page-one">
|
||||
<div id="download-progress" class="fadeIn">
|
||||
<div id="dl-title" class="title">${state.translate(
|
||||
'downloadingPageProgress',
|
||||
@@ -21,6 +23,8 @@ module.exports = function(state) {
|
||||
transfer.sizes
|
||||
)}</div>
|
||||
</div>
|
||||
</div>
|
||||
${state.promo === 'body' ? fxPromo(state, emit) : ''}
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
||||
31
app/templates/footer.js
Normal file
31
app/templates/footer.js
Normal file
@@ -0,0 +1,31 @@
|
||||
const html = require('choo/html');
|
||||
const assets = require('../../common/assets');
|
||||
|
||||
module.exports = function(state) {
|
||||
return html`<div class="footer">
|
||||
<div class="legal-links">
|
||||
<a href="https://www.mozilla.org" role="presentation"><img class="mozilla-logo" src="${assets.get(
|
||||
'mozilla-logo.svg'
|
||||
)}" alt="mozilla"/></a>
|
||||
<a href="https://www.mozilla.org/about/legal">${state.translate(
|
||||
'footerLinkLegal'
|
||||
)}</a>
|
||||
<a href="https://testpilot.firefox.com/about">${state.translate(
|
||||
'footerLinkAbout'
|
||||
)}</a>
|
||||
<a href="/legal">${state.translate('footerLinkPrivacy')}</a>
|
||||
<a href="/legal">${state.translate('footerLinkTerms')}</a>
|
||||
<a href="https://www.mozilla.org/privacy/websites/#cookies">${state.translate(
|
||||
'footerLinkCookies'
|
||||
)}</a>
|
||||
</div>
|
||||
<div class="social-links">
|
||||
<a href="https://github.com/mozilla/send" role="presentation"><img class="github" src="${assets.get(
|
||||
'github-icon.svg'
|
||||
)}" alt="github"/></a>
|
||||
<a href="https://twitter.com/FxTestPilot" role="presentation"><img class="twitter" src="${assets.get(
|
||||
'twitter-icon.svg'
|
||||
)}" alt="twitter"/></a>
|
||||
</div>
|
||||
</div>`;
|
||||
};
|
||||
44
app/templates/fxPromo.js
Normal file
44
app/templates/fxPromo.js
Normal file
@@ -0,0 +1,44 @@
|
||||
const html = require('choo/html');
|
||||
const assets = require('../../common/assets');
|
||||
|
||||
// function replaceLinks(str, urls) {
|
||||
// let i = -1;
|
||||
// const s = str.replace(/<a>([^<]+)<\/a>/g, (m, v) => {
|
||||
// i++;
|
||||
// return `<a class="link" href="${urls[i]}">${v}</a>`;
|
||||
// });
|
||||
// return [`<span>${s}</span>`];
|
||||
// }
|
||||
|
||||
module.exports = function(state, emit) {
|
||||
// function close() {
|
||||
// document.querySelector('.banner').remove();
|
||||
// }
|
||||
|
||||
function clicked(evt) {
|
||||
emit('exit', evt);
|
||||
}
|
||||
|
||||
return html`
|
||||
<div class="banner">
|
||||
<div>
|
||||
<img
|
||||
src="${assets.get('firefox_logo-only.svg')}"
|
||||
class="firefox-logo-small"
|
||||
alt="Firefox"/>
|
||||
<span>Send is brought to you by the all-new Firefox.
|
||||
<a
|
||||
class="link"
|
||||
href="https://www.mozilla.org/firefox/new/?utm_campaign=send-acquisition&utm_medium=referral&utm_source=send.firefox.com"
|
||||
onclick=${clicked}
|
||||
>Download Firefox now ≫</a></span>
|
||||
</div>
|
||||
</div>`;
|
||||
};
|
||||
|
||||
/*
|
||||
<img
|
||||
src="${assets.get('close-16.svg')}"
|
||||
class="icon-delete"
|
||||
onclick=${close}>
|
||||
*/
|
||||
21
app/templates/header.js
Normal file
21
app/templates/header.js
Normal file
@@ -0,0 +1,21 @@
|
||||
const html = require('choo/html');
|
||||
const assets = require('../../common/assets');
|
||||
|
||||
module.exports = function(state) {
|
||||
return html`<header class="header">
|
||||
<div class="send-logo">
|
||||
<a href="/">
|
||||
<img src="${assets.get(
|
||||
'send_logo.svg'
|
||||
)}" alt="Send"/><h1 class="site-title">Send</h1>
|
||||
</a>
|
||||
<div class="site-subtitle">
|
||||
<a href="https://testpilot.firefox.com">Firefox Test Pilot</a>
|
||||
<div>${state.translate('siteSubtitle')}</div>
|
||||
</div>
|
||||
</div>
|
||||
<a href="https://qsurvey.mozilla.com/s3/txp-firefox-send" rel="noreferrer noopener" class="feedback" target="_blank">${state.translate(
|
||||
'siteFeedback'
|
||||
)}</a>
|
||||
</header>`;
|
||||
};
|
||||
@@ -30,9 +30,5 @@ module.exports = function(state) {
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
if (state.layout) {
|
||||
return state.layout(state, div);
|
||||
}
|
||||
return div;
|
||||
};
|
||||
|
||||
@@ -17,9 +17,5 @@ module.exports = function(state) {
|
||||
)}</a>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
if (state.layout) {
|
||||
return state.layout(state, div);
|
||||
}
|
||||
return div;
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@ const assets = require('../../common/assets');
|
||||
const notFound = require('./notFound');
|
||||
const downloadPassword = require('./downloadPassword');
|
||||
const { bytes } = require('../utils');
|
||||
const fxPromo = require('./fxPromo');
|
||||
|
||||
function getFileFromDOM() {
|
||||
const el = document.getElementById('dl-file');
|
||||
@@ -61,6 +62,7 @@ module.exports = function(state, emit) {
|
||||
</div>
|
||||
<a class="send-new" href="/">${state.translate('sendYourFilesLink')}</a>
|
||||
</div>
|
||||
${state.promo === 'body' ? fxPromo(state, emit) : ''}
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -68,9 +70,5 @@ module.exports = function(state, emit) {
|
||||
event.preventDefault();
|
||||
emit('download', fileInfo);
|
||||
}
|
||||
|
||||
if (state.layout) {
|
||||
return state.layout(state, div);
|
||||
}
|
||||
return div;
|
||||
};
|
||||
|
||||
@@ -10,10 +10,18 @@ module.exports = function(progressRatio) {
|
||||
const percent = Math.floor(progressRatio * 100);
|
||||
const div = html`
|
||||
<div class="progress-bar">
|
||||
<svg id="progress" width="${oDiameter}" height="${oDiameter}" viewPort="0 0 ${oDiameter} ${oDiameter}" version="1.1">
|
||||
<svg id="progress" width="${oDiameter}" height="${
|
||||
oDiameter
|
||||
}" viewPort="0 0 ${oDiameter} ${oDiameter}" version="1.1">
|
||||
<circle r="${radius}" cx="${oRadius}" cy="${oRadius}" fill="transparent"/>
|
||||
<circle id="bar" r="${radius}" cx="${oRadius}" cy="${oRadius}" fill="transparent" transform="rotate(-90 ${oRadius} ${oRadius})" stroke-dasharray="${circumference}" stroke-dashoffset="${dashOffset}"/>
|
||||
<text class="percentage" text-anchor="middle" x="50%" y="98"><tspan class="percent-number">${percent}</tspan><tspan class="percent-sign">%</tspan></text>
|
||||
<circle id="bar" r="${radius}" cx="${oRadius}" cy="${
|
||||
oRadius
|
||||
}" fill="transparent" transform="rotate(-90 ${oRadius} ${
|
||||
oRadius
|
||||
})" stroke-dasharray="${circumference}" stroke-dashoffset="${dashOffset}"/>
|
||||
<text class="percentage" text-anchor="middle" x="50%" y="98"><tspan class="percent-number">${
|
||||
percent
|
||||
}</tspan><tspan class="percent-sign">%</tspan></text>
|
||||
</svg>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -42,9 +42,5 @@ module.exports = function(state) {
|
||||
)}</div>
|
||||
</div>`;
|
||||
const div = html`<div id="page-one">${msg}</div>`;
|
||||
|
||||
if (state.layout) {
|
||||
return state.layout(state, div);
|
||||
}
|
||||
return div;
|
||||
};
|
||||
|
||||
@@ -5,7 +5,9 @@ module.exports = function(state, emit) {
|
||||
const div = html`
|
||||
<div class="selectPassword">
|
||||
<div id="addPasswordWrapper">
|
||||
<input id="addPassword" type="checkbox" autocomplete="off" onchange=${togglePasswordInput}/>
|
||||
<input id="addPassword" type="checkbox" autocomplete="off" onchange=${
|
||||
togglePasswordInput
|
||||
}/>
|
||||
<label for="addPassword">
|
||||
${state.translate('requirePasswordCheckbox')}</label>
|
||||
</div>
|
||||
@@ -41,10 +43,6 @@ module.exports = function(state, emit) {
|
||||
document
|
||||
.querySelector('.setPassword')
|
||||
.classList.toggle('hidden', !boxChecked);
|
||||
document
|
||||
.getElementById('copy')
|
||||
.classList.toggle('wait-password', boxChecked);
|
||||
document.getElementById('copy-btn').disabled = boxChecked;
|
||||
if (boxChecked) {
|
||||
unlockInput.focus();
|
||||
} else {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const html = require('choo/html');
|
||||
const assets = require('../../common/assets');
|
||||
const fileList = require('./fileList');
|
||||
const fxPromo = require('./fxPromo');
|
||||
const { fadeOut } = require('../utils');
|
||||
|
||||
module.exports = function(state, emit) {
|
||||
@@ -35,6 +36,7 @@ module.exports = function(state, emit) {
|
||||
title="${state.translate('uploadPageBrowseButton1')}">
|
||||
${state.translate('uploadPageBrowseButton1')}</label>
|
||||
</div>
|
||||
${state.promo === 'body' ? fxPromo(state, emit) : ''}
|
||||
${fileList(state, emit)}
|
||||
</div>
|
||||
`;
|
||||
@@ -67,9 +69,5 @@ module.exports = function(state, emit) {
|
||||
await fadeOut('page-one');
|
||||
emit('upload', { file, type: 'click' });
|
||||
}
|
||||
|
||||
if (state.layout) {
|
||||
return state.layout(state, div);
|
||||
}
|
||||
return div;
|
||||
};
|
||||
|
||||
26
app/utils.js
26
app/utils.js
@@ -105,19 +105,31 @@ function bytes(num) {
|
||||
}
|
||||
const exponent = Math.min(Math.floor(Math.log10(num) / 3), UNITS.length - 1);
|
||||
const n = Number(num / Math.pow(1000, exponent));
|
||||
const nStr = LOCALIZE_NUMBERS
|
||||
? n.toLocaleString(navigator.languages, {
|
||||
let nStr = n.toFixed(1);
|
||||
if (LOCALIZE_NUMBERS) {
|
||||
try {
|
||||
const locale = document.querySelector('html').lang;
|
||||
nStr = n.toLocaleString(locale, {
|
||||
minimumFractionDigits: 1,
|
||||
maximumFractionDigits: 1
|
||||
})
|
||||
: n.toFixed(1);
|
||||
});
|
||||
} catch (e) {
|
||||
// fall through
|
||||
}
|
||||
}
|
||||
return `${nStr}${UNITS[exponent]}`;
|
||||
}
|
||||
|
||||
function percent(ratio) {
|
||||
return LOCALIZE_NUMBERS
|
||||
? ratio.toLocaleString(navigator.languages, { style: 'percent' })
|
||||
: `${Math.floor(ratio * 100)}%`;
|
||||
if (LOCALIZE_NUMBERS) {
|
||||
try {
|
||||
const locale = document.querySelector('html').lang;
|
||||
return ratio.toLocaleString(locale, { style: 'percent' });
|
||||
} catch (e) {
|
||||
// fall through
|
||||
}
|
||||
}
|
||||
return `${Math.floor(ratio * 100)}%`;
|
||||
}
|
||||
|
||||
function allowedCopy() {
|
||||
|
||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 239 KiB After Width: | Height: | Size: 16 KiB |
@@ -8,7 +8,6 @@ html {
|
||||
background-repeat: no-repeat;
|
||||
background-position: center top;
|
||||
height: 100%;
|
||||
max-width: 1440px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
@@ -130,7 +129,7 @@ body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
max-width: 630px;
|
||||
max-width: 650px;
|
||||
margin: 0 auto;
|
||||
padding: 0 20px;
|
||||
box-sizing: border-box;
|
||||
@@ -213,7 +212,7 @@ a {
|
||||
|
||||
.upload-window {
|
||||
border: 3px dashed rgba(0, 148, 251, 0.5);
|
||||
margin: 0 auto;
|
||||
margin: 0 auto 10px;
|
||||
height: 255px;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
@@ -709,6 +708,10 @@ tbody {
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
.firefox-logo-small {
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
#dl-firefox,
|
||||
#update-firefox {
|
||||
margin-bottom: 181px;
|
||||
@@ -766,7 +769,7 @@ tbody {
|
||||
}
|
||||
|
||||
#download {
|
||||
margin: 0 auto;
|
||||
margin: 0 auto 30px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
@@ -955,6 +958,29 @@ tbody {
|
||||
background-position: 2px 1px;
|
||||
}
|
||||
|
||||
.banner {
|
||||
padding: 0 15px;
|
||||
height: 48px;
|
||||
background-color: #efeff1;
|
||||
color: #4a4a4f;
|
||||
font-size: 13px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.banner > div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.banner > div > span {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
@media (max-device-width: 992px), (max-width: 992px) {
|
||||
.popup .popuptext {
|
||||
left: auto;
|
||||
|
||||
2200
package-lock.json
generated
2200
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
43
package.json
43
package.json
@@ -43,66 +43,66 @@
|
||||
"node": ">=8.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^7.1.5",
|
||||
"autoprefixer": "^7.1.6",
|
||||
"babel-core": "^6.26.0",
|
||||
"babel-loader": "^7.1.2",
|
||||
"babel-plugin-yo-yoify": "^1.0.1",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"babel-preset-env": "^1.6.0",
|
||||
"babel-preset-env": "^1.6.1",
|
||||
"babel-preset-es2015": "^6.24.1",
|
||||
"babel-preset-stage-2": "^6.24.1",
|
||||
"base64-js": "^1.2.1",
|
||||
"copy-webpack-plugin": "^4.1.1",
|
||||
"cross-env": "^5.0.5",
|
||||
"copy-webpack-plugin": "^4.2.0",
|
||||
"cross-env": "^5.1.1",
|
||||
"css-loader": "^0.28.7",
|
||||
"css-mqpacker": "^6.0.1",
|
||||
"cssnano": "^3.10.0",
|
||||
"eslint": "^4.8.0",
|
||||
"eslint": "^4.10.0",
|
||||
"eslint-plugin-mocha": "^4.11.0",
|
||||
"eslint-plugin-node": "^5.2.0",
|
||||
"eslint-plugin-node": "^5.2.1",
|
||||
"eslint-plugin-security": "^1.4.0",
|
||||
"expose-loader": "^0.7.3",
|
||||
"extract-loader": "^1.0.1",
|
||||
"file-loader": "^1.1.5",
|
||||
"git-rev-sync": "^1.9.1",
|
||||
"github-changes": "^1.1.0",
|
||||
"github-changes": "^1.1.1",
|
||||
"html-loader": "^0.5.1",
|
||||
"husky": "^0.14.3",
|
||||
"lint-staged": "^4.2.3",
|
||||
"lint-staged": "^4.3.0",
|
||||
"mocha": "^3.5.3",
|
||||
"nanobus": "^4.2.0",
|
||||
"npm-run-all": "^4.1.1",
|
||||
"postcss-loader": "^2.0.6",
|
||||
"prettier": "^1.7.4",
|
||||
"nanobus": "^4.3.0",
|
||||
"npm-run-all": "^4.1.2",
|
||||
"postcss-loader": "^2.0.8",
|
||||
"prettier": "^1.8.2",
|
||||
"proxyquire": "^1.8.0",
|
||||
"raven-js": "^3.18.1",
|
||||
"raven-js": "^3.19.1",
|
||||
"redis-mock": "^0.20.0",
|
||||
"require-from-string": "^2.0.1",
|
||||
"rimraf": "^2.6.2",
|
||||
"selenium-webdriver": "^3.6.0",
|
||||
"sinon": "^4.0.1",
|
||||
"sinon": "^4.1.2",
|
||||
"string-hash": "^1.1.3",
|
||||
"stylelint-config-standard": "^17.0.0",
|
||||
"stylelint-no-unsupported-browser-features": "^1.0.1",
|
||||
"supertest": "^3.0.0",
|
||||
"testpilot-ga": "^0.3.0",
|
||||
"val-loader": "^1.0.2",
|
||||
"webpack": "^3.6.0",
|
||||
"webpack-dev-server": "^2.9.1",
|
||||
"webpack": "^3.8.1",
|
||||
"webpack-dev-server": "2.9.1",
|
||||
"webpack-manifest-plugin": "^1.3.2",
|
||||
"webpack-unassert-loader": "^1.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"aws-sdk": "^2.130.0",
|
||||
"aws-sdk": "^2.149.0",
|
||||
"body-parser": "^1.18.2",
|
||||
"choo": "^6.4.2",
|
||||
"choo": "^6.5.1",
|
||||
"cldr-core": "^32.0.0",
|
||||
"connect-busboy": "0.0.2",
|
||||
"convict": "^4.0.1",
|
||||
"express": "^4.16.2",
|
||||
"express-request-language": "^1.1.12",
|
||||
"fluent": "^0.4.1",
|
||||
"fluent-langneg": "^0.1.0",
|
||||
"helmet": "^3.8.2",
|
||||
"helmet": "^3.9.0",
|
||||
"mkdirp": "^0.5.1",
|
||||
"mozlog": "^2.1.1",
|
||||
"raven": "^2.2.1",
|
||||
@@ -112,6 +112,7 @@
|
||||
"en-US",
|
||||
"ast",
|
||||
"az",
|
||||
"bs",
|
||||
"ca",
|
||||
"cak",
|
||||
"cs",
|
||||
@@ -132,6 +133,7 @@
|
||||
"id",
|
||||
"it",
|
||||
"ja",
|
||||
"ka",
|
||||
"kab",
|
||||
"ko",
|
||||
"ms",
|
||||
@@ -145,6 +147,7 @@
|
||||
"sl",
|
||||
"sr",
|
||||
"sv-SE",
|
||||
"tl",
|
||||
"tr",
|
||||
"uk",
|
||||
"vi",
|
||||
|
||||
@@ -76,3 +76,5 @@ footerLinkTerms = الشروط
|
||||
footerLinkCookies = الكعكات
|
||||
requirePasswordCheckbox = اطلب كلمة سر لتنزيل هذا الملف
|
||||
addPasswordButton = أضِف كلمة سر
|
||||
// This label is followed by the password needed to download a file
|
||||
passwordResult = كلمة السر: { $password }
|
||||
|
||||
@@ -87,3 +87,5 @@ footerLinkCookies = Cookies
|
||||
requirePasswordCheckbox = Riquir una contraseña pa baxar esti ficheru
|
||||
addPasswordButton = Amestar contraseña
|
||||
passwordTryAgain = Contraseña incorreuta. Volvi tentalo.
|
||||
// This label is followed by the password needed to download a file
|
||||
passwordResult = Contraseña: { $password }
|
||||
|
||||
@@ -87,3 +87,5 @@ footerLinkCookies = Küpsistest
|
||||
requirePasswordCheckbox = Selle faili allalaadimiseks nõutakse parooli
|
||||
addPasswordButton = Lisa parool
|
||||
passwordTryAgain = Vale parool. Palun proovi uuesti.
|
||||
// This label is followed by the password needed to download a file
|
||||
passwordResult = Parool: { $password }
|
||||
|
||||
@@ -87,3 +87,5 @@ footerLinkCookies = Cookies
|
||||
requirePasswordCheckbox = Pri preberaní súboru vyžadovať heslo
|
||||
addPasswordButton = Pridať heslo
|
||||
passwordTryAgain = Nesprávne heslo. Skúste to znova.
|
||||
// This label is followed by the password needed to download a file
|
||||
passwordResult = Heslo: { $password }
|
||||
|
||||
@@ -31,6 +31,9 @@ sendAnotherFileLink = మరో ఫైలును పంపండి
|
||||
downloadAltText = దిగుమతి
|
||||
downloadFileName = దిగుమతి { $filename }
|
||||
downloadFileSize = ({ $size })
|
||||
unlockInputLabel = సంకేతపదాన్ని తెలపండి
|
||||
unlockInputPlaceholder = సంకేతపదం
|
||||
unlockButtonLabel = తాళం తీయి
|
||||
// Text and title used on the download link/button (indicates an action).
|
||||
downloadButtonLabel = దిగుమతి
|
||||
downloadNotification = మీ దిగుమతి పూర్తయ్యింది.
|
||||
@@ -69,3 +72,8 @@ footerLinkAbout = టెస్ట్ పైలట్ గురించి
|
||||
footerLinkPrivacy = గోప్యత
|
||||
footerLinkTerms = నియమాలు
|
||||
footerLinkCookies = కుకీలు
|
||||
requirePasswordCheckbox = ఈ ఫైల్ను దింపుకోటానికి సంకేతపదం అవసరం
|
||||
addPasswordButton = సంకేతపదం జోడించండి
|
||||
passwordTryAgain = సరికాని సంకేతపదం. మళ్ళీ ప్రయత్నించండి.
|
||||
// This label is followed by the password needed to download a file
|
||||
passwordResult = సంకేతపదం: { $password }
|
||||
|
||||
@@ -4,11 +4,9 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
function allLangs() {
|
||||
const langs = fs.readdirSync(
|
||||
return fs.readdirSync(
|
||||
path.join(__dirname, '..', 'dist', 'public', 'locales')
|
||||
);
|
||||
langs.unshift('en-US'); // default first, TODO change for fluent-langneg
|
||||
return langs;
|
||||
}
|
||||
|
||||
if (config.l10n_dev) {
|
||||
|
||||
@@ -8,7 +8,7 @@ module.exports = function(state, body = '') {
|
||||
: '';
|
||||
return html`
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<html lang="${state.locale}">
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta charset="utf-8" />
|
||||
@@ -31,7 +31,7 @@ module.exports = function(state, body = '') {
|
||||
<title>${state.title}</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="${assets.get('main.css')}" />
|
||||
|
||||
|
||||
<!-- generic favicons -->
|
||||
<link rel="icon" href="${assets.get('favicon-32.png')}" sizes="32x32">
|
||||
<link rel="icon" href="${assets.get('favicon-96.png')}" sizes="96x96">
|
||||
@@ -62,8 +62,8 @@ module.exports = function(state, body = '') {
|
||||
|
||||
<!— Windows 8.1 + IE11 and above —>
|
||||
<meta name="msapplication-config" content="/browserconfig.xml"/>
|
||||
|
||||
|
||||
|
||||
|
||||
${firaTag}
|
||||
<script defer src="/jsconfig.js"></script>
|
||||
<script defer src="${assets.get('runtime.js')}"></script>
|
||||
@@ -71,58 +71,7 @@ module.exports = function(state, body = '') {
|
||||
<script defer src="${locales.get(state.locale)}"></script>
|
||||
<script defer src="${assets.get('app.js')}"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header class="header">
|
||||
<div class="send-logo">
|
||||
<a href="/">
|
||||
<img src="${assets.get(
|
||||
'send_logo.svg'
|
||||
)}" alt="Send"/><h1 class="site-title">Send</h1>
|
||||
</a>
|
||||
<div class="site-subtitle">
|
||||
<a href="https://testpilot.firefox.com">Firefox Test Pilot</a>
|
||||
<div>${state.translate('siteSubtitle')}</div>
|
||||
</div>
|
||||
</div>
|
||||
<a href="https://qsurvey.mozilla.com/s3/txp-firefox-send" rel="noreferrer noopener" class="feedback" target="_blank">${state.translate(
|
||||
'siteFeedback'
|
||||
)}</a>
|
||||
</header>
|
||||
<div class="all">
|
||||
<noscript>
|
||||
<h2>Firefox Send requires JavaScript</h2>
|
||||
<p><a href="https://github.com/mozilla/send/blob/master/docs/faq.md#why-does-firefox-send-require-javascript">Why does Firefox Send require JavaScript?</a></p>
|
||||
<p>Please enable JavaScript and try again.</p>
|
||||
</noscript>
|
||||
${body}
|
||||
</div>
|
||||
<div class="footer">
|
||||
<div class="legal-links">
|
||||
<a href="https://www.mozilla.org" role="presentation"><img class="mozilla-logo" src="${assets.get(
|
||||
'mozilla-logo.svg'
|
||||
)}" alt="mozilla"/></a>
|
||||
<a href="https://www.mozilla.org/about/legal">${state.translate(
|
||||
'footerLinkLegal'
|
||||
)}</a>
|
||||
<a href="https://testpilot.firefox.com/about">${state.translate(
|
||||
'footerLinkAbout'
|
||||
)}</a>
|
||||
<a href="/legal">${state.translate('footerLinkPrivacy')}</a>
|
||||
<a href="/legal">${state.translate('footerLinkTerms')}</a>
|
||||
<a href="https://www.mozilla.org/privacy/websites/#cookies">${state.translate(
|
||||
'footerLinkCookies'
|
||||
)}</a>
|
||||
</div>
|
||||
<div class="social-links">
|
||||
<a href="https://github.com/mozilla/send" role="presentation"><img class="github" src="${assets.get(
|
||||
'github-icon.svg'
|
||||
)}" alt="github"/></a>
|
||||
<a href="https://twitter.com/FxTestPilot" role="presentation"><img class="twitter" src="${assets.get(
|
||||
'twitter-icon.svg'
|
||||
)}" alt="twitter"/></a>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
${body}
|
||||
</html>
|
||||
`;
|
||||
};
|
||||
|
||||
@@ -1,20 +1,40 @@
|
||||
const busboy = require('connect-busboy');
|
||||
const helmet = require('helmet');
|
||||
const bodyParser = require('body-parser');
|
||||
const requestLanguage = require('express-request-language');
|
||||
const languages = require('../languages');
|
||||
const storage = require('../storage');
|
||||
const config = require('../config');
|
||||
const pages = require('./pages');
|
||||
// const lang = require('fluent-langneg')
|
||||
const { negotiateLanguages } = require('fluent-langneg');
|
||||
const IS_DEV = config.env === 'development';
|
||||
const acceptLanguages = /(([a-zA-Z]+(-[a-zA-Z0-9]+){0,2})|\*)(;q=[0-1](\.[0-9]+)?)?/g;
|
||||
const langData = require('cldr-core/supplemental/likelySubtags.json');
|
||||
|
||||
module.exports = function(app) {
|
||||
app.use(
|
||||
requestLanguage({
|
||||
languages
|
||||
})
|
||||
);
|
||||
app.use(function(req, res, next) {
|
||||
const header = req.headers['accept-language'] || 'en-US';
|
||||
if (header.length > 255) {
|
||||
req.language = 'en-US';
|
||||
return next();
|
||||
}
|
||||
const langs = header.replace(/\s/g, '').match(acceptLanguages);
|
||||
const preferred = langs
|
||||
.map(l => {
|
||||
const parts = l.split(';');
|
||||
return {
|
||||
locale: parts[0],
|
||||
q: parts[1] ? parseFloat(parts[1].split('=')[1]) : 1
|
||||
};
|
||||
})
|
||||
.sort((a, b) => b.q - a.q)
|
||||
.map(x => x.locale);
|
||||
req.language = negotiateLanguages(preferred, languages, {
|
||||
strategy: 'lookup',
|
||||
likelySubtags: langData.supplemental.likelySubtags,
|
||||
defaultLocale: 'en-US'
|
||||
})[0];
|
||||
next();
|
||||
});
|
||||
app.use(helmet());
|
||||
app.use(
|
||||
helmet.hsts({
|
||||
|
||||
Reference in New Issue
Block a user