🚧 Added more settings & cleanup
@ -1,27 +0,0 @@
|
||||
[
|
||||
{
|
||||
"relation": [
|
||||
"delegate_permission/common.handle_all_urls"
|
||||
],
|
||||
"target": {
|
||||
"namespace": "android_app",
|
||||
"package_name": "com.google.android.apps.santatracker",
|
||||
"sha256_cert_fingerprints": [
|
||||
"F4:CB:0B:81:9F:14:B7:FB:BE:3E:61:3E:AF:86:01:10:B5:18:62:76:65:40:27:CD:52:5B:27:DC:23:2E:9C:8E",
|
||||
"7B:5A:D5:51:80:A4:8A:1F:30:F3:53:77:C0:F9:E5:F9:11:BF:94:2F:B4:CF:83:EB:A2:55:A0:EB:F5:80:BE:EF"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"relation": [
|
||||
"delegate_permission/common.handle_all_urls"
|
||||
],
|
||||
"target": {
|
||||
"namespace": "android_app",
|
||||
"package_name": "com.google.android.apps.santatracker.debug",
|
||||
"sha256_cert_fingerprints": [
|
||||
"89:53:FE:3E:7D:97:CF:46:7B:B1:70:C7:77:12:54:FB:59:D8:D0:9F:20:A8:32:4A:3A:9F:B0:0F:2A:7A:83:8E"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
@ -1,176 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
Copyright 2020 Google LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title msgid="meta_title"></title>
|
||||
<meta charset="utf-8" />
|
||||
<script src="//www.gstatic.com/cast/sdk/libs/caf_receiver/v3/cast_receiver_framework.js"></script>
|
||||
<style type="text/css">
|
||||
html {
|
||||
background: black;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
.op {
|
||||
transition: opacity 0.5s;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
#wrap {
|
||||
background: black;
|
||||
}
|
||||
#loader {
|
||||
background: #18854b;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 100;
|
||||
}
|
||||
body.loaded #loader {
|
||||
opacity: 0;
|
||||
}
|
||||
.gone {
|
||||
opacity: 0;
|
||||
}
|
||||
video {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="op" id="loader">
|
||||
<img src="images/icon-512.png" width="256" height="256" />
|
||||
</div>
|
||||
<div id="wrap">
|
||||
<video id="player" class="op" playsinline></video>
|
||||
<video id="playerExtra" class="op gone" playsinline></video>
|
||||
</div>
|
||||
<script type="module">
|
||||
|
||||
const allVideos = [
|
||||
'comroom',
|
||||
'carpool',
|
||||
'jingle',
|
||||
'museum',
|
||||
'office',
|
||||
'onvacation',
|
||||
'penguinproof',
|
||||
'reindeerworries',
|
||||
'reload',
|
||||
'santasback',
|
||||
'satellite',
|
||||
'selfies',
|
||||
'slackingoff',
|
||||
'temptation',
|
||||
'tired',
|
||||
'wheressanta',
|
||||
'workshop',
|
||||
];
|
||||
allVideos.sort(() => Math.random() - 0.5); // shuffle order
|
||||
|
||||
cast.framework.CastReceiverContext.getInstance().start({disableIdleTimeout: true});
|
||||
|
||||
function urlForVideo(id) {
|
||||
let quality = '1080p';
|
||||
if (id === null) {
|
||||
id = 'santafire';
|
||||
quality = '900p';
|
||||
}
|
||||
return `https://firebasestorage.googleapis.com/v0/b/santa-api.appspot.com/o/videos%2F${id}_${quality}.mp4?alt=media`;
|
||||
}
|
||||
|
||||
const player = /** @type {!HTMLVideoElement} */ (document.getElementById('player'));
|
||||
const playerExtra = /** @type {!HTMLVideoElement} */ (document.getElementById('playerExtra'));
|
||||
|
||||
player.src = urlForVideo(null);
|
||||
player.loop = true;
|
||||
player.addEventListener('canplaythrough', () => {
|
||||
document.body.classList.add('loaded');
|
||||
player.play();
|
||||
ready();
|
||||
}, {once: true});
|
||||
|
||||
// Pick between 40-90 seconds, on 10-second intervals.
|
||||
function randomTime() {
|
||||
const raw = 40 + (Math.random() * 50);
|
||||
const seconds = Math.round(raw / 10) * 10;
|
||||
return seconds * 1000;
|
||||
}
|
||||
|
||||
function ready() {
|
||||
function playNext() {
|
||||
// Videos will always shuffle the same way.
|
||||
const id = allVideos.shift();
|
||||
allVideos.push(id);
|
||||
playerExtra.src = urlForVideo(id);
|
||||
}
|
||||
|
||||
let timeout = 0;
|
||||
const enqueueNext = () => {
|
||||
window.clearTimeout(timeout);
|
||||
const time = randomTime();
|
||||
console.info('playing next video in', time);
|
||||
timeout = window.setTimeout(playNext, time);
|
||||
};
|
||||
|
||||
playerExtra.addEventListener('canplaythrough', () => {
|
||||
player.pause();
|
||||
playerExtra.play();
|
||||
player.classList.add('gone');
|
||||
playerExtra.classList.remove('gone');
|
||||
});
|
||||
|
||||
const fadeToNext = () => {
|
||||
playerExtra.removeAttribute('src');
|
||||
player.play();
|
||||
window.setTimeout(() => {
|
||||
playerExtra.classList.add('gone');
|
||||
player.classList.remove('gone');
|
||||
enqueueNext();
|
||||
}, 250);
|
||||
};
|
||||
|
||||
playerExtra.addEventListener('error', (event) => {
|
||||
console.warn('error in playerExtra', event.error, event.target.error);
|
||||
window.setTimeout(() => {
|
||||
if (playerExtra.paused) {
|
||||
fadeToNext();
|
||||
}
|
||||
}, 2000);
|
||||
});
|
||||
playerExtra.addEventListener('ended', fadeToNext);
|
||||
|
||||
enqueueNext();
|
||||
}
|
||||
|
||||
// for dev
|
||||
document.body.onclick = () => {
|
||||
player.play();
|
||||
};
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
Before Width: | Height: | Size: 413 B |
Before Width: | Height: | Size: 644 B |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 105 KiB |
Before Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 61 KiB |
Before Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 77 KiB |
Before Width: | Height: | Size: 66 KiB |
Before Width: | Height: | Size: 63 KiB |
Before Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 98 KiB |
Before Width: | Height: | Size: 82 KiB |
Before Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 66 KiB |
Before Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 165 KiB |
Before Width: | Height: | Size: 200 KiB |
Before Width: | Height: | Size: 135 KiB |
Before Width: | Height: | Size: 69 KiB |
Before Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 92 KiB |
Before Width: | Height: | Size: 119 KiB |
Before Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 64 KiB |
Before Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 110 KiB |
Before Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 83 KiB |
Before Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 82 KiB |
Before Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 138 KiB |
Before Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 74 KiB |
Before Width: | Height: | Size: 66 KiB |
Before Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 107 KiB |
Before Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 105 KiB |
Before Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 96 KiB |
Before Width: | Height: | Size: 177 KiB |
Before Width: | Height: | Size: 15 KiB |
@ -1,111 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
Copyright 2020 Google LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title data-title></title>
|
||||
<meta property="og:title" itemprop="name" data-title />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:image" itemprop="image" name="thumbnail" content="https://santatracker.google.com/images/og.png" />
|
||||
<meta property="og:image:width" content="1333" />
|
||||
<meta property="og:image:height" content="1000" />
|
||||
<meta property="og:description" itemprop="description" name="description" msgid="village_explore" />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:site" content="@google" />
|
||||
<meta name="twitter:title" data-title />
|
||||
<meta name="twitter:description" msgid="village_explore" />
|
||||
<meta name="twitter:image" content="https://santatracker.google.com/images/og.png" />
|
||||
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover" />
|
||||
<link rel="manifest" crossorigin="use-credentials" href="./manifest.json" />
|
||||
<link href="https://maps.gstatic.com" rel="preconnect" crossorigin />
|
||||
<link href="https://fonts.gstatic.com" rel="preconnect" crossorigin />
|
||||
<link href="https://santa-api.appspot.com" rel="preconnect" crossorigin />
|
||||
|
||||
<!-- Fonts -->
|
||||
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,600|Lobster|Google+Sans:400,500,700" rel="stylesheet" />
|
||||
|
||||
<!-- Icons -->
|
||||
<link rel="apple-touch-icon" href="/images/icon-76.png" sizes="76x76" />
|
||||
<link rel="apple-touch-icon" href="/images/icon-120.png" sizes="120x120" />
|
||||
<link rel="apple-touch-icon" href="/images/icon-152.png" sizes="152x152" />
|
||||
<link rel="apple-touch-icon" href="/images/icon-167.png" sizes="167x167" />
|
||||
<link rel="apple-touch-icon" href="/images/icon-180.png" sizes="180x180" />
|
||||
<link rel="apple-touch-icon" href="/images/icon-512.png" sizes="512x512" />
|
||||
<link id="favicon" rel="shortcut icon" href="/images/favicon-16.png" sizes="16x16" />
|
||||
<link id="favicon" rel="shortcut icon" href="/images/favicon-32.png" sizes="32x32" />
|
||||
<link id="favicon" rel="shortcut icon" href="/images/favicon-64.png" sizes="64x64" />
|
||||
<link id="favicon" rel="shortcut icon" href="/images/favicon-96.png" sizes="96x96" />
|
||||
|
||||
<!-- CSS -->
|
||||
<style>
|
||||
@keyframes loader-move {
|
||||
50% { transform: translateY(-100%); }
|
||||
}
|
||||
body {
|
||||
background: #1a844b;
|
||||
margin: 0;
|
||||
}
|
||||
.loader {
|
||||
position: fixed;
|
||||
top: 80%;
|
||||
left: calc(50% - 160px);
|
||||
width: 320px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.loader u {
|
||||
transform: translateY(0);
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 16px;
|
||||
margin: 5px;
|
||||
animation: loader-move 1.6s cubic-bezier(0.50, 0.1, 0.50, 1) infinite;
|
||||
}
|
||||
.loader u:nth-child(1) {
|
||||
background: #9fceff;
|
||||
animation-delay: -0.80s;
|
||||
}
|
||||
.loader u:nth-child(2) {
|
||||
background: #ffb1b1;
|
||||
animation-delay: -0.60s;
|
||||
}
|
||||
.loader u:nth-child(3) {
|
||||
background: #fff173;
|
||||
animation-delay: -0.40s;
|
||||
}
|
||||
.loader u:nth-child(4) {
|
||||
animation-delay: -0.20s;
|
||||
background: #a8e9a2;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="module" src="/loader.js"></script>
|
||||
<!-- <script type="module" src="../static/entrypoint.js"></script> -->
|
||||
</head>
|
||||
<body class="loading">
|
||||
|
||||
<!-- <noscript>
|
||||
<meta http-equiv="refresh" content="0;url=/upgrade.html" />
|
||||
</noscript> -->
|
||||
|
||||
<div class="loader"><u></u><u></u><u></u><u></u></div>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -1,149 +0,0 @@
|
||||
/**
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Loader code for Santa Tracker. Adds a <script ...> pointing to the entrypoint.
|
||||
*
|
||||
* This is transpiled down to ES5-compatible code before being run. It should not use modern
|
||||
* library code, like `Promise`, until the support library is loaded.
|
||||
*/
|
||||
|
||||
// import config from './src/:config.json';
|
||||
import checkFallback from './src/fallback.js';
|
||||
import * as load from './src/load.js';
|
||||
// import {initialize} from './src/firebase.js';
|
||||
import onInteractive from './src/interactive.js';
|
||||
import isAndroidTWA from './src/android-twa.js';
|
||||
|
||||
const config = {
|
||||
staticScope: 'http://localhost:3000'
|
||||
}
|
||||
|
||||
// In prod, the documentElement has `lang="en"` or similar.
|
||||
const documentLang = document.documentElement.lang || null;
|
||||
const isProd = (documentLang !== null);
|
||||
const fallback = checkFallback() || (location.search || '').match(/\bfallback=.*?\b/);
|
||||
const ignoreErrors = (location.search || '').match(/\bignore=.*?\b/);
|
||||
|
||||
// Global error handler. Redirect if we fail to load the entrypoint.
|
||||
let loaded = false;
|
||||
window.onerror = (msg, file, line, col, error) => {
|
||||
console.error('error (loaded=' + loaded + ')', msg, file, line, col, error);
|
||||
if (location.hostname === 'santatracker.google.com' && !loaded && !ignoreErrors) {
|
||||
window.location.href = 'error.html';
|
||||
}
|
||||
};
|
||||
window.onunhandledrejection = (event) => {
|
||||
console.warn('rejection (loaded=' + loaded + ')', event.reason);
|
||||
if (location.hostname === 'santatracker.google.com' && !loaded && !ignoreErrors) {
|
||||
window.location.href = 'error.html';
|
||||
}
|
||||
};
|
||||
|
||||
// Add this early. We get it very aggressively from Chrome and friends.
|
||||
window.installEvent = null;
|
||||
window.addEventListener('beforeinstallprompt', (event) => {
|
||||
window.installEvent = event;
|
||||
});
|
||||
|
||||
// window.sw = null;
|
||||
// let hasInstalledServiceWorker = false;
|
||||
|
||||
// if ('serviceWorker' in navigator) {
|
||||
// // Register the SW in the served language, not the request language (as this isn't available
|
||||
// // on the naked domain anyway).
|
||||
// const params = new URLSearchParams();
|
||||
// if (isProd) {
|
||||
// params.set('baseurl', config.baseurl);
|
||||
// }
|
||||
// window.sw = navigator.serviceWorker.register(`/sw.js?${params.toString()}`).catch((err) => {
|
||||
// console.warn('sw failed to register', err);
|
||||
// return null;
|
||||
// });
|
||||
// hasInstalledServiceWorker = Boolean(navigator.serviceWorker.controller);
|
||||
|
||||
// navigator.serviceWorker.addEventListener('controllerchange', () => {
|
||||
// loaded = true; // pretend that we're loaded, so that Safari doesn't send us to an error page
|
||||
// window.location.reload();
|
||||
// });
|
||||
// }
|
||||
|
||||
|
||||
// Load support code for fallback browsers like IE11, non-Chromium Edge, and friends. This is
|
||||
// needed before using Firebase, as it requires Promise and fetch. This always uses the deployed
|
||||
// static version, as we only potentially replace it below after Firebase is ready.
|
||||
if (fallback && isProd) {
|
||||
load.supportScripts([
|
||||
config.staticScope + 'support.js',
|
||||
config.staticScope + 'node_modules/@webcomponents/webcomponentsjs/webcomponents-loader.js',
|
||||
], () => {
|
||||
WebComponents.waitFor(() => {
|
||||
onInteractive(startup); // should be past DOMContentLoaded now
|
||||
});
|
||||
});
|
||||
} else {
|
||||
onInteractive(startup);
|
||||
}
|
||||
|
||||
|
||||
function sanitizeStaticScope(arg) {
|
||||
const staticScopeUrl = new URL(arg, window.location);
|
||||
if (!staticScopeUrl.pathname.match(/\/$/)) {
|
||||
staticScopeUrl.pathname += '/';
|
||||
}
|
||||
return staticScopeUrl.toString();
|
||||
}
|
||||
|
||||
|
||||
function startup() {
|
||||
// Check Android TWA, but force it if the "?android=1" param is set.
|
||||
const startParams = new URLSearchParams(window.location.search);
|
||||
isAndroidTWA(startParams.has('android'));
|
||||
|
||||
// Wait for both Firebase Remote Config and the Service Worker (optional), then load entrypoint.
|
||||
// This is racey in that a Service Worker change might trigger a reload.
|
||||
// const ready = Promise.all([initialize(), window.sw]);
|
||||
// ready.then(([remoteConfig, registration]) => {
|
||||
// if (remoteConfig.getBoolean('switchOff')) {
|
||||
// throw new Error('switchOff');
|
||||
// }
|
||||
|
||||
// Allow Firebase force or optional ?static=... for new releases.
|
||||
// const forceStaticScope = remoteConfig.getString('staticScope') || null;
|
||||
// if (forceStaticScope) {
|
||||
// if (!isProd) {
|
||||
// // This arguably makes no sense here, as the files probably have the wrong suffix (i18n),
|
||||
// // and you control your own dev environment.
|
||||
// console.warn('ignoring custom static scope for dev', forceStaticScope);
|
||||
// } else {
|
||||
// console.warn('using custom static scope', forceStaticScope);
|
||||
// try {
|
||||
// config.staticScope = sanitizeStaticScope(forceStaticScope);
|
||||
// } catch (e) {
|
||||
// // don't set an invalid URL
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
document.body.setAttribute('static', config.staticScope);
|
||||
|
||||
// Load entrypoint.
|
||||
const entrypoint = config.staticScope + (fallback ? 'fallback' : 'entrypoint') + (isProd ? '_' + documentLang : '') + '.js';
|
||||
return load.script(entrypoint, fallback && isProd ? '' : 'module').then(() => {
|
||||
loaded = true;
|
||||
});
|
||||
// });
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
{
|
||||
"name": "Google Santa Tracker",
|
||||
"short_name": "Santa",
|
||||
"start_url": "./?utm_source=web_app_manifest&utm_medium=web_app_manifest",
|
||||
"scope": "/",
|
||||
"display": "standalone",
|
||||
"orientation": "any",
|
||||
"theme_color": "#416d98",
|
||||
"background_color": "#3ec4f0",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/images/favicon-32.png",
|
||||
"sizes": "32x32",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/images/icon-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "/images/icon-256.png",
|
||||
"sizes": "256x256",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "/images/icon-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
}
|
||||
],
|
||||
"related_applications": [
|
||||
{
|
||||
"platform": "web"
|
||||
},
|
||||
{
|
||||
"platform": "play",
|
||||
"url": "https://play.google.com/store/apps/details?id=com.google.android.apps.santatracker"
|
||||
}
|
||||
]
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
Disallow: /upgrade.html
|
||||
Disallow: /cast.html
|
||||
Disallow: /error.html
|
@ -1,41 +0,0 @@
|
||||
/**
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
// Safeguard sessionStorage in case a browser's Private mode prevents use.
|
||||
export const sessionStorage = window.sessionStorage || {};
|
||||
|
||||
/**
|
||||
* Checks whether this is an Android TWA. Note that this has side-effects and persists this state
|
||||
* for the current session.
|
||||
*
|
||||
* @param {boolean=} force whether to force enable
|
||||
* @return {boolean} whether this is an Android TWA load
|
||||
*/
|
||||
export default function isAndroidTWA(force = false) {
|
||||
// NOTE: This detection may fail when the user swipes down and refreshes the page, so we
|
||||
// should persist the state somehow, e.g., local storage or URL modification. See:
|
||||
// https://stackoverflow.com/q/54580414
|
||||
if (sessionStorage['android-twa'] ||
|
||||
document.referrer.startsWith('android-app://com.google.android.apps.santatracker') ||
|
||||
force) {
|
||||
sessionStorage['android-twa'] = true;
|
||||
document.body.setAttribute('data-mode', 'android');
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
/**
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Determines load type. Santa Tracker supports modern browsers like Chrome (and Chromium-based
|
||||
* browsers), Firefox and Safari. We load a fallback environment (and polyfills) if the browser
|
||||
* does not hit minimum standards.
|
||||
*
|
||||
* @return {boolean} whether to load fallback environment
|
||||
*/
|
||||
export default function() {
|
||||
try {
|
||||
if (!('ShadowRoot' in window)) {
|
||||
throw 'Shadow DOM';
|
||||
}
|
||||
if (!('customElements' in window)) {
|
||||
throw 'Custom Elements';
|
||||
}
|
||||
if (!CSS.supports("(--foo: red)")) {
|
||||
// need CSS variable support for most modern scenes and the modern entrypoint
|
||||
throw 'CSS Variables';
|
||||
}
|
||||
if (!('noModule' in HTMLScriptElement.prototype)) {
|
||||
// modern code is loaded as modules
|
||||
throw '<script type="module">';
|
||||
}
|
||||
if (!('URLSearchParams' in window)) {
|
||||
// stops IE11
|
||||
throw 'URLSearchParams';
|
||||
}
|
||||
if (!('Symbol' in window)) {
|
||||
// stops IE11
|
||||
throw 'Symbol';
|
||||
}
|
||||
if (!('includes' in String.prototype && 'startsWith' in String.prototype && 'includes' in Array.prototype && 'from' in Array)) {
|
||||
// stops IE11 and browsers without standard niceities
|
||||
throw 'arraylike helpers';
|
||||
}
|
||||
if (!('append' in document.head)) {
|
||||
// friendly node helpers (nb. do NOT use 'body', doesn't exist yet)
|
||||
throw 'append';
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('loading fallback, failure:', e);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
/**
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Initialize function to prepare Firebase and Remote Config.
|
||||
*/
|
||||
|
||||
import firebase from 'firebase/app';
|
||||
import 'firebase/remote-config';
|
||||
import defaults from './remote-config-defaults.js';
|
||||
import isAndroidTWA from './android-twa.js';
|
||||
|
||||
export const firebaseConfig = {
|
||||
apiKey: 'AIzaSyBrNcGcna0TMn2uLRxhMBwxVwXUBjlZqzU',
|
||||
authDomain: 'santa-api.firebaseapp.com',
|
||||
databaseURL: 'https://santa-api.firebaseio.com',
|
||||
projectId: 'santa-api',
|
||||
storageBucket: 'santa-api.appspot.com',
|
||||
messagingSenderId: '593146395815',
|
||||
appId: '1:593146395815:web:d766962076fbbd13492f82',
|
||||
measurementId: 'G-EWRYGZS6D3',
|
||||
};
|
||||
|
||||
export function initialize() {
|
||||
if (isAndroidTWA()) {
|
||||
// Swap for TWA (dev and prod)
|
||||
firebaseConfig.appId = '1:593146395815:web:aefb4c5b5e01137f492f82';
|
||||
firebaseConfig.measurementId = 'G-0X2VE68GZD';
|
||||
} else if (window.location.hostname !== 'santatracker.google.com') {
|
||||
// Swap for dev
|
||||
firebaseConfig.appId = '1:593146395815:web:54c339298196fd10492f82';
|
||||
firebaseConfig.measurementId = 'G-GPEHME4LVG';
|
||||
}
|
||||
|
||||
firebase.initializeApp(firebaseConfig);
|
||||
|
||||
// Fetch RC, with a fetch timeout of 30s and a key expiry of ~1 minute. This is reset later via
|
||||
// the config itself.
|
||||
let minimumFetchIntervalMillis = 1000 * 60;
|
||||
if (!navigator.onLine) {
|
||||
// If the browser thinks we're offline, then allow a much larger range of cached keys (~12
|
||||
// hours, the default).
|
||||
minimumFetchIntervalMillis *= (12 * 60);
|
||||
}
|
||||
const remoteConfig = firebase.remoteConfig();
|
||||
remoteConfig.settings = {
|
||||
fetchTimeoutMillis: 30 * 1000,
|
||||
minimumFetchIntervalMillis,
|
||||
};
|
||||
|
||||
remoteConfig.defaultConfig = defaults;
|
||||
window.firebase = firebase; // side-effect
|
||||
|
||||
return remoteConfig.fetchAndActivate().catch((err) => {
|
||||
ga('send', 'event', 'config', 'failure', 'firebase', {nonInteraction: true});
|
||||
console.warn('could not fetch remoteConfig, using defaults', err);
|
||||
}).then(() => {
|
||||
return remoteConfig;
|
||||
});
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
/**
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
let done = (document.readyState === 'interactive' || document.readyState === 'complete');
|
||||
let all;
|
||||
|
||||
if (!done) {
|
||||
all = [];
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
done = true;
|
||||
all.forEach((fn) => fn());
|
||||
all = undefined;
|
||||
});
|
||||
}
|
||||
|
||||
export default function onInteractive(fn) {
|
||||
if (!done) {
|
||||
all.push(fn);
|
||||
} else {
|
||||
fn();
|
||||
}
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
/**
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Loads the passed script.
|
||||
*
|
||||
* @param {string} src
|
||||
* @param {?string=} type
|
||||
* @return {!Promise<void>}
|
||||
*/
|
||||
export function script(src, type=null) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const script = document.createElement('script');
|
||||
script.src = src;
|
||||
if (type) {
|
||||
script.type = type;
|
||||
}
|
||||
script.setAttribute('crossorigin', 'anonymous');
|
||||
script.onload = () => resolve();
|
||||
script.onerror = reject;
|
||||
document.head.append(script);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a number of passed scripts, without Promise. These unfortunately load in-order.
|
||||
*
|
||||
* @param {!Array<string>} scripts to load
|
||||
* @param {function(): void} callback to call when done
|
||||
*/
|
||||
export function supportScripts(scripts, callback) {
|
||||
const next = () => {
|
||||
const src = scripts.shift();
|
||||
if (src === undefined) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
const script = document.createElement('script');
|
||||
script.src = src;
|
||||
script.setAttribute('crossorigin', 'anonymous');
|
||||
|
||||
script.onload = next;
|
||||
script.onerror = () => {
|
||||
console.warn('cannot load', src);
|
||||
next();
|
||||
};
|
||||
document.head.appendChild(script);
|
||||
};
|
||||
|
||||
next();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} src to load as global CSS
|
||||
* @return {!Promise<void>}
|
||||
*/
|
||||
export function css(src) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const css = document.createElement('link');
|
||||
css.href = src;
|
||||
css.type = 'stylesheet';
|
||||
css.onload = () => resolve();
|
||||
css.onerror = reject;
|
||||
document.head.append(css);
|
||||
});
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
/**
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Provides defaults for Firebase Remote Config.
|
||||
*
|
||||
* This is a sensible, low-key fallback configuration in case the RC service fails for new users
|
||||
* (existing users will always use cached values, as there's no real way to indicate that they
|
||||
* could be fundamentally out-of-date).
|
||||
*/
|
||||
|
||||
var defaults = {
|
||||
featured: {},
|
||||
nav: ['@cityquiz','@rocketsleigh','@dasherdancer','@snowballrun','@presenttoss','@penguinswim','santaselfie','codeboogie','jetpack','jamband','snowball','elfmaker','codelab','wrapbattle','penguindash','build','matching','museum','boatload','takeoff','gumball','presentbounce','glider','speedsketch','santascanvas','seasonofgiving','penguinproof','traditions','wheressanta','santasearch','translations','runner','snowbox','mercator','windtunnel'],
|
||||
fallbackIndexScene: 'retro',
|
||||
indexScene: 'modvil',
|
||||
switchOff: false,
|
||||
upgradeToVersion: '',
|
||||
sceneRedirect: {'educators':'familyguide','press':'familyguide','tracker':'','village':''},
|
||||
routeUrl: 'https://firebasestorage.googleapis.com/v0/b/santa-tracker-firebase.appspot.com/o/route%2Fsanta_|LANG|.json?alt=media&2018b',
|
||||
sceneLock: {},
|
||||
videos: ['carpool','comroom','jingle','liftoff','museum','office','onvacation','penguinproof','reindeerworries','reload','santasback','satellite','selfies','slackingoff','takeoff','temptation','tired','wheressanta','workshop','likealight','yulelog'],
|
||||
refreshEvery: 60, // this is low, _because_ we're offline
|
||||
useGeoIP: true,
|
||||
showTracker: false,
|
||||
routeJitter: 10,
|
||||
};
|
||||
|
||||
var now = new Date();
|
||||
if (now.getMonth() === 9 || now.getMonth() === 10) {
|
||||
// Oct-Nov
|
||||
|
||||
} else if (now.getMonth() !== 11) {
|
||||
// Jan-Sep
|
||||
|
||||
} else {
|
||||
// Dec
|
||||
|
||||
}
|
||||
|
||||
// Firebase Remote Config only returns strings, so wrap everything.
|
||||
for (const key in defaults) {
|
||||
const v = defaults[key];
|
||||
if (typeof v !== 'string') {
|
||||
defaults[key] = JSON.stringify(defaults[key]);
|
||||
}
|
||||
}
|
||||
|
||||
export default defaults;
|
@ -1,206 +0,0 @@
|
||||
/**
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Service Worker for Santa Tracker.
|
||||
*/
|
||||
|
||||
const debug = false;
|
||||
|
||||
const swUrl = new URL(self.location);
|
||||
const baseurl = swUrl.searchParams.get('baseurl') || '';
|
||||
|
||||
console.info('SW', baseurl);
|
||||
|
||||
const STATIC_VERSION_HEADER = 'X-Santa-Version';
|
||||
const IGNORE_PROD = ['cast', 'error', 'upgrade'];
|
||||
const IGNORE_STATIC_PREFIX = ['/audio/', '/scenes/'];
|
||||
const PRECACHE = [
|
||||
'/',
|
||||
'/error.html',
|
||||
'/manifest.json',
|
||||
'/loader.js',
|
||||
'/images/favicon-32.png',
|
||||
'/images/icon-192.png',
|
||||
'/images/icon-256.png',
|
||||
'/images/icon-512.png',
|
||||
];
|
||||
|
||||
let replacingPreviousServiceWorker = false;
|
||||
|
||||
self.addEventListener('install', (event) => {
|
||||
console.info('SW install');
|
||||
|
||||
const call = async () => {
|
||||
// This is non-null if there was a previous Service Worker registered. Record for "activate", so
|
||||
// that a lack of current architecture can be seen as a reason to reload our clients.
|
||||
if (self.registration.active) {
|
||||
replacingPreviousServiceWorker = true;
|
||||
}
|
||||
|
||||
const prodCache = await caches.open('prod');
|
||||
try {
|
||||
await Promise.all(PRECACHE.map((url) => prodCache.add(url)));
|
||||
} catch (e) {
|
||||
console.error('failed to fetch', e);
|
||||
}
|
||||
console.info('precached', PRECACHE.length, 'prod URLs');
|
||||
|
||||
await self.skipWaiting();
|
||||
};
|
||||
event.waitUntil(call());
|
||||
});
|
||||
|
||||
self.addEventListener('activate', (event) => {
|
||||
console.info('SW activate, replacing:', replacingPreviousServiceWorker);
|
||||
|
||||
const call = async () => {
|
||||
// We can't use client.navigate here, as Safari doesn't support it. The 'controllerchange'
|
||||
// event in the foreground handles this.
|
||||
await self.clients.claim();
|
||||
};
|
||||
event.waitUntil(call());
|
||||
});
|
||||
|
||||
function staticRequestPath(url) {
|
||||
if (!baseurl) {
|
||||
// do nothing, not prod
|
||||
} else if (baseurl.startsWith('/')) {
|
||||
// for staging on a single domain
|
||||
const u = new URL(url);
|
||||
if (u.hostname === location.hostname && u.pathname.startsWith(baseurl)) {
|
||||
return u.pathname.substr(baseurl.length);
|
||||
}
|
||||
} else if (url.startsWith(baseurl)) {
|
||||
return url.substr(baseurl.length);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} raw pathname
|
||||
* @return {{intl: string, pathname: string}}
|
||||
*/
|
||||
function splitProdPath(raw) {
|
||||
|
||||
// get prefix, will be blank or e.g. "/intl/foo-BAR"
|
||||
const intlMatch = raw.match(/^(\/intl\/.*?)\//);
|
||||
const intlPrefix = intlMatch ? intlMatch[1] : '';
|
||||
|
||||
let pathname = raw.substr(intlPrefix.length);
|
||||
const routeMatch = pathname.match(/^\/(?:(\w+)\.html|)(\?|$)/);
|
||||
if (routeMatch && !IGNORE_PROD.includes(routeMatch[1])) {
|
||||
pathname = '/'; // don't use /index.html, as our Go server redirects in staging (breaks Safari)
|
||||
}
|
||||
|
||||
return {intl: intlPrefix, pathname};
|
||||
}
|
||||
|
||||
self.addEventListener('fetch', (event) => {
|
||||
if (event.request.method !== 'GET') {
|
||||
return;
|
||||
}
|
||||
|
||||
const url = new URL(event.request.url);
|
||||
|
||||
// Don't check domain for static requests; in dev, it's the same domain.
|
||||
const naked = staticRequestPath(event.request.url);
|
||||
if (naked) |