├── next-env.d.ts ├── .babelrc ├── public ├── images │ ├── android-launchericon-48-48.png │ ├── android-launchericon-72-72.png │ ├── android-launchericon-96-96.png │ ├── android-launchericon-144-144.png │ ├── android-launchericon-192-192.png │ ├── android-launchericon-512-512.png │ └── favicon.svg ├── manifest.json ├── sw.js.map ├── sw.js ├── workbox-529fc111.js └── workbox-529fc111.js.map ├── src ├── styles │ ├── settings-page.ts │ ├── loading.ts │ ├── switch.ts │ ├── rockets-page.tsx │ ├── global.ts │ ├── navbar.ts │ └── cards.ts ├── pages │ ├── api │ │ ├── launches.ts │ │ └── news.ts │ ├── rockets.tsx │ ├── news.tsx │ ├── launches.tsx │ ├── _app.tsx │ ├── index.tsx │ ├── _document.tsx │ └── settings.tsx ├── firebase.ts ├── data │ ├── rocketsTypes.ts │ └── vehicles.ts └── components │ ├── newscard.tsx │ ├── loadingmodal.tsx │ ├── bottombar.tsx │ ├── launchcard.tsx │ └── topbar.tsx ├── next.config.js ├── .gitignore ├── tsconfig.json ├── package.json └── README.md /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["next/babel"], 3 | "plugins": [["styled-components", { "ssr": true }]] 4 | } -------------------------------------------------------------------------------- /public/images/android-launchericon-48-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patogordo/RocketLaunches/HEAD/public/images/android-launchericon-48-48.png -------------------------------------------------------------------------------- /public/images/android-launchericon-72-72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patogordo/RocketLaunches/HEAD/public/images/android-launchericon-72-72.png -------------------------------------------------------------------------------- /public/images/android-launchericon-96-96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patogordo/RocketLaunches/HEAD/public/images/android-launchericon-96-96.png -------------------------------------------------------------------------------- /public/images/android-launchericon-144-144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patogordo/RocketLaunches/HEAD/public/images/android-launchericon-144-144.png -------------------------------------------------------------------------------- /public/images/android-launchericon-192-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patogordo/RocketLaunches/HEAD/public/images/android-launchericon-192-192.png -------------------------------------------------------------------------------- /public/images/android-launchericon-512-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patogordo/RocketLaunches/HEAD/public/images/android-launchericon-512-512.png -------------------------------------------------------------------------------- /src/styles/settings-page.ts: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | 3 | 4 | export const ToggleCard = styled.div` 5 | display: flex; 6 | flex-direction: row; 7 | align-items: center; 8 | justify-content: space-evenly; 9 | padding: 10px 5px; 10 | border-bottom: 1px solid rgba(170,170,170,0.186); 11 | margin: 10px 15px; 12 | h3 { 13 | font-weight: 400 !important; 14 | } 15 | label { 16 | margin: 0 10px; 17 | } 18 | ` -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | // next.config.js 2 | const path = require("path"); 3 | const withPWA = require("next-pwa"); 4 | const runtimeCaching = require("next-pwa/cache"); 5 | 6 | module.exports = withPWA({ 7 | pwa: { 8 | dest: "public", 9 | runtimeCaching, 10 | }, 11 | 12 | // This is not required to make it into a PWA, but is a nice way to clean up your imports 13 | webpack: (config) => { 14 | config.resolve.modules.push(path.resolve("./")); 15 | return config; 16 | }, 17 | }); -------------------------------------------------------------------------------- /src/pages/api/launches.ts: -------------------------------------------------------------------------------- 1 | import { NextApiRequest, NextApiResponse } from "next" 2 | import axios from 'axios' 3 | 4 | export default async (req: NextApiRequest, res: NextApiResponse) => { 5 | if(req.method === 'GET') { 6 | 7 | const myRequest = await axios.get('https://fdo.rocketlaunch.live/json/launches/next/4') 8 | res.setHeader('Cache-Control', 's-maxage=35, stale-while-revalidate') 9 | 10 | return res.json({ 11 | result: myRequest.data.result 12 | }) 13 | } 14 | } -------------------------------------------------------------------------------- /src/pages/api/news.ts: -------------------------------------------------------------------------------- 1 | import { NextApiRequest, NextApiResponse } from "next" 2 | import axios from 'axios' 3 | 4 | export default async (req: NextApiRequest, res: NextApiResponse) => { 5 | if(req.method === 'GET') { 6 | 7 | const myRequest = await axios.get('https://api.spaceflightnewsapi.net/v3/articles?_limit=4') 8 | res.setHeader('Cache-Control', 's-maxage=35, stale-while-revalidate') 9 | 10 | return res.json({ 11 | result: myRequest.data 12 | }) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/pages/rockets.tsx: -------------------------------------------------------------------------------- 1 | import { List, ListItem } from '../styles/rockets-page' 2 | import vehicles from '../data/vehicles' 3 | 4 | function rockets() { 5 | return ( 6 |
7 |

Rockets

8 | 9 | { 10 | vehicles.map((vehicle, key) => ( 11 | 12 | )) 13 | } 14 | 15 |
16 | ) 17 | } 18 | export default rockets -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # development 4 | /.codesandbox 5 | 6 | # dependencies 7 | /node_modules 8 | /.pnp 9 | .pnp.js 10 | 11 | # testing 12 | /coverage 13 | 14 | # next.js 15 | /.next/ 16 | /out/ 17 | 18 | # production 19 | /build 20 | /dist 21 | 22 | # misc 23 | .DS_Store 24 | *.pem 25 | 26 | # debug 27 | npm-debug.log* 28 | yarn-debug.log* 29 | yarn-error.log* 30 | 31 | # local env files 32 | .env.local 33 | .env.development.local 34 | .env.test.local 35 | .env.production.local 36 | 37 | # vercel 38 | .vercel 39 | -------------------------------------------------------------------------------- /src/firebase.ts: -------------------------------------------------------------------------------- 1 | import Firebase from 'firebase/app' 2 | import 'firebase/analytics' 3 | 4 | var app: Firebase.app.App 5 | 6 | if (!Firebase.apps.length) { 7 | // if ins't initialized, initialize it 8 | app = Firebase.initializeApp({ 9 | apiKey: "AIzaSyCjJn_NsN0liES-xUz58k7BOxdhX5VEnaA", 10 | authDomain: "rocketlaunchesga.firebaseapp.com", 11 | projectId: "rocketlaunchesga", 12 | storageBucket: "rocketlaunchesga.appspot.com", 13 | messagingSenderId: "66332600027", 14 | appId: "1:66332600027:web:7691e75f0ea9cf52ed4efe", 15 | measurementId: "G-CHK8Q2K6MZ" 16 | }) 17 | }else { 18 | app = Firebase.app(); // if already initialized, use that one 19 | } 20 | 21 | export { app } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "strict": false, 12 | "forceConsistentCasingInFileNames": true, 13 | "noEmit": true, 14 | "esModuleInterop": true, 15 | "skipDefaultLibCheck": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "jsx": "preserve" 21 | }, 22 | "exclude": [ 23 | "node_modules" 24 | ], 25 | "include": [ 26 | "next-env.d.ts", 27 | "**/*.ts", 28 | "**/*.tsx" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /src/styles/loading.ts: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | 3 | export const LoadingAnimation = styled.div` 4 | display: none; 5 | flex-direction: column; 6 | align-items: center; 7 | justify-content: center; 8 | position: fixed; 9 | top: 0; 10 | left: 0; 11 | width: 100%; 12 | height: 100vh; 13 | background: #22272e; 14 | z-index: 10; 15 | &.run { 16 | display: flex !important; 17 | } 18 | svg { 19 | width: 120px; 20 | margin-bottom: 85px; 21 | } 22 | @keyframes blink {50% { color: transparent }} 23 | .loader__dot { animation: 1s blink infinite } 24 | .loader__dot:nth-child(2) { animation-delay: 250ms } 25 | .loader__dot:nth-child(3) { animation-delay: 500ms } 26 | ` -------------------------------------------------------------------------------- /src/data/rocketsTypes.ts: -------------------------------------------------------------------------------- 1 | export default interface RocketInterface { 2 | name: string; 3 | description?: string; 4 | first_launch: string; 5 | manufacturer: string; 6 | img: string; 7 | reusable: boolean; 8 | orbital: boolean; 9 | stages: number; 10 | propellent: string; 11 | total_thrust_kn: number; 12 | wiki_link?: string; 13 | engines: Array<{ 14 | name: string; 15 | quantity: number; 16 | }>; 17 | size: { 18 | height_meters: number; 19 | diameter_meters: number; 20 | mass_tons: number; 21 | }; 22 | pricing: { 23 | projectCost: string; 24 | costPerLaunch: string; 25 | }; 26 | missions?: [ 27 | { 28 | name: string; 29 | date: string; 30 | } 31 | ]; 32 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rocketlaunches-next-and-react", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "dev": "next", 6 | "build": "next build", 7 | "start": "next", 8 | "post-update": "yarn upgrade --latest" 9 | }, 10 | "dependencies": { 11 | "axios": "0.21.2", 12 | "firebase": "^8.6.7", 13 | "next": "latest", 14 | "next-pwa": "^5.2.21", 15 | "react": "latest", 16 | "react-dom": "latest", 17 | "react-ionicons": "^4.2.0", 18 | "styled-components": "5.3.0" 19 | }, 20 | "devDependencies": { 21 | "@types/node": "15.3.0", 22 | "@types/react": "latest", 23 | "@types/styled-components": "5.1.9", 24 | "typescript": "4.2.4" 25 | }, 26 | "license": "MIT", 27 | "keywords": [], 28 | "description": "" 29 | } 30 | -------------------------------------------------------------------------------- /src/components/newscard.tsx: -------------------------------------------------------------------------------- 1 | import { Newspaper } from 'react-ionicons' 2 | import { NewsCard } from '../styles/cards' 3 | 4 | interface PropsTypes { 5 | website: string; 6 | publishedAt: number; 7 | articleName: string; 8 | articleDescription: string; 9 | website_url: string; 10 | } 11 | 12 | export default function NewsCard_(props: PropsTypes) { 13 | return ( 14 | 15 |
16 | 17 |
18 |

{props.website}

19 | {new Date(props.publishedAt).toLocaleString('en-us', { timeZoneName: 'short' })} 20 |
21 |
22 | 23 |
24 |
25 |

{props.articleName}

26 |

{props.articleDescription}

27 | Know more in {props.website} 28 |
29 |
30 |
31 | ) 32 | } -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Rocket Launches", 3 | "short_name": "Rocket Launches", 4 | "theme_color": "#2d333b", 5 | "background_color": "#22272e", 6 | "display": "standalone", 7 | "orientation": "portrait", 8 | "scope": "/", 9 | "start_url": "/", 10 | "icons": [ 11 | { 12 | "src": "/images/android-launchericon-512-512.png", 13 | "type": "image/png", 14 | "sizes": "512x512" 15 | }, 16 | { 17 | "src": "/images/android-launchericon-192-192.png", 18 | "type": "image/png", 19 | "sizes": "192x129" 20 | }, 21 | { 22 | "src": "/images/android-launchericon-144-144.png", 23 | "type": "image/png", 24 | "sizes": "144x144" 25 | }, 26 | { 27 | "src": "/images/android-launchericon-96-96.png", 28 | "type": "image/png", 29 | "sizes": "96x96" 30 | }, 31 | { 32 | "src": "/images/android-launchericon-72-72.png", 33 | "type": "image/png", 34 | "sizes": "72x72" 35 | }, 36 | { 37 | "src": "/images/android-launchericon-48-48.png", 38 | "type": "image/png", 39 | "sizes": "48x48" 40 | } 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /src/components/loadingmodal.tsx: -------------------------------------------------------------------------------- 1 | import { LoadingAnimation } from '../styles/loading' 2 | 3 | export default function ModalLoading() { 4 | return ( 5 | 6 | 8 | 9 | 10 | 17 | 18 | 19 | 20 |

Fetching data 21 | . 22 | . 23 | . 24 |

25 |
26 | ) 27 | } -------------------------------------------------------------------------------- /src/pages/news.tsx: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import React, { useState, useEffect } from "react"; 3 | import { Cards } from "../styles/cards"; 4 | import NewsCard from '../components/newscard' 5 | 6 | function news() { 7 | const [news, setNews] = useState([]); 8 | 9 | useEffect(() => { 10 | const loadingBar = document.querySelector('#loading') 11 | loadingBar.classList.add('run') 12 | 13 | axios.get("/api/news").then((res) => { 14 | setNews(res.data.result); 15 | loadingBar.classList.remove('run') 16 | }); 17 | }, []); 18 | 19 | return ( 20 |
21 |

Astronomy news

22 | 23 | { 24 | news.map((article, key) => ( 25 | 33 | )) 34 | } 35 | 36 |
37 | ); 38 | } 39 | export default news; -------------------------------------------------------------------------------- /src/styles/switch.ts: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | 3 | export const SwitchComponent = styled.label` 4 | position: relative; 5 | display: inline-block; 6 | width: 60px; 7 | height: 34px; 8 | input { 9 | opacity: 0; 10 | width: 0; 11 | height: 0; 12 | } 13 | input:checked + .slider { 14 | background-color: #00E676; 15 | } 16 | input:focus + .slider { 17 | box-shadow: 0 0 1px #00E676; 18 | } 19 | input:checked + .slider:before { 20 | -webkit-transform: translateX(26px); 21 | -ms-transform: translateX(26px); 22 | transform: translateX(26px); 23 | } 24 | .slider { 25 | position: absolute; 26 | cursor: pointer; 27 | top: 0; 28 | left: 0; 29 | right: 0; 30 | bottom: 0; 31 | background-color: #ccc; 32 | -webkit-transition: .4s; 33 | transition: .4s; 34 | &:before { 35 | position: absolute; 36 | content: ""; 37 | height: 26px; 38 | width: 26px; 39 | left: 4px; 40 | bottom: 4px; 41 | background-color: white; 42 | -webkit-transition: .4s; 43 | transition: .4s; 44 | } 45 | &.round { 46 | border-radius: 34px; 47 | &:before { 48 | border-radius: 50%; 49 | } 50 | } 51 | } 52 | ` -------------------------------------------------------------------------------- /src/pages/launches.tsx: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import React, { useState, useEffect } from "react"; 3 | import { Cards, Card } from "../styles/cards"; 4 | import LaunchCard from '../components/launchcard' 5 | 6 | function launches() { 7 | const [launches, setLaunches] = useState([]); 8 | 9 | useEffect(() => { 10 | const loadingBar = document.querySelector('#loading') 11 | loadingBar.classList.add('run') 12 | 13 | axios.get("/api/launches").then((res) => { 14 | setLaunches(res.data.result); 15 | loadingBar.classList.remove('run') 16 | }); 17 | }, []); 18 | 19 | 20 | return ( 21 |
22 |

Next four launches

23 | 24 | {launches.map((launch, key) => { 25 | return ( 26 | 37 | ); 38 | })} 39 | 40 |
41 | ); 42 | } 43 | export default launches; 44 | -------------------------------------------------------------------------------- /src/components/bottombar.tsx: -------------------------------------------------------------------------------- 1 | import { BottomBar, IconContainer } from '../styles/navbar' 2 | import { HomeOutline, NewspaperOutline, RocketOutline, TimeOutline, StorefrontOutline } from 'react-ionicons' 3 | import { useRouter } from 'next/router' 4 | 5 | export default function BottomBar_() { 6 | const router = useRouter() 7 | 8 | return ( 9 | 10 | router.push('/')}> 11 | 12 | Home 13 | 14 | 15 | router.push('/news')}> 16 | 17 | News 18 | 19 | 20 | router.push('/launches')}> 21 | 22 | Launches 23 | 24 | 25 | router.push('/rockets')}> 26 | 27 | Rockets 28 | 29 | 30 | 31 | ) 32 | } -------------------------------------------------------------------------------- /src/styles/rockets-page.tsx: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | import { ChevronForwardOutline } from 'react-ionicons' 3 | 4 | export const List = styled.ul` 5 | list-style: none; 6 | width: 100%; 7 | display: flex; 8 | flex-direction: column; 9 | align-items: center; 10 | justify-content: start; 11 | ` 12 | 13 | export const ListItem_ = styled.li` 14 | width: min(95%, 460px); 15 | margin: 10px; 16 | background: #2d333b; 17 | border-radius: .35rem; 18 | padding: 5px; 19 | display: flex; 20 | flex-direction: row; 21 | align-items: center; 22 | justify-content: space-between; 23 | align-items: center; 24 | div { 25 | display: flex; 26 | > div { 27 | margin-left: 10px; 28 | text-align: start; 29 | flex-direction: column; 30 | justify-content: center; 31 | } 32 | } 33 | img { 34 | width: 50px; 35 | height: 50px; 36 | object-fit: cover; 37 | border-radius: .65rem; 38 | } 39 | ` 40 | 41 | interface ListItemPropsTypes { 42 | imgUrl: string; 43 | rocketName: string; 44 | rocketLaunchDate: string; 45 | } 46 | 47 | export function ListItem(props: ListItemPropsTypes) { 48 | return ( 49 | 50 |
51 | 🚀 52 |
53 |

{props.rocketName}

54 | First flight: {props.rocketLaunchDate} 55 |
56 |
57 | alert('not working yet')} /> 58 |
59 | ) 60 | } -------------------------------------------------------------------------------- /src/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import { AppProps } from 'next/app' 3 | import GlobalStyle from '../styles/global' 4 | import { useRouter } from 'next/router' 5 | import { app } from '../firebase' 6 | import BottomBar from '../components/bottombar' 7 | import TopBar from '../components/topbar' 8 | import ModalLoading from '../components/loadingmodal' 9 | 10 | const MyApp: React.FC = ({ Component, pageProps }) => { 11 | const [showWarn, setShowWarn] = useState(false) 12 | const router = useRouter() 13 | 14 | useEffect(() => { 15 | app.analytics() 16 | 17 | if(!localStorage.getItem('RocketLaunches::Theme')) { 18 | localStorage.setItem('RocketLaunches::Theme', 'dark') 19 | } 20 | if(!localStorage.getItem('RocketLaunches::Measures')) { 21 | localStorage.setItem('RocketLaunches::Measures', 'metric') 22 | } 23 | if(localStorage.getItem('RocketLaunches::Theme') === 'light') { 24 | document.querySelector('body').classList.add('light') 25 | } 26 | 27 | document.querySelector('body').classList.remove('dark') 28 | 29 | // Check user screen width and User agent 30 | if(screen.width > 768 && !/Android|webOS|iPhone|iPad|Mac|Macintosh|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) { 31 | setShowWarn(true) 32 | return 33 | } 34 | setShowWarn(false) 35 | }, []) 36 | 37 | return ( 38 | <> 39 | 40 | 41 |
42 | 43 |
44 | 45 | 46 | 47 | {/*
48 | Try using this application on a mobile phone to have a better experience. 49 |
*/} 50 | 51 | 52 | 53 | ) 54 | } 55 | 56 | export default MyApp -------------------------------------------------------------------------------- /src/styles/global.ts: -------------------------------------------------------------------------------- 1 | import { createGlobalStyle } from "styled-components"; 2 | 3 | export default createGlobalStyle` 4 | * { 5 | margin: 0; 6 | padding: 0; 7 | box-sizing: border-box; 8 | font-family: Montserrat, 'sans-serif'; 9 | ::-webkit-scrollbar { 10 | visibility: 0; 11 | display: none; 12 | } 13 | } 14 | #__next { 15 | width: 100%; 16 | } 17 | .screen-size-warn { 18 | position: fixed; 19 | width: 100%; 20 | text-align: center; 21 | bottom: 80px; 22 | left: 0; 23 | display: none; 24 | background: #2b2f32; 25 | color: #FFB74D; 26 | padding: 5px 0; 27 | opacity: .8; 28 | &.on { 29 | display: block; 30 | } 31 | } 32 | .component { 33 | margin-bottom: 80px; 34 | } 35 | body { 36 | display: grid; 37 | place-items: center; 38 | text-align: center; 39 | background: #22272e; 40 | color: #e1e2e4; 41 | transition: all ease .3s; 42 | &.light { 43 | background: #fff !important; 44 | color: #24292e !important; 45 | transition: all ease .3s; 46 | #loading { 47 | background: #f3fbff !important; 48 | transition: all ease .3s; 49 | } 50 | #bottom-nav, #top-nav { 51 | background: #24292E !important; 52 | transition: all ease .3s; 53 | } 54 | .list-item { 55 | background: #cbced2 !important; 56 | transition: all ease .3s; 57 | } 58 | .topbar-menu { 59 | background: #dae1ea !important; 60 | transition: all ease .3s; 61 | } 62 | .card { 63 | background: #ffffff; 64 | border: 1px solid #aaa; 65 | transition: all ease .3s; 66 | } 67 | .top { 68 | background: #f6f8fa; 69 | border-bottom: 1px solid #aaa; 70 | transition: all ease .3s; 71 | } 72 | .icon { 73 | color: #22272e; 74 | fill: #22272e; 75 | transition: all ease .3s; 76 | } 77 | } 78 | } 79 | a { 80 | color: #1E88E5!important 81 | } 82 | `; -------------------------------------------------------------------------------- /src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import React, { useState, useEffect } from "react"; 3 | import { Cards, Card } from "../styles/cards"; 4 | import LaunchCard from '../components/launchcard' 5 | import NewsCard from '../components/newscard' 6 | 7 | function home() { 8 | const [launches, setLaunches] = useState([]); 9 | const [news, setNews] = useState([]) 10 | 11 | useEffect(() => { 12 | const loadingBar = document.querySelector('#loading') 13 | loadingBar.classList.add('run') 14 | 15 | axios.get("/api/launches").then((res) => { 16 | setLaunches(res.data.result.slice(0, 1)); 17 | axios.get('/api/news').then((res) => { 18 | setNews(res.data.result.slice(0, 1)); 19 | loadingBar.classList.remove('run') 20 | }) 21 | }); 22 | }, []); 23 | 24 | return ( 25 |
26 |
27 | 28 | { 29 | launches.map((launch, key) => ( 30 | 31 |

Next Launch

32 | 42 |
43 | )) 44 | } 45 | { 46 | news.map((article, key) => ( 47 | 48 |

Breaking news

49 | 56 |
57 | )) 58 | } 59 |
60 |
61 |
62 | ); 63 | } 64 | 65 | export default home; -------------------------------------------------------------------------------- /src/pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Document, { DocumentContext, DocumentInitialProps, Head, Html, Main, NextScript } from 'next/document' 3 | import { ServerStyleSheet } from 'styled-components' 4 | 5 | export default class MyDocument extends Document { 6 | 7 | static async getInitialProps(ctx: DocumentContext): Promise { 8 | const sheet = new ServerStyleSheet() 9 | const originalRenderPage = ctx.renderPage 10 | 11 | try { 12 | ctx.renderPage = () => 13 | originalRenderPage({ 14 | enhanceApp: (App) => (props) => 15 | sheet.collectStyles(), 16 | }) 17 | 18 | const initialProps = await Document.getInitialProps(ctx) 19 | return { 20 | ...initialProps, 21 | styles: ( 22 | <> 23 | {initialProps.styles} 24 | {sheet.getStyleElement()} 25 | 26 | ), 27 | } 28 | } finally { 29 | sheet.seal() 30 | } 31 | } 32 | 33 | render() { 34 | return ( 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 |
55 | 56 | 57 | 58 | 59 | ) 60 | } 61 | } -------------------------------------------------------------------------------- /src/styles/navbar.ts: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | 3 | export const TopMenu = styled.div` 4 | position: fixed; 5 | top: 0; 6 | left: 0; 7 | width: 100%; 8 | height: 100%; 9 | background: #252c35; 10 | padding-top: 65px; 11 | z-index: 9; 12 | overflow-y: auto; 13 | transform: translateX(-100%); 14 | footer { 15 | margin: 10px 0; 16 | } 17 | &.on { 18 | transition: all ease .3s; 19 | animation: slide-right .4s linear both; 20 | } 21 | // Open animation 22 | @keyframes slide-right { 23 | 0% { 24 | transform: translateX(-100%); 25 | } 26 | 100% { 27 | transform: translateX(0); 28 | } 29 | } 30 | 31 | // Close animation 32 | @keyframes slide-left { 33 | 0% { 34 | transform: translateX(0); 35 | } 36 | 100% { 37 | transform: translateX(-100%); 38 | } 39 | } 40 | ` 41 | 42 | export const BottomBar = styled.nav` 43 | position: fixed; 44 | bottom: 0; 45 | left: 0; 46 | width: 100%; 47 | height: 80px; 48 | background: #2d333b; 49 | color: #fff; 50 | display: flex; 51 | flex-direction: row; 52 | align-items: center; 53 | justify-content: space-evenly; 54 | ` 55 | 56 | export const TopBar = styled.nav` 57 | position: fixed; 58 | top: 0; 59 | left: 0; 60 | width: 100%; 61 | height: 45px; 62 | background: #2d333b; 63 | color: #fff; 64 | display: flex; 65 | flex-direction: row; 66 | align-items: center; 67 | justify-content: space-between; 68 | z-index: 10; 69 | div { 70 | display: flex; 71 | align-items: center; 72 | margin: 0 16px; 73 | h2 { 74 | font-weight: 500; 75 | } 76 | h2, .config { 77 | margin-left: 16px; 78 | } 79 | } 80 | ` 81 | 82 | export const IconContainer = styled.div` 83 | display: flex; 84 | flex-direction: column; 85 | align-items: center; 86 | justify-content: center; 87 | cursor: pointer; 88 | transition: all ease .3s; 89 | a { 90 | color: #fff !important; 91 | font-size: .75rem; 92 | text-decoration: none; 93 | } 94 | &.active { 95 | a, svg { 96 | color: #42b883 !important; 97 | fill: #42b883 !important; 98 | } 99 | } 100 | &:hover { 101 | transition: all ease .3s; 102 | svg, a { 103 | color: #42b883 !important; 104 | fill: #42b883 !important; 105 | } 106 | } 107 | ` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Welcome to Rocket Launches Next Version 👋 2 | ![Version](https://img.shields.io/badge/Version-1.0-blue.svg?cacheSeconds=2592000) 3 | [![License: MIT](https://img.shields.io/badge/License-MIT-purple.svg)](#) 4 | [![Open Source Love png1](https://badges.frapsoft.com/os/v1/open-source.png?v=103)](https://github.com/ellerbrock/open-source-badges/) 5 | 6 | > Rocket launches, a simple site to let you know what is the next rocket launch. 7 | ### 🏠 [Homepage](https://rocketlaunches.ga/) 8 | 9 | ## Features :zap: 10 | [![Deployed in Vercel](https://img.shields.io/badge/Vercel-000000?style=for-the-badge&logo=vercel&logoColor=white)](https://rocketlaunches.ga) 11 | [![Nextjs]( https://img.shields.io/badge/next.js-000000?style=for-the-badge&logo=Next.js&logoColor=white)](https://nextjs.org/) 12 | [![Typescript]( https://img.shields.io/badge/TypeScript-007ACC?style=for-the-badge&logo=typescript&logoColor=white)](https://www.typescriptlang.org/) 13 | [![Stlyed Components](https://img.shields.io/badge/styled--components-DB7093?style=for-the-badge&logo=styled-components&logoColor=white)](https://styled-components.com/) 14 | [![Pwa](https://img.shields.io/badge/Pwa-35495E?style=for-the-badge&logo=pwa&logoColor=4FC08D)](https://web.dev/progressive-web-apps/) 15 | [![Google Analytics](https://img.shields.io/badge/Google%20Analytics-E37400?style=for-the-badge&logo=google%20analytics&logoColor=white)](https://analytics.google.com/) 16 | 17 | ## Usage :hammer_and_pick: 18 | ``` 19 | yarn dev 20 | // run development local server 21 | 22 | yarn build 23 | // make a production build of your app 24 | ``` 25 | *** 26 | 27 | ## What's new? 28 | - [x] Styles 29 | - [x] Api cache control 30 | - [x] Loading Modal Animations 31 | - [x] App settings v1 32 | - [x] Progresive web app 33 | - [x] Google analytics integration (Firebase) 34 | 35 | ## Plans 36 | - [ ] Mobile app 37 | - [ ] Push notifications 38 | 39 | ## Show your support :handshake: 40 | 41 | Give a ⭐️ if this project helped you!
42 | Makes a [pull request](https://github.com/PatoGordo/Rocket-Launches/pulls) adding new features or solving bugs.
43 | Create a [Issue](https://github.com/PatoGordo/Rocket-Launches/issues) if you don't want to implement the feature or fix the bug.
44 | 45 | ## Author :technologist: 46 | 47 | **PatoGordo** 48 | 49 | * Website: [https://patogordo.ga](https://patogordo.ga)
50 | * Twitter: [@Patogordoo](https://twitter.com/Patogordoo)
51 | * Github: [@PatoGordo](https://github.com/PatoGordo)
52 | 53 | *** 54 | -------------------------------------------------------------------------------- /src/pages/settings.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import { SwitchComponent } from '../styles/switch' 3 | import { ToggleCard } from '../styles/settings-page' 4 | import { useRouter } from 'next/router' 5 | 6 | function settings() { 7 | const [isDark, setIsDark] = useState(true) 8 | const [measures, setMeasures] = useState(true) // true => metric; fasle => imperial 9 | 10 | useEffect(() => { 11 | if(localStorage.getItem('RocketLaunches::Measures') === 'metric') { 12 | setMeasures(true) 13 | }else{ 14 | setMeasures(false) 15 | } 16 | 17 | if(localStorage.getItem('RocketLaunches::Theme') === 'dark') { 18 | setIsDark(true) 19 | }else{ 20 | setIsDark(false) 21 | } 22 | }, []) 23 | 24 | function handleChangeTheme(e: React.FormEvent) { 25 | e.preventDefault() 26 | 27 | const body = document.querySelector('body') 28 | 29 | if(isDark) { 30 | setIsDark(!isDark) 31 | body.classList.add('light') 32 | localStorage.setItem('RocketLaunches::Theme', 'light') 33 | return 34 | } 35 | setIsDark(!isDark) 36 | body.classList.remove('light') 37 | localStorage.setItem('RocketLaunches::Theme', 'dark') 38 | } 39 | 40 | function handleChangeMeasures(e: React.FormEvent | React.MouseEvent) { 41 | e.preventDefault() 42 | 43 | 44 | if(measures) { 45 | setMeasures(false) 46 | localStorage.setItem('RocketLaunches::Measures', 'imperial') 47 | return 48 | } 49 | setMeasures(true) 50 | localStorage.setItem('RocketLaunches::Measures', 'metric') 51 | } 52 | 53 | return ( 54 |
55 |

Settings

56 | 57 |

Theme

58 | 59 |

Light

60 | handleChangeTheme(e)} htmlFor="input"> 61 | {}} /> 62 | {/* The onChange is to stop warning */} 63 | 64 | 65 |

Dark

66 |
67 | 68 |

Measures

69 | 70 |

Imperial

71 | handleChangeMeasures(e)} htmlFor="input2"> 72 | {}} /> 73 | 74 | 75 |

Metric

76 |
77 | 78 |
79 | ) 80 | } 81 | 82 | export default settings -------------------------------------------------------------------------------- /public/sw.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"sw.js","sources":["../../tmp/f6cc3d9e091344de07164c1df673b6f3/sw.js"],"sourcesContent":["import {registerRoute as workbox_routing_registerRoute} from '/workspace/RocketLaunchesBeta/node_modules/workbox-routing/registerRoute.mjs';\nimport {NetworkFirst as workbox_strategies_NetworkFirst} from '/workspace/RocketLaunchesBeta/node_modules/workbox-strategies/NetworkFirst.mjs';\nimport {NetworkOnly as workbox_strategies_NetworkOnly} from '/workspace/RocketLaunchesBeta/node_modules/workbox-strategies/NetworkOnly.mjs';\nimport {clientsClaim as workbox_core_clientsClaim} from '/workspace/RocketLaunchesBeta/node_modules/workbox-core/clientsClaim.mjs';/**\n * Welcome to your Workbox-powered service worker!\n *\n * You'll need to register this file in your web app.\n * See https://goo.gl/nhQhGp\n *\n * The rest of the code is auto-generated. Please don't update this file\n * directly; instead, make changes to your Workbox build configuration\n * and re-run your build process.\n * See https://goo.gl/2aRDsh\n */\n\n\nimportScripts(\n \n);\n\n\n\n\n\n\n\nself.skipWaiting();\n\nworkbox_core_clientsClaim();\n\n\n\nworkbox_routing_registerRoute(\"/\", new workbox_strategies_NetworkFirst({ \"cacheName\":\"start-url\", plugins: [{ cacheWillUpdate: async ({request, response, event, state}) => { if (response && response.type === 'opaqueredirect') { return new Response(response.body, {status: 200, statusText: 'OK', headers: response.headers}); } return response; } }] }), 'GET');\nworkbox_routing_registerRoute(/.*/i, new workbox_strategies_NetworkOnly({ \"cacheName\":\"dev\", plugins: [] }), 'GET');\n\n\n\n\n"],"names":["importScripts","self","skipWaiting","workbox_core_clientsClaim","workbox_routing_registerRoute","workbox_strategies_NetworkFirst","plugins","cacheWillUpdate","request","response","event","state","type","Response","body","status","statusText","headers","workbox_strategies_NetworkOnly"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAGmI;EACnI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;EAGAA,aAAa;EAUbC,IAAI,CAACC,WAAL;AAEAC,sBAAyB;AAIzBC,uBAA6B,CAAC,GAAD,EAAM,IAAIC,oBAAJ,CAAoC;EAAE,eAAY,WAAd;EAA2BC,EAAAA,OAAO,EAAE,CAAC;EAAEC,IAAAA,eAAe,EAAE,OAAO;EAACC,MAAAA,OAAD;EAAUC,MAAAA,QAAV;EAAoBC,MAAAA,KAApB;EAA2BC,MAAAA;EAA3B,KAAP,KAA6C;EAAE,UAAIF,QAAQ,IAAIA,QAAQ,CAACG,IAAT,KAAkB,gBAAlC,EAAoD;EAAE,eAAO,IAAIC,QAAJ,CAAaJ,QAAQ,CAACK,IAAtB,EAA4B;EAACC,UAAAA,MAAM,EAAE,GAAT;EAAcC,UAAAA,UAAU,EAAE,IAA1B;EAAgCC,UAAAA,OAAO,EAAER,QAAQ,CAACQ;EAAlD,SAA5B,CAAP;EAAiG;;EAAC,aAAOR,QAAP;EAAkB;EAA5O,GAAD;EAApC,CAApC,CAAN,EAAmU,KAAnU,CAA7B;AACAL,uBAA6B,CAAC,KAAD,EAAQ,IAAIc,mBAAJ,CAAmC;EAAE,eAAY,KAAd;EAAqBZ,EAAAA,OAAO,EAAE;EAA9B,CAAnC,CAAR,EAAgF,KAAhF,CAA7B;;"} -------------------------------------------------------------------------------- /src/components/launchcard.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | import { RocketSharp } from 'react-ionicons' 3 | import { LaunchCard } from '../styles/cards' 4 | 5 | interface PropsTypes { 6 | company: string; 7 | vehicleName: string; 8 | PadName: string; 9 | PadLocationState: string; 10 | PadLocationCountry: string; 11 | LaunchDate?: number; 12 | MissionName: string; 13 | MissionDescription: string; 14 | ttimer?: boolean 15 | } 16 | 17 | export default function LaunchCard_(props: PropsTypes) { 18 | const launchDate: number = Number(new Date(props.LaunchDate * 1000)) 19 | const [remainTime, setRemainTime] = useState(`00:00:00:00`) 20 | // const [isMetric, setIsMetric] = useState(false) 21 | 22 | // useEffect(() => { 23 | // if (localStorage.getItem('RocketLaunches::Measures') === 'metric') { 24 | // setIsMetric(true) 25 | // } 26 | // }) 27 | 28 | var x = setInterval(function () { 29 | var now = new Date().getTime(); 30 | 31 | var distance = launchDate - now; 32 | 33 | var days = Math.floor(distance / (1000 * 60 * 60 * 24)); 34 | var hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); 35 | var minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60)); 36 | var seconds = Math.floor((distance % (1000 * 60)) / 1000); 37 | 38 | setRemainTime(`T-${days < 10 ? '0' : ''}${days}:${hours < 10 ? '0' : ''}${hours}:${minutes < 10 ? '0' : ''}${minutes}:${seconds < 10 ? '0' : ''}${seconds}`) 39 | 40 | if (distance < 0) { 41 | clearInterval(x); 42 | setRemainTime(`T-00:00:00:00`) 43 | } 44 | }, 1000); 45 | 46 | // function toMetricTemp(fahrenheit: number) { 47 | // if (fahrenheit === null) { 48 | // return null 49 | // } else { 50 | // var celsius = (fahrenheit - 32) * 5 / 9 51 | // return Math.ceil(celsius) 52 | // } 53 | // } 54 | 55 | // function toMetricSpeed(mph: number) { 56 | // if (mph === null) { 57 | // return null 58 | // } else { 59 | // var kmh = mph * 1.609344 60 | // return kmh.toFixed(2) 61 | // } 62 | // } 63 | 64 | return ( 65 | 66 |
67 | 68 |
69 |

{props.company} · {props.vehicleName}

70 | {props.PadName}, {props.PadLocationState !== null ? props.PadLocationState + ', ' : ''} {props.PadLocationCountry} 71 | {new Date(props.LaunchDate * 1000).toLocaleString('en-us', { timeZoneName: 'short' })} 72 |
73 |
74 | 75 |
76 |

{remainTime}

77 |
78 |

{props.MissionName}

79 |

{props.MissionDescription}

80 |
81 |
82 |
83 | ) 84 | } -------------------------------------------------------------------------------- /src/styles/cards.ts: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | 3 | export const LaunchCard = styled.div` 4 | display: flex; 5 | flex-direction: column; 6 | align-items: center; 7 | justify-content: center; 8 | text-align: start; 9 | background: #2d333b; 10 | border-radius: 8px; 11 | margin: 15px; 12 | .top { 13 | width: 100%; 14 | padding: 5px 0; 15 | display: flex; 16 | align-items: center; 17 | background: #48515c; 18 | border-radius: 8px 8px 0 0; 19 | .icon { 20 | display: flex; 21 | align-items: center; 22 | margin: 0 20px; 23 | } 24 | div { 25 | display: flex; 26 | flex-direction: column; 27 | justify-content: start; 28 | } 29 | } 30 | .body { 31 | padding: 10px; 32 | .TTimer { 33 | text-align: center; 34 | font-family: 'Roboto Slab', serif; 35 | font-size: 25px; 36 | font-weight: 500; 37 | border-bottom: 1px solid #aaa; 38 | margin-bottom: 15px; 39 | } 40 | .launch_details { 41 | h3 { 42 | font-weight: 500; 43 | font-size: 18px; 44 | } 45 | p { 46 | font-weight: 300; 47 | font-size: 14px; 48 | } 49 | } 50 | } 51 | ` 52 | export const NewsCard = styled.div` 53 | display: flex; 54 | flex-direction: column; 55 | align-items: center; 56 | justify-content: start; 57 | text-align: start; 58 | background: #2d333b; 59 | border-radius: 8px; 60 | margin: 15px; 61 | .top { 62 | width: 100%; 63 | padding: 5px 0; 64 | display: flex; 65 | align-items: center; 66 | background: #48515c; 67 | border-radius: 8px 8px 0 0; 68 | align-self: flex-start; 69 | .icon { 70 | display: flex; 71 | align-items: center; 72 | margin: 0 20px; 73 | } 74 | div { 75 | display: flex; 76 | flex-direction: column; 77 | justify-content: start; 78 | } 79 | } 80 | .body { 81 | padding: 10px; 82 | .article_details { 83 | h3 { 84 | font-weight: 500; 85 | font-size: 18px; 86 | } 87 | p { 88 | font-weight: 300; 89 | font-size: 14px; 90 | } 91 | a { 92 | color: #00E676; 93 | } 94 | } 95 | } 96 | ` 97 | 98 | export const Cards = styled.section` 99 | display: grid; 100 | place-items: center; 101 | align-items: stretch; 102 | grid-template-columns: 1fr 1fr; 103 | @media screen and (max-width: 775px) { 104 | grid-template-columns: 1fr; 105 | } 106 | ` 107 | 108 | export const Card = styled.div` 109 | text-align: start; 110 | display: flex; 111 | flex-direction: column; 112 | align-items: flex-start; 113 | justify-content: start; 114 | padding: 10px; 115 | border-bottom: 1px solid #aaa; 116 | margin: 15px 5px; 117 | width: min(400px, 95%); 118 | `; 119 | 120 | export const CardTitle = styled.h2` 121 | text-align: start; 122 | font-weight: bolder; 123 | align-self: start; 124 | `; 125 | 126 | export const CardDate = styled.small` 127 | font-weight: 300; 128 | `; 129 | 130 | export const CardDescription = styled.p` 131 | margin-top: 10px; 132 | `; 133 | 134 | export const CardText = styled.p` 135 | margin-top: 6px; 136 | ` 137 | 138 | export const CardImage = styled.img` 139 | margin: 5px 0; 140 | border-radius: .35rem; 141 | width: 100%; 142 | ` -------------------------------------------------------------------------------- /public/sw.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | // If the loader is already loaded, just stop. 15 | if (!self.define) { 16 | const singleRequire = name => { 17 | if (name !== 'require') { 18 | name = name + '.js'; 19 | } 20 | let promise = Promise.resolve(); 21 | if (!registry[name]) { 22 | 23 | promise = new Promise(async resolve => { 24 | if ("document" in self) { 25 | const script = document.createElement("script"); 26 | script.src = name; 27 | document.head.appendChild(script); 28 | script.onload = resolve; 29 | } else { 30 | importScripts(name); 31 | resolve(); 32 | } 33 | }); 34 | 35 | } 36 | return promise.then(() => { 37 | if (!registry[name]) { 38 | throw new Error(`Module ${name} didn’t register its module`); 39 | } 40 | return registry[name]; 41 | }); 42 | }; 43 | 44 | const require = (names, resolve) => { 45 | Promise.all(names.map(singleRequire)) 46 | .then(modules => resolve(modules.length === 1 ? modules[0] : modules)); 47 | }; 48 | 49 | const registry = { 50 | require: Promise.resolve(require) 51 | }; 52 | 53 | self.define = (moduleName, depsNames, factory) => { 54 | if (registry[moduleName]) { 55 | // Module is already loading or loaded. 56 | return; 57 | } 58 | registry[moduleName] = Promise.resolve().then(() => { 59 | let exports = {}; 60 | const module = { 61 | uri: location.origin + moduleName.slice(1) 62 | }; 63 | return Promise.all( 64 | depsNames.map(depName => { 65 | switch(depName) { 66 | case "exports": 67 | return exports; 68 | case "module": 69 | return module; 70 | default: 71 | return singleRequire(depName); 72 | } 73 | }) 74 | ).then(deps => { 75 | const facValue = factory(...deps); 76 | if(!exports.default) { 77 | exports.default = facValue; 78 | } 79 | return exports; 80 | }); 81 | }); 82 | }; 83 | } 84 | define("./sw.js",['./workbox-6b19f60b'], function (workbox) { 'use strict'; 85 | 86 | /** 87 | * Welcome to your Workbox-powered service worker! 88 | * 89 | * You'll need to register this file in your web app. 90 | * See https://goo.gl/nhQhGp 91 | * 92 | * The rest of the code is auto-generated. Please don't update this file 93 | * directly; instead, make changes to your Workbox build configuration 94 | * and re-run your build process. 95 | * See https://goo.gl/2aRDsh 96 | */ 97 | 98 | importScripts(); 99 | self.skipWaiting(); 100 | workbox.clientsClaim(); 101 | workbox.registerRoute("/", new workbox.NetworkFirst({ 102 | "cacheName": "start-url", 103 | plugins: [{ 104 | cacheWillUpdate: async ({ 105 | request, 106 | response, 107 | event, 108 | state 109 | }) => { 110 | if (response && response.type === 'opaqueredirect') { 111 | return new Response(response.body, { 112 | status: 200, 113 | statusText: 'OK', 114 | headers: response.headers 115 | }); 116 | } 117 | 118 | return response; 119 | } 120 | }] 121 | }), 'GET'); 122 | workbox.registerRoute(/.*/i, new workbox.NetworkOnly({ 123 | "cacheName": "dev", 124 | plugins: [] 125 | }), 'GET'); 126 | 127 | }); 128 | //# sourceMappingURL=sw.js.map 129 | -------------------------------------------------------------------------------- /src/components/topbar.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react' 2 | import { MenuOutline, NotificationsOutline } from 'react-ionicons' 3 | import { TopBar, TopMenu } from '../styles/navbar' 4 | import { SwitchComponent } from '../styles/switch' 5 | import { ToggleCard } from '../styles/settings-page' 6 | 7 | export default function TopBar_() { 8 | const [isDark, setIsDark] = useState(true) 9 | const [measures, setMeasures] = useState(true) // true => metric; fasle => imperial 10 | const [isOpen, setIsOpen] = useState(false) 11 | 12 | useEffect(() => { 13 | if(localStorage.getItem('RocketLaunches::Measures') === 'metric') { 14 | setMeasures(true) 15 | }else{ 16 | setMeasures(false) 17 | } 18 | 19 | if(localStorage.getItem('RocketLaunches::Theme') === 'dark') { 20 | setIsDark(true) 21 | }else{ 22 | setIsDark(false) 23 | } 24 | }, []) 25 | 26 | function handleChangeTheme(e: React.FormEvent) { 27 | e.preventDefault() 28 | setIsOpen(false) 29 | 30 | const body = document.querySelector('body') 31 | 32 | if(isDark) { 33 | setIsDark(!isDark) 34 | body.classList.add('light') 35 | localStorage.setItem('RocketLaunches::Theme', 'light') 36 | return 37 | } 38 | setIsDark(!isDark) 39 | body.classList.remove('light') 40 | localStorage.setItem('RocketLaunches::Theme', 'dark') 41 | } 42 | 43 | // function handleChangeMeasures(e: React.FormEvent) { 44 | // e.preventDefault() 45 | // setIsOpen(false) 46 | 47 | // if(measures) { 48 | // setMeasures(false) 49 | // localStorage.setItem('RocketLaunches::Measures', 'imperial') 50 | // return 51 | // } 52 | // setMeasures(true) 53 | // localStorage.setItem('RocketLaunches::Measures', 'metric') 54 | // } 55 | 56 | return ( 57 | <> 58 | 59 |
60 | setIsOpen(!isOpen)} /> 61 |

Rocket Launches

62 |
63 | {/*
64 | alert('Not working yet!')} /> 65 |
*/} 66 |
67 | 68 | 69 |

Settings

70 | 71 |

Theme

72 | 73 |

Light

74 | handleChangeTheme(e)} htmlFor="input"> 75 | {}} /> 76 | {/* The onChange is to stop warning */} 77 | 78 | 79 |

Dark

80 |
81 | 82 | {/*

Measures

83 | 84 |

Imperial

85 | handleChangeMeasures(e)} htmlFor="input2"> 86 | {}} /> 87 | 88 | 89 |

Metric

90 |
*/} 91 | 92 |

Sources

93 | 94 |

Public API

95 | 96 |

Repository

97 | | 98 |

API endpoint

99 |
100 | 101 |

Website

102 | 103 |

Repository

104 |
105 | 106 | 109 |
110 | 111 | ) 112 | } -------------------------------------------------------------------------------- /src/data/vehicles.ts: -------------------------------------------------------------------------------- 1 | import RocketInterface from './rocketsTypes' 2 | 3 | export const SpaceX_Falcon1: RocketInterface = { 4 | name: 'Falcon 1', 5 | first_launch: 'March, 24, 2006', 6 | manufacturer: 'SpaceX', 7 | img: 'https://upload.wikimedia.org/wikipedia/commons/thumb/c/c8/Falcon_1_Flight_4_liftoff.jpg/300px-Falcon_1_Flight_4_liftoff.jpg', 8 | reusable: false, 9 | orbital: true, 10 | stages: 2, 11 | propellent: 'RP-1 / LOX', 12 | total_thrust_kn: 450, 13 | wiki_link: 'https://en.wikipedia.org/wiki/Falcon_1', 14 | engines: [ 15 | { 16 | name: 'Merlin, sea lavel', 17 | quantity: 1 18 | }, 19 | { 20 | name: 'Kastel, vacuum', 21 | quantity: 1 22 | } 23 | ], 24 | size: { 25 | height_meters: 21, 26 | diameter_meters: 1.7, 27 | mass_tons: 28 28 | }, 29 | pricing: { 30 | projectCost: 'US$90 million', 31 | costPerLaunch: 'US$7 million', 32 | } 33 | } 34 | 35 | export const SpaceX_Falcon9: RocketInterface = { 36 | name: 'Falcon 9', 37 | first_launch: 'December, 23, 2018', 38 | manufacturer: 'SpaceX', 39 | img: 'https://engenharia360.com/wp-content/uploads/2020/03/spacex-falcon-9-1024x570.jpg', 40 | reusable: true, 41 | orbital: true, 42 | stages: 2, 43 | propellent: 'RP-1 / LOX', 44 | total_thrust_kn: 7607, 45 | wiki_link: 'https://en.wikipedia.org/wiki/Falcon_9', 46 | engines: [ 47 | { 48 | name: 'Merlin, sea lavel', 49 | quantity: 9 50 | }, 51 | { 52 | name: 'Merlin, vacuum', 53 | quantity: 1 54 | } 55 | ], 56 | size: { 57 | height_meters: 70, 58 | diameter_meters: 3.7, 59 | mass_tons: 605 60 | }, 61 | pricing: { 62 | projectCost: 'US$300 million', 63 | costPerLaunch: 'New: US$62 million (2020),\nReused: US$50 million (2019)', 64 | } 65 | } 66 | 67 | export const SpaceX_Starship: RocketInterface = { 68 | name: 'Starship', 69 | first_launch: "First Flight hasn't happened yet", 70 | manufacturer: 'SpaceX', 71 | img: 'https://img.olhardigital.com.br/wp-content/uploads/2020/09/20200901030459.jpg', 72 | reusable: true, 73 | orbital: true, 74 | stages: 2, 75 | propellent: 'CH₄(Methane) / LOX', 76 | total_thrust_kn: 7607, 77 | wiki_link: 'https://en.wikipedia.org/wiki/SpaceX_Starship', 78 | engines: [ 79 | { 80 | name: 'Raptor, sea lavel', 81 | quantity: 35 82 | }, 83 | { 84 | name: 'Raptor, vacuum', 85 | quantity: 3 86 | } 87 | ], 88 | size: { 89 | height_meters: 120, 90 | diameter_meters: 9, 91 | mass_tons: 5000, 92 | }, 93 | pricing: { 94 | projectCost: 'Information not found', 95 | costPerLaunch: 'US$2 million', 96 | } 97 | } 98 | 99 | export const Roscosmos_Soyuz = { 100 | name: 'Soyuz FG', 101 | img: 'https://img.olhardigital.com.br/wp-content/uploads/2021/04/Soyuz_MS-18.jpg', 102 | first_launch: 'May, 21, 2001' 103 | } 104 | 105 | export const RocketLab_Electron = { 106 | name: 'Electron', 107 | img: 'https://www.spacelaunchreport.com/electron2.jpg', 108 | first_launch: 'May, 25, 2017' 109 | } 110 | 111 | export const NorthropGrumman_Minotaur = { 112 | name: 'Minotaur', 113 | img: 'https://upload.wikimedia.org/wikipedia/commons/7/76/Minotaur_I_rocket.jpg', 114 | first_launch: 'January, 27, 2000' 115 | } 116 | 117 | export const BoeingAndNasa_SpaceLaunchSystem = { 118 | name: 'Space Launch System(SLS)', 119 | img: 'https://upload.wikimedia.org/wikipedia/commons/thumb/d/db/Sls_block1_on-pad_sunrisesmall.jpg/800px-Sls_block1_on-pad_sunrisesmall.jpg', 120 | first_launch: "First Flight hasn't happened yet" 121 | } 122 | 123 | export const BlueOrgin_NewShepard = { 124 | name: 'New Shepard', 125 | img: 'https://pplware.sapo.pt/wp-content/uploads/2021/01/new_shepard_blue_origin00.jpg', 126 | first_launch: "April, 29, 2015" 127 | } 128 | 129 | export const NASA_SaturnV = { 130 | name: 'Saturn V', 131 | img: 'https://upload.wikimedia.org/wikipedia/commons/1/16/Apollo_11_Launch_-_GPN-2000-000630.jpg', 132 | first_launch: "November, 9, 1967" 133 | } 134 | 135 | export const ESA_ArianeV = { 136 | name: 'Ariane V', 137 | img: 'https://upload.wikimedia.org/wikipedia/commons/thumb/3/3c/Ariane_5ES_with_ATV_4_on_its_way_to_ELA-3.jpg/220px-Ariane_5ES_with_ATV_4_on_its_way_to_ELA-3.jpg', 138 | first_launch: "June, 4, 1996" 139 | } 140 | 141 | export const CNSA_LongMarchV = { 142 | name: 'Long March V', 143 | img: 'https://upload.wikimedia.org/wikipedia/commons/thumb/4/4e/%E9%95%BF%E5%BE%81%E4%BA%94%E5%8F%B7%E9%81%A5%E4%BA%8C%E7%81%AB%E7%AE%AD%E8%BD%AC%E5%9C%BA.jpg/220px-%E9%95%BF%E5%BE%81%E4%BA%94%E5%8F%B7%E9%81%A5%E4%BA%8C%E7%81%AB%E7%AE%AD%E8%BD%AC%E5%9C%BA.jpg', 144 | first_launch: "November, 3, 2016" 145 | } 146 | 147 | export default [ 148 | SpaceX_Falcon1, 149 | SpaceX_Falcon9, 150 | SpaceX_Starship, 151 | Roscosmos_Soyuz, 152 | RocketLab_Electron, 153 | NorthropGrumman_Minotaur, 154 | BoeingAndNasa_SpaceLaunchSystem, 155 | BlueOrgin_NewShepard, 156 | NASA_SaturnV, 157 | ESA_ArianeV, 158 | CNSA_LongMarchV, 159 | ] -------------------------------------------------------------------------------- /public/workbox-529fc111.js: -------------------------------------------------------------------------------- 1 | define("./workbox-529fc111.js",["exports"],(function(t){"use strict";try{self["workbox:core:6.1.5"]&&_()}catch(t){}const e=(t,...e)=>{let s=t;return e.length>0&&(s+=` :: ${JSON.stringify(e)}`),s};class s extends Error{constructor(t,s){super(e(t,s)),this.name=t,this.details=s}}try{self["workbox:routing:6.1.5"]&&_()}catch(t){}const r=t=>t&&"object"==typeof t?t:{handle:t};class n{constructor(t,e,s="GET"){this.handler=r(e),this.match=t,this.method=s}setCatchHandler(t){this.catchHandler=r(t)}}class i extends n{constructor(t,e,s){super((({url:e})=>{const s=t.exec(e.href);if(s&&(e.origin===location.origin||0===s.index))return s.slice(1)}),e,s)}}class o{constructor(){this.t=new Map,this.i=new Map}get routes(){return this.t}addFetchListener(){self.addEventListener("fetch",(t=>{const{request:e}=t,s=this.handleRequest({request:e,event:t});s&&t.respondWith(s)}))}addCacheListener(){self.addEventListener("message",(t=>{if(t.data&&"CACHE_URLS"===t.data.type){const{payload:e}=t.data,s=Promise.all(e.urlsToCache.map((e=>{"string"==typeof e&&(e=[e]);const s=new Request(...e);return this.handleRequest({request:s,event:t})})));t.waitUntil(s),t.ports&&t.ports[0]&&s.then((()=>t.ports[0].postMessage(!0)))}}))}handleRequest({request:t,event:e}){const s=new URL(t.url,location.href);if(!s.protocol.startsWith("http"))return;const r=s.origin===location.origin,{params:n,route:i}=this.findMatchingRoute({event:e,request:t,sameOrigin:r,url:s});let o=i&&i.handler;const a=t.method;if(!o&&this.i.has(a)&&(o=this.i.get(a)),!o)return;let c;try{c=o.handle({url:s,request:t,event:e,params:n})}catch(t){c=Promise.reject(t)}const h=i&&i.catchHandler;return c instanceof Promise&&(this.o||h)&&(c=c.catch((async r=>{if(h)try{return await h.handle({url:s,request:t,event:e,params:n})}catch(t){r=t}if(this.o)return this.o.handle({url:s,request:t,event:e});throw r}))),c}findMatchingRoute({url:t,sameOrigin:e,request:s,event:r}){const n=this.t.get(s.method)||[];for(const i of n){let n;const o=i.match({url:t,sameOrigin:e,request:s,event:r});if(o)return n=o,(Array.isArray(o)&&0===o.length||o.constructor===Object&&0===Object.keys(o).length||"boolean"==typeof o)&&(n=void 0),{route:i,params:n}}return{}}setDefaultHandler(t,e="GET"){this.i.set(e,r(t))}setCatchHandler(t){this.o=r(t)}registerRoute(t){this.t.has(t.method)||this.t.set(t.method,[]),this.t.get(t.method).push(t)}unregisterRoute(t){if(!this.t.has(t.method))throw new s("unregister-route-but-not-found-with-method",{method:t.method});const e=this.t.get(t.method).indexOf(t);if(!(e>-1))throw new s("unregister-route-route-not-registered");this.t.get(t.method).splice(e,1)}}let a;const c=()=>(a||(a=new o,a.addFetchListener(),a.addCacheListener()),a);try{self["workbox:strategies:6.1.5"]&&_()}catch(t){}const h={cacheWillUpdate:async({response:t})=>200===t.status||0===t.status?t:null},u={googleAnalytics:"googleAnalytics",precache:"precache-v2",prefix:"workbox",runtime:"runtime",suffix:"undefined"!=typeof registration?registration.scope:""},l=t=>[u.prefix,t,u.suffix].filter((t=>t&&t.length>0)).join("-"),f=t=>t||l(u.runtime);function w(){return(w=Object.assign||function(t){for(var e=1;e{this.resolve=t,this.reject=e}))}}const m=new Set;function y(t){return new Promise((e=>setTimeout(e,t)))}function g(t){return"string"==typeof t?new Request(t):t}class v{constructor(t,e){this.h={},Object.assign(this,e),this.event=e.event,this.u=t,this.l=new p,this.p=[],this.m=[...t.plugins],this.g=new Map;for(const t of this.m)this.g.set(t,{});this.event.waitUntil(this.l.promise)}async fetch(t){const{event:e}=this;let r=g(t);if("navigate"===r.mode&&e instanceof FetchEvent&&e.preloadResponse){const t=await e.preloadResponse;if(t)return t}const n=this.hasCallback("fetchDidFail")?r.clone():null;try{for(const t of this.iterateCallbacks("requestWillFetch"))r=await t({request:r.clone(),event:e})}catch(t){throw new s("plugin-error-request-will-fetch",{thrownError:t})}const i=r.clone();try{let t;t=await fetch(r,"navigate"===r.mode?void 0:this.u.fetchOptions);for(const s of this.iterateCallbacks("fetchDidSucceed"))t=await s({event:e,request:i,response:t});return t}catch(t){throw n&&await this.runCallbacks("fetchDidFail",{error:t,event:e,originalRequest:n.clone(),request:i.clone()}),t}}async fetchAndCachePut(t){const e=await this.fetch(t),s=e.clone();return this.waitUntil(this.cachePut(t,s)),e}async cacheMatch(t){const e=g(t);let s;const{cacheName:r,matchOptions:n}=this.u,i=await this.getCacheKey(e,"read"),o=w({},n,{cacheName:r});s=await caches.match(i,o);for(const t of this.iterateCallbacks("cachedResponseWillBeUsed"))s=await t({cacheName:r,matchOptions:n,cachedResponse:s,request:i,event:this.event})||void 0;return s}async cachePut(t,e){const r=g(t);await y(0);const n=await this.getCacheKey(r,"write");if(!e)throw new s("cache-put-with-no-response",{url:(i=n.url,new URL(String(i),location.href).href.replace(new RegExp(`^${location.origin}`),""))});var i;const o=await this.v(e);if(!o)return!1;const{cacheName:a,matchOptions:c}=this.u,h=await self.caches.open(a),u=this.hasCallback("cacheDidUpdate"),l=u?await async function(t,e,s,r){const n=d(e.url,s);if(e.url===n)return t.match(e,r);const i=w({},r,{ignoreSearch:!0}),o=await t.keys(e,i);for(const e of o)if(n===d(e.url,s))return t.match(e,r)}(h,n.clone(),["__WB_REVISION__"],c):null;try{await h.put(n,u?o.clone():o)}catch(t){throw"QuotaExceededError"===t.name&&await async function(){for(const t of m)await t()}(),t}for(const t of this.iterateCallbacks("cacheDidUpdate"))await t({cacheName:a,oldResponse:l,newResponse:o.clone(),request:n,event:this.event});return!0}async getCacheKey(t,e){if(!this.h[e]){let s=t;for(const t of this.iterateCallbacks("cacheKeyWillBeUsed"))s=g(await t({mode:e,request:s,event:this.event,params:this.params}));this.h[e]=s}return this.h[e]}hasCallback(t){for(const e of this.u.plugins)if(t in e)return!0;return!1}async runCallbacks(t,e){for(const s of this.iterateCallbacks(t))await s(e)}*iterateCallbacks(t){for(const e of this.u.plugins)if("function"==typeof e[t]){const s=this.g.get(e),r=r=>{const n=w({},r,{state:s});return e[t](n)};yield r}}waitUntil(t){return this.p.push(t),t}async doneWaiting(){let t;for(;t=this.p.shift();)await t}destroy(){this.l.resolve()}async v(t){let e=t,s=!1;for(const t of this.iterateCallbacks("cacheWillUpdate"))if(e=await t({request:this.request,response:e,event:this.event})||void 0,s=!0,!e)break;return s||e&&200!==e.status&&(e=void 0),e}}class q{constructor(t={}){this.cacheName=f(t.cacheName),this.plugins=t.plugins||[],this.fetchOptions=t.fetchOptions,this.matchOptions=t.matchOptions}handle(t){const[e]=this.handleAll(t);return e}handleAll(t){t instanceof FetchEvent&&(t={event:t,request:t.request});const e=t.event,s="string"==typeof t.request?new Request(t.request):t.request,r="params"in t?t.params:void 0,n=new v(this,{event:e,request:s,params:r}),i=this.q(n,s,e);return[i,this.R(i,n,s,e)]}async q(t,e,r){let n;await t.runCallbacks("handlerWillStart",{event:r,request:e});try{if(n=await this._(e,t),!n||"error"===n.type)throw new s("no-response",{url:e.url})}catch(s){for(const i of t.iterateCallbacks("handlerDidError"))if(n=await i({error:s,event:r,request:e}),n)break;if(!n)throw s}for(const s of t.iterateCallbacks("handlerWillRespond"))n=await s({event:r,request:e,response:n});return n}async R(t,e,s,r){let n,i;try{n=await t}catch(i){}try{await e.runCallbacks("handlerDidRespond",{event:r,request:s,response:n}),await e.doneWaiting()}catch(t){i=t}if(await e.runCallbacks("handlerDidComplete",{event:r,request:s,response:n,error:i}),e.destroy(),i)throw i}}t.NetworkFirst=class extends q{constructor(t={}){super(t),this.plugins.some((t=>"cacheWillUpdate"in t))||this.plugins.unshift(h),this.C=t.networkTimeoutSeconds||0}async _(t,e){const r=[],n=[];let i;if(this.C){const{id:s,promise:o}=this.O({request:t,logs:r,handler:e});i=s,n.push(o)}const o=this.U({timeoutId:i,request:t,logs:r,handler:e});n.push(o);const a=await e.waitUntil((async()=>await e.waitUntil(Promise.race(n))||await o)());if(!a)throw new s("no-response",{url:t.url});return a}O({request:t,logs:e,handler:s}){let r;return{promise:new Promise((e=>{r=setTimeout((async()=>{e(await s.cacheMatch(t))}),1e3*this.C)})),id:r}}async U({timeoutId:t,request:e,logs:s,handler:r}){let n,i;try{i=await r.fetchAndCachePut(e)}catch(t){n=t}return t&&clearTimeout(t),!n&&i||(i=await r.cacheMatch(e)),i}},t.NetworkOnly=class extends q{constructor(t={}){super(t),this.C=t.networkTimeoutSeconds||0}async _(t,e){let r,n;try{const s=[e.fetch(t)];if(this.C){const t=y(1e3*this.C);s.push(t)}if(n=await Promise.race(s),!n)throw new Error(`Timed out the network response after ${this.C} seconds.`)}catch(t){r=t}if(!n)throw new s("no-response",{url:t.url,error:r});return n}},t.clientsClaim=function(){self.addEventListener("activate",(()=>self.clients.claim()))},t.registerRoute=function(t,e,r){let o;if("string"==typeof t){const s=new URL(t,location.href);o=new n((({url:t})=>t.href===s.href),e,r)}else if(t instanceof RegExp)o=new i(t,e,r);else if("function"==typeof t)o=new n(t,e,r);else{if(!(t instanceof n))throw new s("unsupported-route-type",{moduleName:"workbox-routing",funcName:"registerRoute",paramName:"capture"});o=t}return c().registerRoute(o),o}})); 2 | //# sourceMappingURL=workbox-529fc111.js.map 3 | -------------------------------------------------------------------------------- /public/images/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /public/workbox-529fc111.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"workbox-529fc111.js","sources":["node_modules/workbox-core/_version.js","node_modules/workbox-core/_private/logger.js","node_modules/workbox-core/models/messages/messageGenerator.js","node_modules/workbox-core/_private/WorkboxError.js","node_modules/workbox-routing/_version.js","node_modules/workbox-routing/utils/constants.js","node_modules/workbox-routing/utils/normalizeHandler.js","node_modules/workbox-routing/Route.js","node_modules/workbox-routing/RegExpRoute.js","node_modules/workbox-routing/Router.js","node_modules/workbox-routing/utils/getOrCreateDefaultRouter.js","node_modules/workbox-strategies/_version.js","node_modules/workbox-strategies/plugins/cacheOkAndOpaquePlugin.js","node_modules/workbox-core/_private/cacheNames.js","node_modules/workbox-core/_private/cacheMatchIgnoreParams.js","node_modules/workbox-core/_private/Deferred.js","node_modules/workbox-core/models/quotaErrorCallbacks.js","node_modules/workbox-core/_private/timeout.js","node_modules/workbox-strategies/StrategyHandler.js","node_modules/workbox-core/_private/getFriendlyURL.js","node_modules/workbox-core/_private/executeQuotaErrorCallbacks.js","node_modules/workbox-strategies/Strategy.js","node_modules/workbox-strategies/NetworkFirst.js","node_modules/workbox-strategies/NetworkOnly.js","node_modules/workbox-core/clientsClaim.js","node_modules/workbox-routing/registerRoute.js"],"sourcesContent":["\"use strict\";\n// @ts-ignore\ntry {\n self['workbox:core:6.1.5'] && _();\n}\ncatch (e) { }\n","/*\n Copyright 2019 Google LLC\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\nconst logger = (process.env.NODE_ENV === 'production' ? null : (() => {\n // Don't overwrite this value if it's already set.\n // See https://github.com/GoogleChrome/workbox/pull/2284#issuecomment-560470923\n if (!('__WB_DISABLE_DEV_LOGS' in self)) {\n self.__WB_DISABLE_DEV_LOGS = false;\n }\n let inGroup = false;\n const methodToColorMap = {\n debug: `#7f8c8d`,\n log: `#2ecc71`,\n warn: `#f39c12`,\n error: `#c0392b`,\n groupCollapsed: `#3498db`,\n groupEnd: null,\n };\n const print = function (method, args) {\n if (self.__WB_DISABLE_DEV_LOGS) {\n return;\n }\n if (method === 'groupCollapsed') {\n // Safari doesn't print all console.groupCollapsed() arguments:\n // https://bugs.webkit.org/show_bug.cgi?id=182754\n if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) {\n console[method](...args);\n return;\n }\n }\n const styles = [\n `background: ${methodToColorMap[method]}`,\n `border-radius: 0.5em`,\n `color: white`,\n `font-weight: bold`,\n `padding: 2px 0.5em`,\n ];\n // When in a group, the workbox prefix is not displayed.\n const logPrefix = inGroup ? [] : ['%cworkbox', styles.join(';')];\n console[method](...logPrefix, ...args);\n if (method === 'groupCollapsed') {\n inGroup = true;\n }\n if (method === 'groupEnd') {\n inGroup = false;\n }\n };\n const api = {};\n const loggerMethods = Object.keys(methodToColorMap);\n for (const key of loggerMethods) {\n const method = key;\n api[method] = (...args) => {\n print(method, args);\n };\n }\n return api;\n})());\nexport { logger };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { messages } from './messages.js';\nimport '../../_version.js';\nconst fallback = (code, ...args) => {\n let msg = code;\n if (args.length > 0) {\n msg += ` :: ${JSON.stringify(args)}`;\n }\n return msg;\n};\nconst generatorFunction = (code, details = {}) => {\n const message = messages[code];\n if (!message) {\n throw new Error(`Unable to find message for code '${code}'.`);\n }\n return message(details);\n};\nexport const messageGenerator = (process.env.NODE_ENV === 'production') ?\n fallback : generatorFunction;\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { messageGenerator } from '../models/messages/messageGenerator.js';\nimport '../_version.js';\n/**\n * Workbox errors should be thrown with this class.\n * This allows use to ensure the type easily in tests,\n * helps developers identify errors from workbox\n * easily and allows use to optimise error\n * messages correctly.\n *\n * @private\n */\nclass WorkboxError extends Error {\n /**\n *\n * @param {string} errorCode The error code that\n * identifies this particular error.\n * @param {Object=} details Any relevant arguments\n * that will help developers identify issues should\n * be added as a key on the context object.\n */\n constructor(errorCode, details) {\n const message = messageGenerator(errorCode, details);\n super(message);\n this.name = errorCode;\n this.details = details;\n }\n}\nexport { WorkboxError };\n","\"use strict\";\n// @ts-ignore\ntry {\n self['workbox:routing:6.1.5'] && _();\n}\ncatch (e) { }\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\n/**\n * The default HTTP method, 'GET', used when there's no specific method\n * configured for a route.\n *\n * @type {string}\n *\n * @private\n */\nexport const defaultMethod = 'GET';\n/**\n * The list of valid HTTP methods associated with requests that could be routed.\n *\n * @type {Array}\n *\n * @private\n */\nexport const validMethods = [\n 'DELETE',\n 'GET',\n 'HEAD',\n 'PATCH',\n 'POST',\n 'PUT',\n];\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { assert } from 'workbox-core/_private/assert.js';\nimport '../_version.js';\n/**\n * @param {function()|Object} handler Either a function, or an object with a\n * 'handle' method.\n * @return {Object} An object with a handle method.\n *\n * @private\n */\nexport const normalizeHandler = (handler) => {\n if (handler && typeof handler === 'object') {\n if (process.env.NODE_ENV !== 'production') {\n assert.hasMethod(handler, 'handle', {\n moduleName: 'workbox-routing',\n className: 'Route',\n funcName: 'constructor',\n paramName: 'handler',\n });\n }\n return handler;\n }\n else {\n if (process.env.NODE_ENV !== 'production') {\n assert.isType(handler, 'function', {\n moduleName: 'workbox-routing',\n className: 'Route',\n funcName: 'constructor',\n paramName: 'handler',\n });\n }\n return { handle: handler };\n }\n};\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { assert } from 'workbox-core/_private/assert.js';\nimport { defaultMethod, validMethods } from './utils/constants.js';\nimport { normalizeHandler } from './utils/normalizeHandler.js';\nimport './_version.js';\n/**\n * A `Route` consists of a pair of callback functions, \"match\" and \"handler\".\n * The \"match\" callback determine if a route should be used to \"handle\" a\n * request by returning a non-falsy value if it can. The \"handler\" callback\n * is called when there is a match and should return a Promise that resolves\n * to a `Response`.\n *\n * @memberof module:workbox-routing\n */\nclass Route {\n /**\n * Constructor for Route class.\n *\n * @param {module:workbox-routing~matchCallback} match\n * A callback function that determines whether the route matches a given\n * `fetch` event by returning a non-falsy value.\n * @param {module:workbox-routing~handlerCallback} handler A callback\n * function that returns a Promise resolving to a Response.\n * @param {string} [method='GET'] The HTTP method to match the Route\n * against.\n */\n constructor(match, handler, method = defaultMethod) {\n if (process.env.NODE_ENV !== 'production') {\n assert.isType(match, 'function', {\n moduleName: 'workbox-routing',\n className: 'Route',\n funcName: 'constructor',\n paramName: 'match',\n });\n if (method) {\n assert.isOneOf(method, validMethods, { paramName: 'method' });\n }\n }\n // These values are referenced directly by Router so cannot be\n // altered by minificaton.\n this.handler = normalizeHandler(handler);\n this.match = match;\n this.method = method;\n }\n /**\n *\n * @param {module:workbox-routing-handlerCallback} handler A callback\n * function that returns a Promise resolving to a Response\n */\n setCatchHandler(handler) {\n this.catchHandler = normalizeHandler(handler);\n }\n}\nexport { Route };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { assert } from 'workbox-core/_private/assert.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { Route } from './Route.js';\nimport './_version.js';\n/**\n * RegExpRoute makes it easy to create a regular expression based\n * [Route]{@link module:workbox-routing.Route}.\n *\n * For same-origin requests the RegExp only needs to match part of the URL. For\n * requests against third-party servers, you must define a RegExp that matches\n * the start of the URL.\n *\n * [See the module docs for info.]{@link https://developers.google.com/web/tools/workbox/modules/workbox-routing}\n *\n * @memberof module:workbox-routing\n * @extends module:workbox-routing.Route\n */\nclass RegExpRoute extends Route {\n /**\n * If the regular expression contains\n * [capture groups]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#grouping-back-references},\n * the captured values will be passed to the\n * [handler's]{@link module:workbox-routing~handlerCallback} `params`\n * argument.\n *\n * @param {RegExp} regExp The regular expression to match against URLs.\n * @param {module:workbox-routing~handlerCallback} handler A callback\n * function that returns a Promise resulting in a Response.\n * @param {string} [method='GET'] The HTTP method to match the Route\n * against.\n */\n constructor(regExp, handler, method) {\n if (process.env.NODE_ENV !== 'production') {\n assert.isInstance(regExp, RegExp, {\n moduleName: 'workbox-routing',\n className: 'RegExpRoute',\n funcName: 'constructor',\n paramName: 'pattern',\n });\n }\n const match = ({ url }) => {\n const result = regExp.exec(url.href);\n // Return immediately if there's no match.\n if (!result) {\n return;\n }\n // Require that the match start at the first character in the URL string\n // if it's a cross-origin request.\n // See https://github.com/GoogleChrome/workbox/issues/281 for the context\n // behind this behavior.\n if ((url.origin !== location.origin) && (result.index !== 0)) {\n if (process.env.NODE_ENV !== 'production') {\n logger.debug(`The regular expression '${regExp}' only partially matched ` +\n `against the cross-origin URL '${url}'. RegExpRoute's will only ` +\n `handle cross-origin requests if they match the entire URL.`);\n }\n return;\n }\n // If the route matches, but there aren't any capture groups defined, then\n // this will return [], which is truthy and therefore sufficient to\n // indicate a match.\n // If there are capture groups, then it will return their values.\n return result.slice(1);\n };\n super(match, handler, method);\n }\n}\nexport { RegExpRoute };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { assert } from 'workbox-core/_private/assert.js';\nimport { getFriendlyURL } from 'workbox-core/_private/getFriendlyURL.js';\nimport { defaultMethod } from './utils/constants.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { normalizeHandler } from './utils/normalizeHandler.js';\nimport { WorkboxError } from 'workbox-core/_private/WorkboxError.js';\nimport './_version.js';\n/**\n * The Router can be used to process a FetchEvent through one or more\n * [Routes]{@link module:workbox-routing.Route} responding with a Request if\n * a matching route exists.\n *\n * If no route matches a given a request, the Router will use a \"default\"\n * handler if one is defined.\n *\n * Should the matching Route throw an error, the Router will use a \"catch\"\n * handler if one is defined to gracefully deal with issues and respond with a\n * Request.\n *\n * If a request matches multiple routes, the **earliest** registered route will\n * be used to respond to the request.\n *\n * @memberof module:workbox-routing\n */\nclass Router {\n /**\n * Initializes a new Router.\n */\n constructor() {\n this._routes = new Map();\n this._defaultHandlerMap = new Map();\n }\n /**\n * @return {Map>} routes A `Map` of HTTP\n * method name ('GET', etc.) to an array of all the corresponding `Route`\n * instances that are registered.\n */\n get routes() {\n return this._routes;\n }\n /**\n * Adds a fetch event listener to respond to events when a route matches\n * the event's request.\n */\n addFetchListener() {\n // See https://github.com/Microsoft/TypeScript/issues/28357#issuecomment-436484705\n self.addEventListener('fetch', ((event) => {\n const { request } = event;\n const responsePromise = this.handleRequest({ request, event });\n if (responsePromise) {\n event.respondWith(responsePromise);\n }\n }));\n }\n /**\n * Adds a message event listener for URLs to cache from the window.\n * This is useful to cache resources loaded on the page prior to when the\n * service worker started controlling it.\n *\n * The format of the message data sent from the window should be as follows.\n * Where the `urlsToCache` array may consist of URL strings or an array of\n * URL string + `requestInit` object (the same as you'd pass to `fetch()`).\n *\n * ```\n * {\n * type: 'CACHE_URLS',\n * payload: {\n * urlsToCache: [\n * './script1.js',\n * './script2.js',\n * ['./script3.js', {mode: 'no-cors'}],\n * ],\n * },\n * }\n * ```\n */\n addCacheListener() {\n // See https://github.com/Microsoft/TypeScript/issues/28357#issuecomment-436484705\n self.addEventListener('message', ((event) => {\n if (event.data && event.data.type === 'CACHE_URLS') {\n const { payload } = event.data;\n if (process.env.NODE_ENV !== 'production') {\n logger.debug(`Caching URLs from the window`, payload.urlsToCache);\n }\n const requestPromises = Promise.all(payload.urlsToCache.map((entry) => {\n if (typeof entry === 'string') {\n entry = [entry];\n }\n const request = new Request(...entry);\n return this.handleRequest({ request, event });\n // TODO(philipwalton): TypeScript errors without this typecast for\n // some reason (probably a bug). The real type here should work but\n // doesn't: `Array | undefined>`.\n })); // TypeScript\n event.waitUntil(requestPromises);\n // If a MessageChannel was used, reply to the message on success.\n if (event.ports && event.ports[0]) {\n requestPromises.then(() => event.ports[0].postMessage(true));\n }\n }\n }));\n }\n /**\n * Apply the routing rules to a FetchEvent object to get a Response from an\n * appropriate Route's handler.\n *\n * @param {Object} options\n * @param {Request} options.request The request to handle.\n * @param {ExtendableEvent} options.event The event that triggered the\n * request.\n * @return {Promise|undefined} A promise is returned if a\n * registered route can handle the request. If there is no matching\n * route and there's no `defaultHandler`, `undefined` is returned.\n */\n handleRequest({ request, event }) {\n if (process.env.NODE_ENV !== 'production') {\n assert.isInstance(request, Request, {\n moduleName: 'workbox-routing',\n className: 'Router',\n funcName: 'handleRequest',\n paramName: 'options.request',\n });\n }\n const url = new URL(request.url, location.href);\n if (!url.protocol.startsWith('http')) {\n if (process.env.NODE_ENV !== 'production') {\n logger.debug(`Workbox Router only supports URLs that start with 'http'.`);\n }\n return;\n }\n const sameOrigin = url.origin === location.origin;\n const { params, route } = this.findMatchingRoute({\n event,\n request,\n sameOrigin,\n url,\n });\n let handler = route && route.handler;\n const debugMessages = [];\n if (process.env.NODE_ENV !== 'production') {\n if (handler) {\n debugMessages.push([\n `Found a route to handle this request:`, route,\n ]);\n if (params) {\n debugMessages.push([\n `Passing the following params to the route's handler:`, params,\n ]);\n }\n }\n }\n // If we don't have a handler because there was no matching route, then\n // fall back to defaultHandler if that's defined.\n const method = request.method;\n if (!handler && this._defaultHandlerMap.has(method)) {\n if (process.env.NODE_ENV !== 'production') {\n debugMessages.push(`Failed to find a matching route. Falling ` +\n `back to the default handler for ${method}.`);\n }\n handler = this._defaultHandlerMap.get(method);\n }\n if (!handler) {\n if (process.env.NODE_ENV !== 'production') {\n // No handler so Workbox will do nothing. If logs is set of debug\n // i.e. verbose, we should print out this information.\n logger.debug(`No route found for: ${getFriendlyURL(url)}`);\n }\n return;\n }\n if (process.env.NODE_ENV !== 'production') {\n // We have a handler, meaning Workbox is going to handle the route.\n // print the routing details to the console.\n logger.groupCollapsed(`Router is responding to: ${getFriendlyURL(url)}`);\n debugMessages.forEach((msg) => {\n if (Array.isArray(msg)) {\n logger.log(...msg);\n }\n else {\n logger.log(msg);\n }\n });\n logger.groupEnd();\n }\n // Wrap in try and catch in case the handle method throws a synchronous\n // error. It should still callback to the catch handler.\n let responsePromise;\n try {\n responsePromise = handler.handle({ url, request, event, params });\n }\n catch (err) {\n responsePromise = Promise.reject(err);\n }\n // Get route's catch handler, if it exists\n const catchHandler = route && route.catchHandler;\n if (responsePromise instanceof Promise && (this._catchHandler || catchHandler)) {\n responsePromise = responsePromise.catch(async (err) => {\n // If there's a route catch handler, process that first\n if (catchHandler) {\n if (process.env.NODE_ENV !== 'production') {\n // Still include URL here as it will be async from the console group\n // and may not make sense without the URL\n logger.groupCollapsed(`Error thrown when responding to: ` +\n ` ${getFriendlyURL(url)}. Falling back to route's Catch Handler.`);\n logger.error(`Error thrown by:`, route);\n logger.error(err);\n logger.groupEnd();\n }\n try {\n return await catchHandler.handle({ url, request, event, params });\n }\n catch (catchErr) {\n err = catchErr;\n }\n }\n if (this._catchHandler) {\n if (process.env.NODE_ENV !== 'production') {\n // Still include URL here as it will be async from the console group\n // and may not make sense without the URL\n logger.groupCollapsed(`Error thrown when responding to: ` +\n ` ${getFriendlyURL(url)}. Falling back to global Catch Handler.`);\n logger.error(`Error thrown by:`, route);\n logger.error(err);\n logger.groupEnd();\n }\n return this._catchHandler.handle({ url, request, event });\n }\n throw err;\n });\n }\n return responsePromise;\n }\n /**\n * Checks a request and URL (and optionally an event) against the list of\n * registered routes, and if there's a match, returns the corresponding\n * route along with any params generated by the match.\n *\n * @param {Object} options\n * @param {URL} options.url\n * @param {boolean} options.sameOrigin The result of comparing `url.origin`\n * against the current origin.\n * @param {Request} options.request The request to match.\n * @param {Event} options.event The corresponding event.\n * @return {Object} An object with `route` and `params` properties.\n * They are populated if a matching route was found or `undefined`\n * otherwise.\n */\n findMatchingRoute({ url, sameOrigin, request, event }) {\n const routes = this._routes.get(request.method) || [];\n for (const route of routes) {\n let params;\n const matchResult = route.match({ url, sameOrigin, request, event });\n if (matchResult) {\n if (process.env.NODE_ENV !== 'production') {\n // Warn developers that using an async matchCallback is almost always\n // not the right thing to do. \n if (matchResult instanceof Promise) {\n logger.warn(`While routing ${getFriendlyURL(url)}, an async ` +\n `matchCallback function was used. Please convert the ` +\n `following route to use a synchronous matchCallback function:`, route);\n }\n }\n // See https://github.com/GoogleChrome/workbox/issues/2079\n params = matchResult;\n if (Array.isArray(matchResult) && matchResult.length === 0) {\n // Instead of passing an empty array in as params, use undefined.\n params = undefined;\n }\n else if ((matchResult.constructor === Object &&\n Object.keys(matchResult).length === 0)) {\n // Instead of passing an empty object in as params, use undefined.\n params = undefined;\n }\n else if (typeof matchResult === 'boolean') {\n // For the boolean value true (rather than just something truth-y),\n // don't set params.\n // See https://github.com/GoogleChrome/workbox/pull/2134#issuecomment-513924353\n params = undefined;\n }\n // Return early if have a match.\n return { route, params };\n }\n }\n // If no match was found above, return and empty object.\n return {};\n }\n /**\n * Define a default `handler` that's called when no routes explicitly\n * match the incoming request.\n *\n * Each HTTP method ('GET', 'POST', etc.) gets its own default handler.\n *\n * Without a default handler, unmatched requests will go against the\n * network as if there were no service worker present.\n *\n * @param {module:workbox-routing~handlerCallback} handler A callback\n * function that returns a Promise resulting in a Response.\n * @param {string} [method='GET'] The HTTP method to associate with this\n * default handler. Each method has its own default.\n */\n setDefaultHandler(handler, method = defaultMethod) {\n this._defaultHandlerMap.set(method, normalizeHandler(handler));\n }\n /**\n * If a Route throws an error while handling a request, this `handler`\n * will be called and given a chance to provide a response.\n *\n * @param {module:workbox-routing~handlerCallback} handler A callback\n * function that returns a Promise resulting in a Response.\n */\n setCatchHandler(handler) {\n this._catchHandler = normalizeHandler(handler);\n }\n /**\n * Registers a route with the router.\n *\n * @param {module:workbox-routing.Route} route The route to register.\n */\n registerRoute(route) {\n if (process.env.NODE_ENV !== 'production') {\n assert.isType(route, 'object', {\n moduleName: 'workbox-routing',\n className: 'Router',\n funcName: 'registerRoute',\n paramName: 'route',\n });\n assert.hasMethod(route, 'match', {\n moduleName: 'workbox-routing',\n className: 'Router',\n funcName: 'registerRoute',\n paramName: 'route',\n });\n assert.isType(route.handler, 'object', {\n moduleName: 'workbox-routing',\n className: 'Router',\n funcName: 'registerRoute',\n paramName: 'route',\n });\n assert.hasMethod(route.handler, 'handle', {\n moduleName: 'workbox-routing',\n className: 'Router',\n funcName: 'registerRoute',\n paramName: 'route.handler',\n });\n assert.isType(route.method, 'string', {\n moduleName: 'workbox-routing',\n className: 'Router',\n funcName: 'registerRoute',\n paramName: 'route.method',\n });\n }\n if (!this._routes.has(route.method)) {\n this._routes.set(route.method, []);\n }\n // Give precedence to all of the earlier routes by adding this additional\n // route to the end of the array.\n this._routes.get(route.method).push(route);\n }\n /**\n * Unregisters a route with the router.\n *\n * @param {module:workbox-routing.Route} route The route to unregister.\n */\n unregisterRoute(route) {\n if (!this._routes.has(route.method)) {\n throw new WorkboxError('unregister-route-but-not-found-with-method', {\n method: route.method,\n });\n }\n const routeIndex = this._routes.get(route.method).indexOf(route);\n if (routeIndex > -1) {\n this._routes.get(route.method).splice(routeIndex, 1);\n }\n else {\n throw new WorkboxError('unregister-route-route-not-registered');\n }\n }\n}\nexport { Router };\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { Router } from '../Router.js';\nimport '../_version.js';\nlet defaultRouter;\n/**\n * Creates a new, singleton Router instance if one does not exist. If one\n * does already exist, that instance is returned.\n *\n * @private\n * @return {Router}\n */\nexport const getOrCreateDefaultRouter = () => {\n if (!defaultRouter) {\n defaultRouter = new Router();\n // The helpers that use the default Router assume these listeners exist.\n defaultRouter.addFetchListener();\n defaultRouter.addCacheListener();\n }\n return defaultRouter;\n};\n","\"use strict\";\n// @ts-ignore\ntry {\n self['workbox:strategies:6.1.5'] && _();\n}\ncatch (e) { }\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\nexport const cacheOkAndOpaquePlugin = {\n /**\n * Returns a valid response (to allow caching) if the status is 200 (OK) or\n * 0 (opaque).\n *\n * @param {Object} options\n * @param {Response} options.response\n * @return {Response|null}\n *\n * @private\n */\n cacheWillUpdate: async ({ response }) => {\n if (response.status === 200 || response.status === 0) {\n return response;\n }\n return null;\n },\n};\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\nconst _cacheNameDetails = {\n googleAnalytics: 'googleAnalytics',\n precache: 'precache-v2',\n prefix: 'workbox',\n runtime: 'runtime',\n suffix: typeof registration !== 'undefined' ? registration.scope : '',\n};\nconst _createCacheName = (cacheName) => {\n return [_cacheNameDetails.prefix, cacheName, _cacheNameDetails.suffix]\n .filter((value) => value && value.length > 0)\n .join('-');\n};\nconst eachCacheNameDetail = (fn) => {\n for (const key of Object.keys(_cacheNameDetails)) {\n fn(key);\n }\n};\nexport const cacheNames = {\n updateDetails: (details) => {\n eachCacheNameDetail((key) => {\n if (typeof details[key] === 'string') {\n _cacheNameDetails[key] = details[key];\n }\n });\n },\n getGoogleAnalyticsName: (userCacheName) => {\n return userCacheName || _createCacheName(_cacheNameDetails.googleAnalytics);\n },\n getPrecacheName: (userCacheName) => {\n return userCacheName || _createCacheName(_cacheNameDetails.precache);\n },\n getPrefix: () => {\n return _cacheNameDetails.prefix;\n },\n getRuntimeName: (userCacheName) => {\n return userCacheName || _createCacheName(_cacheNameDetails.runtime);\n },\n getSuffix: () => {\n return _cacheNameDetails.suffix;\n },\n};\n","/*\n Copyright 2020 Google LLC\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\nfunction stripParams(fullURL, ignoreParams) {\n const strippedURL = new URL(fullURL);\n for (const param of ignoreParams) {\n strippedURL.searchParams.delete(param);\n }\n return strippedURL.href;\n}\n/**\n * Matches an item in the cache, ignoring specific URL params. This is similar\n * to the `ignoreSearch` option, but it allows you to ignore just specific\n * params (while continuing to match on the others).\n *\n * @private\n * @param {Cache} cache\n * @param {Request} request\n * @param {Object} matchOptions\n * @param {Array} ignoreParams\n * @return {Promise}\n */\nasync function cacheMatchIgnoreParams(cache, request, ignoreParams, matchOptions) {\n const strippedRequestURL = stripParams(request.url, ignoreParams);\n // If the request doesn't include any ignored params, match as normal.\n if (request.url === strippedRequestURL) {\n return cache.match(request, matchOptions);\n }\n // Otherwise, match by comparing keys\n const keysOptions = { ...matchOptions, ignoreSearch: true };\n const cacheKeys = await cache.keys(request, keysOptions);\n for (const cacheKey of cacheKeys) {\n const strippedCacheKeyURL = stripParams(cacheKey.url, ignoreParams);\n if (strippedRequestURL === strippedCacheKeyURL) {\n return cache.match(cacheKey, matchOptions);\n }\n }\n return;\n}\nexport { cacheMatchIgnoreParams };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\n/**\n * The Deferred class composes Promises in a way that allows for them to be\n * resolved or rejected from outside the constructor. In most cases promises\n * should be used directly, but Deferreds can be necessary when the logic to\n * resolve a promise must be separate.\n *\n * @private\n */\nclass Deferred {\n /**\n * Creates a promise and exposes its resolve and reject functions as methods.\n */\n constructor() {\n this.promise = new Promise((resolve, reject) => {\n this.resolve = resolve;\n this.reject = reject;\n });\n }\n}\nexport { Deferred };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\n// Callbacks to be executed whenever there's a quota error.\nconst quotaErrorCallbacks = new Set();\nexport { quotaErrorCallbacks };\n","/*\n Copyright 2019 Google LLC\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\n/**\n * Returns a promise that resolves and the passed number of milliseconds.\n * This utility is an async/await-friendly version of `setTimeout`.\n *\n * @param {number} ms\n * @return {Promise}\n * @private\n */\nexport function timeout(ms) {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","/*\n Copyright 2020 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { assert } from 'workbox-core/_private/assert.js';\nimport { cacheMatchIgnoreParams } from 'workbox-core/_private/cacheMatchIgnoreParams.js';\nimport { Deferred } from 'workbox-core/_private/Deferred.js';\nimport { executeQuotaErrorCallbacks } from 'workbox-core/_private/executeQuotaErrorCallbacks.js';\nimport { getFriendlyURL } from 'workbox-core/_private/getFriendlyURL.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { timeout } from 'workbox-core/_private/timeout.js';\nimport { WorkboxError } from 'workbox-core/_private/WorkboxError.js';\nimport './_version.js';\nfunction toRequest(input) {\n return (typeof input === 'string') ? new Request(input) : input;\n}\n/**\n * A class created every time a Strategy instance instance calls\n * [handle()]{@link module:workbox-strategies.Strategy~handle} or\n * [handleAll()]{@link module:workbox-strategies.Strategy~handleAll} that wraps all fetch and\n * cache actions around plugin callbacks and keeps track of when the strategy\n * is \"done\" (i.e. all added `event.waitUntil()` promises have resolved).\n *\n * @memberof module:workbox-strategies\n */\nclass StrategyHandler {\n /**\n * Creates a new instance associated with the passed strategy and event\n * that's handling the request.\n *\n * The constructor also initializes the state that will be passed to each of\n * the plugins handling this request.\n *\n * @param {module:workbox-strategies.Strategy} strategy\n * @param {Object} options\n * @param {Request|string} options.request A request to run this strategy for.\n * @param {ExtendableEvent} options.event The event associated with the\n * request.\n * @param {URL} [options.url]\n * @param {*} [options.params]\n * [match callback]{@link module:workbox-routing~matchCallback},\n * (if applicable).\n */\n constructor(strategy, options) {\n this._cacheKeys = {};\n /**\n * The request the strategy is performing (passed to the strategy's\n * `handle()` or `handleAll()` method).\n * @name request\n * @instance\n * @type {Request}\n * @memberof module:workbox-strategies.StrategyHandler\n */\n /**\n * The event associated with this request.\n * @name event\n * @instance\n * @type {ExtendableEvent}\n * @memberof module:workbox-strategies.StrategyHandler\n */\n /**\n * A `URL` instance of `request.url` (if passed to the strategy's\n * `handle()` or `handleAll()` method).\n * Note: the `url` param will be present if the strategy was invoked\n * from a workbox `Route` object.\n * @name url\n * @instance\n * @type {URL|undefined}\n * @memberof module:workbox-strategies.StrategyHandler\n */\n /**\n * A `param` value (if passed to the strategy's\n * `handle()` or `handleAll()` method).\n * Note: the `param` param will be present if the strategy was invoked\n * from a workbox `Route` object and the\n * [match callback]{@link module:workbox-routing~matchCallback} returned\n * a truthy value (it will be that value).\n * @name params\n * @instance\n * @type {*|undefined}\n * @memberof module:workbox-strategies.StrategyHandler\n */\n if (process.env.NODE_ENV !== 'production') {\n assert.isInstance(options.event, ExtendableEvent, {\n moduleName: 'workbox-strategies',\n className: 'StrategyHandler',\n funcName: 'constructor',\n paramName: 'options.event',\n });\n }\n Object.assign(this, options);\n this.event = options.event;\n this._strategy = strategy;\n this._handlerDeferred = new Deferred();\n this._extendLifetimePromises = [];\n // Copy the plugins list (since it's mutable on the strategy),\n // so any mutations don't affect this handler instance.\n this._plugins = [...strategy.plugins];\n this._pluginStateMap = new Map();\n for (const plugin of this._plugins) {\n this._pluginStateMap.set(plugin, {});\n }\n this.event.waitUntil(this._handlerDeferred.promise);\n }\n /**\n * Fetches a given request (and invokes any applicable plugin callback\n * methods) using the `fetchOptions` (for non-navigation requests) and\n * `plugins` defined on the `Strategy` object.\n *\n * The following plugin lifecycle methods are invoked when using this method:\n * - `requestWillFetch()`\n * - `fetchDidSucceed()`\n * - `fetchDidFail()`\n *\n * @param {Request|string} input The URL or request to fetch.\n * @return {Promise}\n */\n async fetch(input) {\n const { event } = this;\n let request = toRequest(input);\n if (request.mode === 'navigate' &&\n event instanceof FetchEvent &&\n event.preloadResponse) {\n const possiblePreloadResponse = await event.preloadResponse;\n if (possiblePreloadResponse) {\n if (process.env.NODE_ENV !== 'production') {\n logger.log(`Using a preloaded navigation response for ` +\n `'${getFriendlyURL(request.url)}'`);\n }\n return possiblePreloadResponse;\n }\n }\n // If there is a fetchDidFail plugin, we need to save a clone of the\n // original request before it's either modified by a requestWillFetch\n // plugin or before the original request's body is consumed via fetch().\n const originalRequest = this.hasCallback('fetchDidFail') ?\n request.clone() : null;\n try {\n for (const cb of this.iterateCallbacks('requestWillFetch')) {\n request = await cb({ request: request.clone(), event });\n }\n }\n catch (err) {\n throw new WorkboxError('plugin-error-request-will-fetch', {\n thrownError: err,\n });\n }\n // The request can be altered by plugins with `requestWillFetch` making\n // the original request (most likely from a `fetch` event) different\n // from the Request we make. Pass both to `fetchDidFail` to aid debugging.\n const pluginFilteredRequest = request.clone();\n try {\n let fetchResponse;\n // See https://github.com/GoogleChrome/workbox/issues/1796\n fetchResponse = await fetch(request, request.mode === 'navigate' ?\n undefined : this._strategy.fetchOptions);\n if (process.env.NODE_ENV !== 'production') {\n logger.debug(`Network request for ` +\n `'${getFriendlyURL(request.url)}' returned a response with ` +\n `status '${fetchResponse.status}'.`);\n }\n for (const callback of this.iterateCallbacks('fetchDidSucceed')) {\n fetchResponse = await callback({\n event,\n request: pluginFilteredRequest,\n response: fetchResponse,\n });\n }\n return fetchResponse;\n }\n catch (error) {\n if (process.env.NODE_ENV !== 'production') {\n logger.log(`Network request for ` +\n `'${getFriendlyURL(request.url)}' threw an error.`, error);\n }\n // `originalRequest` will only exist if a `fetchDidFail` callback\n // is being used (see above).\n if (originalRequest) {\n await this.runCallbacks('fetchDidFail', {\n error,\n event,\n originalRequest: originalRequest.clone(),\n request: pluginFilteredRequest.clone(),\n });\n }\n throw error;\n }\n }\n /**\n * Calls `this.fetch()` and (in the background) runs `this.cachePut()` on\n * the response generated by `this.fetch()`.\n *\n * The call to `this.cachePut()` automatically invokes `this.waitUntil()`,\n * so you do not have to manually call `waitUntil()` on the event.\n *\n * @param {Request|string} input The request or URL to fetch and cache.\n * @return {Promise}\n */\n async fetchAndCachePut(input) {\n const response = await this.fetch(input);\n const responseClone = response.clone();\n this.waitUntil(this.cachePut(input, responseClone));\n return response;\n }\n /**\n * Matches a request from the cache (and invokes any applicable plugin\n * callback methods) using the `cacheName`, `matchOptions`, and `plugins`\n * defined on the strategy object.\n *\n * The following plugin lifecycle methods are invoked when using this method:\n * - cacheKeyWillByUsed()\n * - cachedResponseWillByUsed()\n *\n * @param {Request|string} key The Request or URL to use as the cache key.\n * @return {Promise} A matching response, if found.\n */\n async cacheMatch(key) {\n const request = toRequest(key);\n let cachedResponse;\n const { cacheName, matchOptions } = this._strategy;\n const effectiveRequest = await this.getCacheKey(request, 'read');\n const multiMatchOptions = { ...matchOptions, ...{ cacheName } };\n cachedResponse = await caches.match(effectiveRequest, multiMatchOptions);\n if (process.env.NODE_ENV !== 'production') {\n if (cachedResponse) {\n logger.debug(`Found a cached response in '${cacheName}'.`);\n }\n else {\n logger.debug(`No cached response found in '${cacheName}'.`);\n }\n }\n for (const callback of this.iterateCallbacks('cachedResponseWillBeUsed')) {\n cachedResponse = (await callback({\n cacheName,\n matchOptions,\n cachedResponse,\n request: effectiveRequest,\n event: this.event,\n })) || undefined;\n }\n return cachedResponse;\n }\n /**\n * Puts a request/response pair in the cache (and invokes any applicable\n * plugin callback methods) using the `cacheName` and `plugins` defined on\n * the strategy object.\n *\n * The following plugin lifecycle methods are invoked when using this method:\n * - cacheKeyWillByUsed()\n * - cacheWillUpdate()\n * - cacheDidUpdate()\n *\n * @param {Request|string} key The request or URL to use as the cache key.\n * @param {Response} response The response to cache.\n * @return {Promise} `false` if a cacheWillUpdate caused the response\n * not be cached, and `true` otherwise.\n */\n async cachePut(key, response) {\n const request = toRequest(key);\n // Run in the next task to avoid blocking other cache reads.\n // https://github.com/w3c/ServiceWorker/issues/1397\n await timeout(0);\n const effectiveRequest = await this.getCacheKey(request, 'write');\n if (process.env.NODE_ENV !== 'production') {\n if (effectiveRequest.method && effectiveRequest.method !== 'GET') {\n throw new WorkboxError('attempt-to-cache-non-get-request', {\n url: getFriendlyURL(effectiveRequest.url),\n method: effectiveRequest.method,\n });\n }\n }\n if (!response) {\n if (process.env.NODE_ENV !== 'production') {\n logger.error(`Cannot cache non-existent response for ` +\n `'${getFriendlyURL(effectiveRequest.url)}'.`);\n }\n throw new WorkboxError('cache-put-with-no-response', {\n url: getFriendlyURL(effectiveRequest.url),\n });\n }\n const responseToCache = await this._ensureResponseSafeToCache(response);\n if (!responseToCache) {\n if (process.env.NODE_ENV !== 'production') {\n logger.debug(`Response '${getFriendlyURL(effectiveRequest.url)}' ` +\n `will not be cached.`, responseToCache);\n }\n return false;\n }\n const { cacheName, matchOptions } = this._strategy;\n const cache = await self.caches.open(cacheName);\n const hasCacheUpdateCallback = this.hasCallback('cacheDidUpdate');\n const oldResponse = hasCacheUpdateCallback ? await cacheMatchIgnoreParams(\n // TODO(philipwalton): the `__WB_REVISION__` param is a precaching\n // feature. Consider into ways to only add this behavior if using\n // precaching.\n cache, effectiveRequest.clone(), ['__WB_REVISION__'], matchOptions) :\n null;\n if (process.env.NODE_ENV !== 'production') {\n logger.debug(`Updating the '${cacheName}' cache with a new Response ` +\n `for ${getFriendlyURL(effectiveRequest.url)}.`);\n }\n try {\n await cache.put(effectiveRequest, hasCacheUpdateCallback ?\n responseToCache.clone() : responseToCache);\n }\n catch (error) {\n // See https://developer.mozilla.org/en-US/docs/Web/API/DOMException#exception-QuotaExceededError\n if (error.name === 'QuotaExceededError') {\n await executeQuotaErrorCallbacks();\n }\n throw error;\n }\n for (const callback of this.iterateCallbacks('cacheDidUpdate')) {\n await callback({\n cacheName,\n oldResponse,\n newResponse: responseToCache.clone(),\n request: effectiveRequest,\n event: this.event,\n });\n }\n return true;\n }\n /**\n * Checks the list of plugins for the `cacheKeyWillBeUsed` callback, and\n * executes any of those callbacks found in sequence. The final `Request`\n * object returned by the last plugin is treated as the cache key for cache\n * reads and/or writes. If no `cacheKeyWillBeUsed` plugin callbacks have\n * been registered, the passed request is returned unmodified\n *\n * @param {Request} request\n * @param {string} mode\n * @return {Promise}\n */\n async getCacheKey(request, mode) {\n if (!this._cacheKeys[mode]) {\n let effectiveRequest = request;\n for (const callback of this.iterateCallbacks('cacheKeyWillBeUsed')) {\n effectiveRequest = toRequest(await callback({\n mode,\n request: effectiveRequest,\n event: this.event,\n params: this.params,\n }));\n }\n this._cacheKeys[mode] = effectiveRequest;\n }\n return this._cacheKeys[mode];\n }\n /**\n * Returns true if the strategy has at least one plugin with the given\n * callback.\n *\n * @param {string} name The name of the callback to check for.\n * @return {boolean}\n */\n hasCallback(name) {\n for (const plugin of this._strategy.plugins) {\n if (name in plugin) {\n return true;\n }\n }\n return false;\n }\n /**\n * Runs all plugin callbacks matching the given name, in order, passing the\n * given param object (merged ith the current plugin state) as the only\n * argument.\n *\n * Note: since this method runs all plugins, it's not suitable for cases\n * where the return value of a callback needs to be applied prior to calling\n * the next callback. See\n * [`iterateCallbacks()`]{@link module:workbox-strategies.StrategyHandler#iterateCallbacks}\n * below for how to handle that case.\n *\n * @param {string} name The name of the callback to run within each plugin.\n * @param {Object} param The object to pass as the first (and only) param\n * when executing each callback. This object will be merged with the\n * current plugin state prior to callback execution.\n */\n async runCallbacks(name, param) {\n for (const callback of this.iterateCallbacks(name)) {\n // TODO(philipwalton): not sure why `any` is needed. It seems like\n // this should work with `as WorkboxPluginCallbackParam[C]`.\n await callback(param);\n }\n }\n /**\n * Accepts a callback and returns an iterable of matching plugin callbacks,\n * where each callback is wrapped with the current handler state (i.e. when\n * you call each callback, whatever object parameter you pass it will\n * be merged with the plugin's current state).\n *\n * @param {string} name The name fo the callback to run\n * @return {Array}\n */\n *iterateCallbacks(name) {\n for (const plugin of this._strategy.plugins) {\n if (typeof plugin[name] === 'function') {\n const state = this._pluginStateMap.get(plugin);\n const statefulCallback = (param) => {\n const statefulParam = { ...param, state };\n // TODO(philipwalton): not sure why `any` is needed. It seems like\n // this should work with `as WorkboxPluginCallbackParam[C]`.\n return plugin[name](statefulParam);\n };\n yield statefulCallback;\n }\n }\n }\n /**\n * Adds a promise to the\n * [extend lifetime promises]{@link https://w3c.github.io/ServiceWorker/#extendableevent-extend-lifetime-promises}\n * of the event event associated with the request being handled (usually a\n * `FetchEvent`).\n *\n * Note: you can await\n * [`doneWaiting()`]{@link module:workbox-strategies.StrategyHandler~doneWaiting}\n * to know when all added promises have settled.\n *\n * @param {Promise} promise A promise to add to the extend lifetime promises\n * of the event that triggered the request.\n */\n waitUntil(promise) {\n this._extendLifetimePromises.push(promise);\n return promise;\n }\n /**\n * Returns a promise that resolves once all promises passed to\n * [`waitUntil()`]{@link module:workbox-strategies.StrategyHandler~waitUntil}\n * have settled.\n *\n * Note: any work done after `doneWaiting()` settles should be manually\n * passed to an event's `waitUntil()` method (not this handler's\n * `waitUntil()` method), otherwise the service worker thread my be killed\n * prior to your work completing.\n */\n async doneWaiting() {\n let promise;\n while (promise = this._extendLifetimePromises.shift()) {\n await promise;\n }\n }\n /**\n * Stops running the strategy and immediately resolves any pending\n * `waitUntil()` promises.\n */\n destroy() {\n this._handlerDeferred.resolve();\n }\n /**\n * This method will call cacheWillUpdate on the available plugins (or use\n * status === 200) to determine if the Response is safe and valid to cache.\n *\n * @param {Request} options.request\n * @param {Response} options.response\n * @return {Promise}\n *\n * @private\n */\n async _ensureResponseSafeToCache(response) {\n let responseToCache = response;\n let pluginsUsed = false;\n for (const callback of this.iterateCallbacks('cacheWillUpdate')) {\n responseToCache = (await callback({\n request: this.request,\n response: responseToCache,\n event: this.event,\n })) || undefined;\n pluginsUsed = true;\n if (!responseToCache) {\n break;\n }\n }\n if (!pluginsUsed) {\n if (responseToCache && responseToCache.status !== 200) {\n responseToCache = undefined;\n }\n if (process.env.NODE_ENV !== 'production') {\n if (responseToCache) {\n if (responseToCache.status !== 200) {\n if (responseToCache.status === 0) {\n logger.warn(`The response for '${this.request.url}' ` +\n `is an opaque response. The caching strategy that you're ` +\n `using will not cache opaque responses by default.`);\n }\n else {\n logger.debug(`The response for '${this.request.url}' ` +\n `returned a status code of '${response.status}' and won't ` +\n `be cached as a result.`);\n }\n }\n }\n }\n }\n return responseToCache;\n }\n}\nexport { StrategyHandler };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\nconst getFriendlyURL = (url) => {\n const urlObj = new URL(String(url), location.href);\n // See https://github.com/GoogleChrome/workbox/issues/2323\n // We want to include everything, except for the origin if it's same-origin.\n return urlObj.href.replace(new RegExp(`^${location.origin}`), '');\n};\nexport { getFriendlyURL };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { logger } from '../_private/logger.js';\nimport { quotaErrorCallbacks } from '../models/quotaErrorCallbacks.js';\nimport '../_version.js';\n/**\n * Runs all of the callback functions, one at a time sequentially, in the order\n * in which they were registered.\n *\n * @memberof module:workbox-core\n * @private\n */\nasync function executeQuotaErrorCallbacks() {\n if (process.env.NODE_ENV !== 'production') {\n logger.log(`About to run ${quotaErrorCallbacks.size} ` +\n `callbacks to clean up caches.`);\n }\n for (const callback of quotaErrorCallbacks) {\n await callback();\n if (process.env.NODE_ENV !== 'production') {\n logger.log(callback, 'is complete.');\n }\n }\n if (process.env.NODE_ENV !== 'production') {\n logger.log('Finished running callbacks.');\n }\n}\nexport { executeQuotaErrorCallbacks };\n","/*\n Copyright 2020 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { cacheNames } from 'workbox-core/_private/cacheNames.js';\nimport { WorkboxError } from 'workbox-core/_private/WorkboxError.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { getFriendlyURL } from 'workbox-core/_private/getFriendlyURL.js';\nimport { StrategyHandler } from './StrategyHandler.js';\nimport './_version.js';\n/**\n * An abstract base class that all other strategy classes must extend from:\n *\n * @memberof module:workbox-strategies\n */\nclass Strategy {\n /**\n * Creates a new instance of the strategy and sets all documented option\n * properties as public instance properties.\n *\n * Note: if a custom strategy class extends the base Strategy class and does\n * not need more than these properties, it does not need to define its own\n * constructor.\n *\n * @param {Object} [options]\n * @param {string} [options.cacheName] Cache name to store and retrieve\n * requests. Defaults to the cache names provided by\n * [workbox-core]{@link module:workbox-core.cacheNames}.\n * @param {Array} [options.plugins] [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}\n * to use in conjunction with this caching strategy.\n * @param {Object} [options.fetchOptions] Values passed along to the\n * [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters)\n * of [non-navigation](https://github.com/GoogleChrome/workbox/issues/1796)\n * `fetch()` requests made by this strategy.\n * @param {Object} [options.matchOptions] The\n * [`CacheQueryOptions`]{@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions}\n * for any `cache.match()` or `cache.put()` calls made by this strategy.\n */\n constructor(options = {}) {\n /**\n * Cache name to store and retrieve\n * requests. Defaults to the cache names provided by\n * [workbox-core]{@link module:workbox-core.cacheNames}.\n *\n * @type {string}\n */\n this.cacheName = cacheNames.getRuntimeName(options.cacheName);\n /**\n * The list\n * [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}\n * used by this strategy.\n *\n * @type {Array}\n */\n this.plugins = options.plugins || [];\n /**\n * Values passed along to the\n * [`init`]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters}\n * of all fetch() requests made by this strategy.\n *\n * @type {Object}\n */\n this.fetchOptions = options.fetchOptions;\n /**\n * The\n * [`CacheQueryOptions`]{@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions}\n * for any `cache.match()` or `cache.put()` calls made by this strategy.\n *\n * @type {Object}\n */\n this.matchOptions = options.matchOptions;\n }\n /**\n * Perform a request strategy and returns a `Promise` that will resolve with\n * a `Response`, invoking all relevant plugin callbacks.\n *\n * When a strategy instance is registered with a Workbox\n * [route]{@link module:workbox-routing.Route}, this method is automatically\n * called when the route matches.\n *\n * Alternatively, this method can be used in a standalone `FetchEvent`\n * listener by passing it to `event.respondWith()`.\n *\n * @param {FetchEvent|Object} options A `FetchEvent` or an object with the\n * properties listed below.\n * @param {Request|string} options.request A request to run this strategy for.\n * @param {ExtendableEvent} options.event The event associated with the\n * request.\n * @param {URL} [options.url]\n * @param {*} [options.params]\n */\n handle(options) {\n const [responseDone] = this.handleAll(options);\n return responseDone;\n }\n /**\n * Similar to [`handle()`]{@link module:workbox-strategies.Strategy~handle}, but\n * instead of just returning a `Promise` that resolves to a `Response` it\n * it will return an tuple of [response, done] promises, where the former\n * (`response`) is equivalent to what `handle()` returns, and the latter is a\n * Promise that will resolve once any promises that were added to\n * `event.waitUntil()` as part of performing the strategy have completed.\n *\n * You can await the `done` promise to ensure any extra work performed by\n * the strategy (usually caching responses) completes successfully.\n *\n * @param {FetchEvent|Object} options A `FetchEvent` or an object with the\n * properties listed below.\n * @param {Request|string} options.request A request to run this strategy for.\n * @param {ExtendableEvent} options.event The event associated with the\n * request.\n * @param {URL} [options.url]\n * @param {*} [options.params]\n * @return {Array} A tuple of [response, done]\n * promises that can be used to determine when the response resolves as\n * well as when the handler has completed all its work.\n */\n handleAll(options) {\n // Allow for flexible options to be passed.\n if (options instanceof FetchEvent) {\n options = {\n event: options,\n request: options.request,\n };\n }\n const event = options.event;\n const request = typeof options.request === 'string' ?\n new Request(options.request) :\n options.request;\n const params = 'params' in options ? options.params : undefined;\n const handler = new StrategyHandler(this, { event, request, params });\n const responseDone = this._getResponse(handler, request, event);\n const handlerDone = this._awaitComplete(responseDone, handler, request, event);\n // Return an array of promises, suitable for use with Promise.all().\n return [responseDone, handlerDone];\n }\n async _getResponse(handler, request, event) {\n await handler.runCallbacks('handlerWillStart', { event, request });\n let response = undefined;\n try {\n response = await this._handle(request, handler);\n // The \"official\" Strategy subclasses all throw this error automatically,\n // but in case a third-party Strategy doesn't, ensure that we have a\n // consistent failure when there's no response or an error response.\n if (!response || response.type === 'error') {\n throw new WorkboxError('no-response', { url: request.url });\n }\n }\n catch (error) {\n for (const callback of handler.iterateCallbacks('handlerDidError')) {\n response = await callback({ error, event, request });\n if (response) {\n break;\n }\n }\n if (!response) {\n throw error;\n }\n else if (process.env.NODE_ENV !== 'production') {\n logger.log(`While responding to '${getFriendlyURL(request.url)}', ` +\n `an ${error} error occurred. Using a fallback response provided by ` +\n `a handlerDidError plugin.`);\n }\n }\n for (const callback of handler.iterateCallbacks('handlerWillRespond')) {\n response = await callback({ event, request, response });\n }\n return response;\n }\n async _awaitComplete(responseDone, handler, request, event) {\n let response;\n let error;\n try {\n response = await responseDone;\n }\n catch (error) {\n // Ignore errors, as response errors should be caught via the `response`\n // promise above. The `done` promise will only throw for errors in\n // promises passed to `handler.waitUntil()`.\n }\n try {\n await handler.runCallbacks('handlerDidRespond', {\n event,\n request,\n response,\n });\n await handler.doneWaiting();\n }\n catch (waitUntilError) {\n error = waitUntilError;\n }\n await handler.runCallbacks('handlerDidComplete', {\n event,\n request,\n response,\n error,\n });\n handler.destroy();\n if (error) {\n throw error;\n }\n }\n}\nexport { Strategy };\n/**\n * Classes extending the `Strategy` based class should implement this method,\n * and leverage the [`handler`]{@link module:workbox-strategies.StrategyHandler}\n * arg to perform all fetching and cache logic, which will ensure all relevant\n * cache, cache options, fetch options and plugins are used (per the current\n * strategy instance).\n *\n * @name _handle\n * @instance\n * @abstract\n * @function\n * @param {Request} request\n * @param {module:workbox-strategies.StrategyHandler} handler\n * @return {Promise}\n *\n * @memberof module:workbox-strategies.Strategy\n */\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { assert } from 'workbox-core/_private/assert.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { WorkboxError } from 'workbox-core/_private/WorkboxError.js';\nimport { cacheOkAndOpaquePlugin } from './plugins/cacheOkAndOpaquePlugin.js';\nimport { Strategy } from './Strategy.js';\nimport { messages } from './utils/messages.js';\nimport './_version.js';\n/**\n * An implementation of a\n * [network first]{@link https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#network-falling-back-to-cache}\n * request strategy.\n *\n * By default, this strategy will cache responses with a 200 status code as\n * well as [opaque responses]{@link https://developers.google.com/web/tools/workbox/guides/handle-third-party-requests}.\n * Opaque responses are are cross-origin requests where the response doesn't\n * support [CORS]{@link https://enable-cors.org/}.\n *\n * If the network request fails, and there is no cache match, this will throw\n * a `WorkboxError` exception.\n *\n * @extends module:workbox-strategies.Strategy\n * @memberof module:workbox-strategies\n */\nclass NetworkFirst extends Strategy {\n /**\n * @param {Object} [options]\n * @param {string} [options.cacheName] Cache name to store and retrieve\n * requests. Defaults to cache names provided by\n * [workbox-core]{@link module:workbox-core.cacheNames}.\n * @param {Array} [options.plugins] [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}\n * to use in conjunction with this caching strategy.\n * @param {Object} [options.fetchOptions] Values passed along to the\n * [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters)\n * of [non-navigation](https://github.com/GoogleChrome/workbox/issues/1796)\n * `fetch()` requests made by this strategy.\n * @param {Object} [options.matchOptions] [`CacheQueryOptions`](https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions)\n * @param {number} [options.networkTimeoutSeconds] If set, any network requests\n * that fail to respond within the timeout will fallback to the cache.\n *\n * This option can be used to combat\n * \"[lie-fi]{@link https://developers.google.com/web/fundamentals/performance/poor-connectivity/#lie-fi}\"\n * scenarios.\n */\n constructor(options = {}) {\n super(options);\n // If this instance contains no plugins with a 'cacheWillUpdate' callback,\n // prepend the `cacheOkAndOpaquePlugin` plugin to the plugins list.\n if (!this.plugins.some((p) => 'cacheWillUpdate' in p)) {\n this.plugins.unshift(cacheOkAndOpaquePlugin);\n }\n this._networkTimeoutSeconds = options.networkTimeoutSeconds || 0;\n if (process.env.NODE_ENV !== 'production') {\n if (this._networkTimeoutSeconds) {\n assert.isType(this._networkTimeoutSeconds, 'number', {\n moduleName: 'workbox-strategies',\n className: this.constructor.name,\n funcName: 'constructor',\n paramName: 'networkTimeoutSeconds',\n });\n }\n }\n }\n /**\n * @private\n * @param {Request|string} request A request to run this strategy for.\n * @param {module:workbox-strategies.StrategyHandler} handler The event that\n * triggered the request.\n * @return {Promise}\n */\n async _handle(request, handler) {\n const logs = [];\n if (process.env.NODE_ENV !== 'production') {\n assert.isInstance(request, Request, {\n moduleName: 'workbox-strategies',\n className: this.constructor.name,\n funcName: 'handle',\n paramName: 'makeRequest',\n });\n }\n const promises = [];\n let timeoutId;\n if (this._networkTimeoutSeconds) {\n const { id, promise } = this._getTimeoutPromise({ request, logs, handler });\n timeoutId = id;\n promises.push(promise);\n }\n const networkPromise = this._getNetworkPromise({ timeoutId, request, logs, handler });\n promises.push(networkPromise);\n const response = await handler.waitUntil((async () => {\n // Promise.race() will resolve as soon as the first promise resolves.\n return await handler.waitUntil(Promise.race(promises)) ||\n // If Promise.race() resolved with null, it might be due to a network\n // timeout + a cache miss. If that were to happen, we'd rather wait until\n // the networkPromise resolves instead of returning null.\n // Note that it's fine to await an already-resolved promise, so we don't\n // have to check to see if it's still \"in flight\".\n await networkPromise;\n })());\n if (process.env.NODE_ENV !== 'production') {\n logger.groupCollapsed(messages.strategyStart(this.constructor.name, request));\n for (const log of logs) {\n logger.log(log);\n }\n messages.printFinalResponse(response);\n logger.groupEnd();\n }\n if (!response) {\n throw new WorkboxError('no-response', { url: request.url });\n }\n return response;\n }\n /**\n * @param {Object} options\n * @param {Request} options.request\n * @param {Array} options.logs A reference to the logs array\n * @param {Event} options.event\n * @return {Promise}\n *\n * @private\n */\n _getTimeoutPromise({ request, logs, handler }) {\n let timeoutId;\n const timeoutPromise = new Promise((resolve) => {\n const onNetworkTimeout = async () => {\n if (process.env.NODE_ENV !== 'production') {\n logs.push(`Timing out the network response at ` +\n `${this._networkTimeoutSeconds} seconds.`);\n }\n resolve(await handler.cacheMatch(request));\n };\n timeoutId = setTimeout(onNetworkTimeout, this._networkTimeoutSeconds * 1000);\n });\n return {\n promise: timeoutPromise,\n id: timeoutId,\n };\n }\n /**\n * @param {Object} options\n * @param {number|undefined} options.timeoutId\n * @param {Request} options.request\n * @param {Array} options.logs A reference to the logs Array.\n * @param {Event} options.event\n * @return {Promise}\n *\n * @private\n */\n async _getNetworkPromise({ timeoutId, request, logs, handler }) {\n let error;\n let response;\n try {\n response = await handler.fetchAndCachePut(request);\n }\n catch (fetchError) {\n error = fetchError;\n }\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n if (process.env.NODE_ENV !== 'production') {\n if (response) {\n logs.push(`Got response from network.`);\n }\n else {\n logs.push(`Unable to get a response from the network. Will respond ` +\n `with a cached response.`);\n }\n }\n if (error || !response) {\n response = await handler.cacheMatch(request);\n if (process.env.NODE_ENV !== 'production') {\n if (response) {\n logs.push(`Found a cached response in the '${this.cacheName}'` +\n ` cache.`);\n }\n else {\n logs.push(`No response found in the '${this.cacheName}' cache.`);\n }\n }\n }\n return response;\n }\n}\nexport { NetworkFirst };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { assert } from 'workbox-core/_private/assert.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { timeout } from 'workbox-core/_private/timeout.js';\nimport { WorkboxError } from 'workbox-core/_private/WorkboxError.js';\nimport { Strategy } from './Strategy.js';\nimport { messages } from './utils/messages.js';\nimport './_version.js';\n/**\n * An implementation of a\n * [network-only]{@link https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#network-only}\n * request strategy.\n *\n * This class is useful if you want to take advantage of any\n * [Workbox plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}.\n *\n * If the network request fails, this will throw a `WorkboxError` exception.\n *\n * @extends module:workbox-strategies.Strategy\n * @memberof module:workbox-strategies\n */\nclass NetworkOnly extends Strategy {\n /**\n * @param {Object} [options]\n * @param {Array} [options.plugins] [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}\n * to use in conjunction with this caching strategy.\n * @param {Object} [options.fetchOptions] Values passed along to the\n * [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters)\n * of [non-navigation](https://github.com/GoogleChrome/workbox/issues/1796)\n * `fetch()` requests made by this strategy.\n * @param {number} [options.networkTimeoutSeconds] If set, any network requests\n * that fail to respond within the timeout will result in a network error.\n */\n constructor(options = {}) {\n super(options);\n this._networkTimeoutSeconds = options.networkTimeoutSeconds || 0;\n }\n /**\n * @private\n * @param {Request|string} request A request to run this strategy for.\n * @param {module:workbox-strategies.StrategyHandler} handler The event that\n * triggered the request.\n * @return {Promise}\n */\n async _handle(request, handler) {\n if (process.env.NODE_ENV !== 'production') {\n assert.isInstance(request, Request, {\n moduleName: 'workbox-strategies',\n className: this.constructor.name,\n funcName: '_handle',\n paramName: 'request',\n });\n }\n let error = undefined;\n let response;\n try {\n const promises = [handler.fetch(request)];\n if (this._networkTimeoutSeconds) {\n const timeoutPromise = timeout(this._networkTimeoutSeconds * 1000);\n promises.push(timeoutPromise);\n }\n response = await Promise.race(promises);\n if (!response) {\n throw new Error(`Timed out the network response after ` +\n `${this._networkTimeoutSeconds} seconds.`);\n }\n }\n catch (err) {\n error = err;\n }\n if (process.env.NODE_ENV !== 'production') {\n logger.groupCollapsed(messages.strategyStart(this.constructor.name, request));\n if (response) {\n logger.log(`Got response from network.`);\n }\n else {\n logger.log(`Unable to get a response from the network.`);\n }\n messages.printFinalResponse(response);\n logger.groupEnd();\n }\n if (!response) {\n throw new WorkboxError('no-response', { url: request.url, error });\n }\n return response;\n }\n}\nexport { NetworkOnly };\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport './_version.js';\n/**\n * Claim any currently available clients once the service worker\n * becomes active. This is normally used in conjunction with `skipWaiting()`.\n *\n * @memberof module:workbox-core\n */\nfunction clientsClaim() {\n self.addEventListener('activate', () => self.clients.claim());\n}\nexport { clientsClaim };\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { WorkboxError } from 'workbox-core/_private/WorkboxError.js';\nimport { Route } from './Route.js';\nimport { RegExpRoute } from './RegExpRoute.js';\nimport { getOrCreateDefaultRouter } from './utils/getOrCreateDefaultRouter.js';\nimport './_version.js';\n/**\n * Easily register a RegExp, string, or function with a caching\n * strategy to a singleton Router instance.\n *\n * This method will generate a Route for you if needed and\n * call [registerRoute()]{@link module:workbox-routing.Router#registerRoute}.\n *\n * @param {RegExp|string|module:workbox-routing.Route~matchCallback|module:workbox-routing.Route} capture\n * If the capture param is a `Route`, all other arguments will be ignored.\n * @param {module:workbox-routing~handlerCallback} [handler] A callback\n * function that returns a Promise resulting in a Response. This parameter\n * is required if `capture` is not a `Route` object.\n * @param {string} [method='GET'] The HTTP method to match the Route\n * against.\n * @return {module:workbox-routing.Route} The generated `Route`(Useful for\n * unregistering).\n *\n * @memberof module:workbox-routing\n */\nfunction registerRoute(capture, handler, method) {\n let route;\n if (typeof capture === 'string') {\n const captureUrl = new URL(capture, location.href);\n if (process.env.NODE_ENV !== 'production') {\n if (!(capture.startsWith('/') || capture.startsWith('http'))) {\n throw new WorkboxError('invalid-string', {\n moduleName: 'workbox-routing',\n funcName: 'registerRoute',\n paramName: 'capture',\n });\n }\n // We want to check if Express-style wildcards are in the pathname only.\n // TODO: Remove this log message in v4.\n const valueToCheck = capture.startsWith('http') ?\n captureUrl.pathname : capture;\n // See https://github.com/pillarjs/path-to-regexp#parameters\n const wildcards = '[*:?+]';\n if ((new RegExp(`${wildcards}`)).exec(valueToCheck)) {\n logger.debug(`The '$capture' parameter contains an Express-style wildcard ` +\n `character (${wildcards}). Strings are now always interpreted as ` +\n `exact matches; use a RegExp for partial or wildcard matches.`);\n }\n }\n const matchCallback = ({ url }) => {\n if (process.env.NODE_ENV !== 'production') {\n if ((url.pathname === captureUrl.pathname) &&\n (url.origin !== captureUrl.origin)) {\n logger.debug(`${capture} only partially matches the cross-origin URL ` +\n `${url}. This route will only handle cross-origin requests ` +\n `if they match the entire URL.`);\n }\n }\n return url.href === captureUrl.href;\n };\n // If `capture` is a string then `handler` and `method` must be present.\n route = new Route(matchCallback, handler, method);\n }\n else if (capture instanceof RegExp) {\n // If `capture` is a `RegExp` then `handler` and `method` must be present.\n route = new RegExpRoute(capture, handler, method);\n }\n else if (typeof capture === 'function') {\n // If `capture` is a function then `handler` and `method` must be present.\n route = new Route(capture, handler, method);\n }\n else if (capture instanceof Route) {\n route = capture;\n }\n else {\n throw new WorkboxError('unsupported-route-type', {\n moduleName: 'workbox-routing',\n funcName: 'registerRoute',\n paramName: 'capture',\n });\n }\n const defaultRouter = getOrCreateDefaultRouter();\n defaultRouter.registerRoute(route);\n return route;\n}\nexport { registerRoute };\n"],"names":["self","_","e","messageGenerator","code","args","msg","length","JSON","stringify","WorkboxError","Error","constructor","errorCode","details","name","normalizeHandler","handler","handle","Route","match","method","setCatchHandler","catchHandler","RegExpRoute","regExp","url","result","exec","href","origin","location","index","slice","Router","_routes","Map","_defaultHandlerMap","this","addFetchListener","addEventListener","event","request","responsePromise","handleRequest","respondWith","addCacheListener","data","type","payload","requestPromises","Promise","all","urlsToCache","map","entry","Request","waitUntil","ports","then","postMessage","URL","protocol","startsWith","sameOrigin","params","route","findMatchingRoute","has","get","err","reject","_catchHandler","catch","async","catchErr","routes","matchResult","Array","isArray","Object","keys","undefined","setDefaultHandler","set","registerRoute","push","unregisterRoute","routeIndex","indexOf","splice","defaultRouter","getOrCreateDefaultRouter","cacheOkAndOpaquePlugin","cacheWillUpdate","response","status","_cacheNameDetails","googleAnalytics","precache","prefix","runtime","suffix","registration","scope","_createCacheName","cacheName","filter","value","join","cacheNames","userCacheName","stripParams","fullURL","ignoreParams","strippedURL","param","searchParams","delete","Deferred","promise","resolve","quotaErrorCallbacks","Set","timeout","ms","setTimeout","toRequest","input","StrategyHandler","strategy","options","_cacheKeys","assign","_strategy","_handlerDeferred","_extendLifetimePromises","_plugins","plugins","_pluginStateMap","plugin","mode","FetchEvent","preloadResponse","possiblePreloadResponse","originalRequest","hasCallback","clone","cb","iterateCallbacks","thrownError","pluginFilteredRequest","fetchResponse","fetch","fetchOptions","callback","error","runCallbacks","responseClone","cachePut","key","cachedResponse","matchOptions","effectiveRequest","getCacheKey","multiMatchOptions","caches","String","replace","RegExp","responseToCache","_ensureResponseSafeToCache","cache","open","hasCacheUpdateCallback","oldResponse","strippedRequestURL","keysOptions","ignoreSearch","cacheKeys","cacheKey","cacheMatchIgnoreParams","put","executeQuotaErrorCallbacks","newResponse","state","statefulCallback","statefulParam","shift","destroy","pluginsUsed","Strategy","responseDone","handleAll","_getResponse","_awaitComplete","_handle","doneWaiting","waitUntilError","some","p","unshift","_networkTimeoutSeconds","networkTimeoutSeconds","logs","promises","timeoutId","id","_getTimeoutPromise","networkPromise","_getNetworkPromise","race","cacheMatch","fetchAndCachePut","fetchError","clearTimeout","timeoutPromise","clients","claim","capture","captureUrl","moduleName","funcName","paramName"],"mappings":"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"} --------------------------------------------------------------------------------