├── .gitignore
├── client
├── release-logo-high-res.312908b7dbc4.png
├── index.html
└── responder.js
├── index.js
├── package.json
└── server
└── index.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | bower_components/
3 |
4 |
--------------------------------------------------------------------------------
/client/release-logo-high-res.312908b7dbc4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shane-tomlinson/firstrun-example/master/client/release-logo-high-res.312908b7dbc4.png
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | module.exports = require('./server');
6 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "firstrun-example",
3 | "version": "0.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "node ./index.js"
8 | },
9 | "author": "",
10 | "license": "MPL 2.0",
11 | "dependencies": {
12 | "hapi": "~8.4.0",
13 | "bower": "~1.3.12",
14 | "nunjucks": "~1.2.0"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/server/index.js:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | var path = require('path');
6 | var hapi = require('hapi');
7 | var nunjucks = require('nunjucks');
8 |
9 | var CLIENT_DIR = path.join(__dirname, '..', 'client');
10 | nunjucks.configure(CLIENT_DIR);
11 |
12 | var HOST = '127.0.0.1';
13 | var PORT = '8111';
14 |
15 | var server = new hapi.Server();
16 | server.connection({
17 | host: HOST,
18 | port: PORT
19 | });
20 |
21 | server.path(CLIENT_DIR);
22 | server.route({
23 | method: 'GET',
24 | path: '/',
25 | handler: function (request, reply) {
26 |
27 | nunjucks.render('index.html', {
28 | bodyClass: request.query.bodyClass || '',
29 | country: request.query.country,
30 | forceExperiment: request.query.forceExperiment,
31 | forceExperimentGroup: request.query.forceExperimentGroup,
32 | pathname: request.query.pathname || '',
33 | }, function (err, res) {
34 | reply(res);
35 | });
36 | }
37 | });
38 |
39 | server.route({
40 | method: 'GET',
41 | path: '/{filename}',
42 | handler: {
43 | file: function (request) {
44 | return request.params.filename;
45 | }
46 | }
47 | });
48 |
49 | server.start();
50 | console.log('server running on ' + HOST + ':' + PORT);
51 |
--------------------------------------------------------------------------------
/client/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Example First Run
7 |
101 |
102 |
103 |
104 |
105 | Firefox First Run Experience
106 |
107 |
108 |
109 |
110 | {% if bodyClass %}
111 |
112 | {% endif %}
113 |
114 |
115 |
116 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/client/responder.js:
--------------------------------------------------------------------------------
1 | var SERVER_ORIGIN = 'http://127.0.0.1:3030';
2 | var searchParams = new URLSearchParams(window.location.search.replace(/^\?/, ''));
3 | if (searchParams.has('endpoint')) {
4 | SERVER_ORIGIN = 'https://' + searchParams.get('endpoint') + '.dev.lcip.org';
5 | }
6 | var iframe = document.querySelector('#fxa');
7 | var iframeTarget = iframe.contentWindow;
8 |
9 | window.addEventListener('message', function (e) {
10 | console.log('received a message: ', e.origin);
11 | console.log('data: ', e.data);
12 | if (e.origin === SERVER_ORIGIN) {
13 | var data = JSON.parse(e.data);
14 | if (data.command === 'ping') {
15 | iframeTarget.postMessage(e.data, SERVER_ORIGIN);
16 | } else if (data.command === 'resize') {
17 | var height = data.data.height;
18 | var newHeight = height;// + 20;
19 | iframe.setAttribute('height', newHeight + 'px');
20 | } else if (data.command === 'form_engaged') {
21 | //removeSkipEl();
22 | //disableSkipEl();
23 | } else if (data.command === 'form_disabled') {
24 | enableSkipEl();
25 | } else if (data.command === 'form_enabled') {
26 | disableSkipEl();
27 | } else if (data.command === 'login') {
28 | document.location.href = `${SERVER_ORIGIN}/settings`;
29 | } else if (data.command === 'navigated') {
30 | switch(data.data.url) {
31 | case 'signin':
32 | case 'signup':
33 | case 'reset_password':
34 | showSkipEl();
35 | break;
36 | default:
37 | removeSkipEl();
38 | break;
39 | }
40 | }
41 | }
42 | }, true);
43 |
44 |
45 | function removeSkipEl () {
46 | const skipEl = document.getElementById('skip');
47 | if (skipEl) {
48 | skipEl.style.display = 'none';
49 | /*skipEl.style.border = 'none';
50 | skipEl.style.padding = '0';
51 | skipEl.style.height = '0';
52 | skipEl.style.overflow = 'hidden';*/
53 | }
54 | }
55 |
56 | function showSkipEl () {
57 | const skipEl = document.getElementById('skip');
58 | if (skipEl) {
59 | skipEl.style.display = 'block';
60 | /*
61 | skipEl.style.border = '';
62 | skipEl.style.padding = '';
63 | skipEl.style.height = '';
64 | skipEl.style.overflow = '';
65 | */
66 | enableSkipEl();
67 |
68 | }
69 | }
70 |
71 | function enableSkipEl () {
72 | const skipEl = document.getElementById('skip');
73 | if (skipEl) {
74 | skipEl.removeAttribute('disabled');
75 | }
76 | }
77 |
78 | function disableSkipEl () {
79 | const skipEl = document.getElementById('skip');
80 | if (skipEl) {
81 | skipEl.setAttribute('disabled', 'disabled');
82 | }
83 | }
84 |
85 | const skipEl = document.getElementById('skip');
86 | if (skipEl) {
87 | skipEl.addEventListener('click', () => {
88 | alert('skip');
89 | });
90 | }
91 |
92 |
93 |
94 | const country = encodeURIComponent(iframe.getAttribute('data-country') || 'US');
95 | const forceExperiment = encodeURIComponent(iframe.getAttribute('data-forceExperiment'));
96 | const forceExperimentGroup = encodeURIComponent(iframe.getAttribute('data-forceExperimentGroup') || 'treatment');
97 | const pathname = iframe.getAttribute('data-pathname');
98 |
99 | const IFRAME_SRC = SERVER_ORIGIN + `/${pathname}?service=sync&haltAfterSignIn=true&context=fx_firstrun_v2&style=chromeless&country=${country}&forceExperiment=${forceExperiment}&forceExperimentGroup=${forceExperimentGroup}&origin=${document.location.origin}`;
100 |
101 | iframe.setAttribute('src', IFRAME_SRC);//'http://127.0.0.1:3030');
102 |
--------------------------------------------------------------------------------