diff --git a/app/experiments.js b/app/experiments.js
index 0072f93d..37bce4e5 100644
--- a/app/experiments.js
+++ b/app/experiments.js
@@ -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) {
diff --git a/app/main.js b/app/main.js
index 7e47e13a..2c55e0ed 100644
--- a/app/main.js
+++ b/app/main.js
@@ -47,4 +47,4 @@ app.use(fileManager);
app.use(dragManager);
app.use(experiments);
-app.mount('#page-one');
+app.mount('body');
diff --git a/app/metrics.js b/app/metrics.js
index 186b7c93..3f3adf4e 100644
--- a/app/metrics.js
+++ b/app/metrics.js
@@ -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 => {
diff --git a/app/routes/index.js b/app/routes/index.js
index 08b84705..12b33c1d 100644
--- a/app/routes/index.js
+++ b/app/routes/index.js
@@ -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`
+ ${state.promo === 'header' ? fxPromo(state, emit) : ''}
+ ${header(state)}
+
+ ${footer(state)}
+ `;
+ 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;
diff --git a/app/templates/blank.js b/app/templates/blank.js
index 377b9817..080a3232 100644
--- a/app/templates/blank.js
+++ b/app/templates/blank.js
@@ -1,9 +1,6 @@
const html = require('choo/html');
-module.exports = function(state) {
+module.exports = function() {
const div = html``;
- if (state.layout) {
- return state.layout(state, div);
- }
return div;
};
diff --git a/app/templates/completed.js b/app/templates/completed.js
index 18d0ce13..751b9de7 100644
--- a/app/templates/completed.js
+++ b/app/templates/completed.js
@@ -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`
+
${state.translate(
@@ -19,6 +21,8 @@ module.exports = function(state, emit) {
sendNew
}>${state.translate('sendYourFilesLink')}
+ ${state.promo === 'body' ? fxPromo(state, emit) : ''}
+
`;
async function sendNew(e) {
diff --git a/app/templates/download.js b/app/templates/download.js
index 38fff942..7551eb5f 100644
--- a/app/templates/download.js
+++ b/app/templates/download.js
@@ -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`
+
${state.translate(
'downloadingPageProgress',
@@ -21,6 +23,8 @@ module.exports = function(state) {
transfer.sizes
)}
+
+ ${state.promo === 'body' ? fxPromo(state, emit) : ''}
`;
diff --git a/app/templates/footer.js b/app/templates/footer.js
new file mode 100644
index 00000000..795cdfda
--- /dev/null
+++ b/app/templates/footer.js
@@ -0,0 +1,31 @@
+const html = require('choo/html');
+const assets = require('../../common/assets');
+
+module.exports = function(state) {
+ return html``;
+};
diff --git a/app/templates/fxPromo.js b/app/templates/fxPromo.js
new file mode 100644
index 00000000..5098314d
--- /dev/null
+++ b/app/templates/fxPromo.js
@@ -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>/g, (m, v) => {
+// i++;
+// return `${v}`;
+// });
+// return [`
${s}`];
+// }
+
+module.exports = function(state, emit) {
+ // function close() {
+ // document.querySelector('.banner').remove();
+ // }
+
+ function clicked(evt) {
+ emit('exit', evt);
+ }
+
+ return html`
+
`;
+};
+
+/*
+
+*/
diff --git a/app/templates/header.js b/app/templates/header.js
new file mode 100644
index 00000000..edd39d7a
--- /dev/null
+++ b/app/templates/header.js
@@ -0,0 +1,21 @@
+const html = require('choo/html');
+const assets = require('../../common/assets');
+
+module.exports = function(state) {
+ return html``;
+};
diff --git a/app/templates/legal.js b/app/templates/legal.js
index 9fa3f0f5..af196ab9 100644
--- a/app/templates/legal.js
+++ b/app/templates/legal.js
@@ -30,9 +30,5 @@ module.exports = function(state) {
`;
-
- if (state.layout) {
- return state.layout(state, div);
- }
return div;
};
diff --git a/app/templates/notFound.js b/app/templates/notFound.js
index 5dc8fba3..c29bf194 100644
--- a/app/templates/notFound.js
+++ b/app/templates/notFound.js
@@ -17,9 +17,5 @@ module.exports = function(state) {
)}
`;
-
- if (state.layout) {
- return state.layout(state, div);
- }
return div;
};
diff --git a/app/templates/preview.js b/app/templates/preview.js
index 93129d1b..cb5352ac 100644
--- a/app/templates/preview.js
+++ b/app/templates/preview.js
@@ -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) {
${state.translate('sendYourFilesLink')}
+ ${state.promo === 'body' ? fxPromo(state, emit) : ''}
`;
@@ -68,9 +70,5 @@ module.exports = function(state, emit) {
event.preventDefault();
emit('download', fileInfo);
}
-
- if (state.layout) {
- return state.layout(state, div);
- }
return div;
};
diff --git a/app/templates/unsupported.js b/app/templates/unsupported.js
index a4a7a60d..f37c9bd5 100644
--- a/app/templates/unsupported.js
+++ b/app/templates/unsupported.js
@@ -42,9 +42,5 @@ module.exports = function(state) {
)}
`;
const div = html`${msg}
`;
-
- if (state.layout) {
- return state.layout(state, div);
- }
return div;
};
diff --git a/app/templates/welcome.js b/app/templates/welcome.js
index 3bac7f75..0f004608 100644
--- a/app/templates/welcome.js
+++ b/app/templates/welcome.js
@@ -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')}
+ ${state.promo === 'body' ? fxPromo(state, emit) : ''}
${fileList(state, emit)}
`;
@@ -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;
};
diff --git a/app/utils.js b/app/utils.js
index ca261322..06f480f9 100644
--- a/app/utils.js
+++ b/app/utils.js
@@ -108,7 +108,8 @@ function bytes(num) {
let nStr = n.toFixed(1);
if (LOCALIZE_NUMBERS) {
try {
- nStr = n.toLocaleString(navigator.languages.map(l => l.split(';')[0]), {
+ const locale = document.querySelector('html').lang;
+ nStr = n.toLocaleString(locale, {
minimumFractionDigits: 1,
maximumFractionDigits: 1
});
@@ -122,10 +123,8 @@ function bytes(num) {
function percent(ratio) {
if (LOCALIZE_NUMBERS) {
try {
- return ratio.toLocaleString(
- navigator.languages.map(l => l.split(';')[0]),
- { style: 'percent' }
- );
+ const locale = document.querySelector('html').lang;
+ return ratio.toLocaleString(locale, { style: 'percent' });
} catch (e) {
// fall through
}
diff --git a/assets/firefox_logo-only.svg b/assets/firefox_logo-only.svg
index 2fc8eedd..4b330869 100644
--- a/assets/firefox_logo-only.svg
+++ b/assets/firefox_logo-only.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/assets/main.css b/assets/main.css
index 12d4af57..6971a32d 100644
--- a/assets/main.css
+++ b/assets/main.css
@@ -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;
diff --git a/package.json b/package.json
index ea7a9776..7235fb8e 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/server/layout.js b/server/layout.js
index 0c5e65b6..18bef3cd 100644
--- a/server/layout.js
+++ b/server/layout.js
@@ -8,7 +8,7 @@ module.exports = function(state, body = '') {
: '';
return html`
-
+
@@ -31,7 +31,7 @@ module.exports = function(state, body = '') {
${state.title}
-
+
@@ -62,8 +62,8 @@ module.exports = function(state, body = '') {
-
-
+
+
${firaTag}
@@ -71,58 +71,7 @@ module.exports = function(state, body = '') {
-
-
-
-
-
+ ${body}
`;
};