├── .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 | --------------------------------------------------------------------------------