{"version":3,"sources":["LineChart.module.css","utils.js","Node.js","constants.js","Graph.js","LineChart.js","SimulationSettings.js","simulation/markov.js","simulation/grid.js","simulation/index.js","App.js","serviceWorker.js","index.js","App.module.css","SimulationSettings.module.css","Graph.module.css"],"names":["module","exports","randomSample","array","size","r","temp","length","i","end","swaps","Math","floor","random","push","sample","slice","pop","shuffle","temporaryValue","randomIndex","currentIndex","weightedRandom","spec","sum","prob","value","padding","max","padded","min","distance","source","target","sqrt","pow","x","y","noop","ICONS","house","hospital","temple","station","supermarket","Node","layout","node","width","height","type","venue","onNodeClick","position","id","transform","classes","classNames","styles","susceptible","state","sick","recovered","dead","locked","className","onClick","fontSize","stroke","fill","strokeWidth","Graph","props","handleTick","bind","setCurrent","current","nodes","reduce","prev","acc","this","runForceSimulation","simulation","on","nextProps","tick","updateForceSimulation","alpha","restart","force","links","edges","forceSimulation","forceLink","forceCollide","iterations","radius","updates","map","setState","nodeId","ref","svgRef","style","shapeRendering","index","Component","defaultProps","buildPolyLineString","points","xMultipier","yMultipier","maxXEntries","concat","join","LineChart","xOffset","yOffset","data","maxYPoint","maxYRange","scaleY","labelY","lineChart","color","labelX","SimulationSettings","simulationState","onSettingChange","onRestartButtonClick","container","form","onChange","initialSickAgents","agentsPerHouse","houses","busStations","hospitals","supermarkets","temples","footer","STAY","BASE","SIR_TRANSITION_STATE","DISEASE_SPREAD_TRANSITION","getNextMarkovStateForAgent","agent","transitionMap","location","split","agentLocation","applyFixedNodeGrid","gridSize","nodesToAlign","filter","count","row","col","fx","fy","VENUES","name","members","isRoot","VENUE_TRANSITIONS","getInitialGraph","forEach","alignment","nodeIndex","venueId","j","agentId","base","sickAgents","nextSimulationTick","find","nextMarkovState","moveAgent","fellow","applySIRModel","targetNode","sourceNode","edge","INITIAL_SIMULATION_STATE","INITIAL_GRAPH","App","useState","setSimulationState","setNodes","setEdges","historicalSickCount","setHistoricalSickCount","historicalRecoveredCount","setHistoricalRecoveredCount","historicalDeadCount","setHistoricalDeadCount","loading","setLoading","graphRef","useRef","callback","delay","savedCallback","useEffect","setInterval","clearInterval","useInterval","header","samples","sampleSusceptible","sampleInfected","sampleRecovered","round","section","stats","population","simulationSettings","simulationInfo","key","event","Boolean","window","hostname","match","ReactDOM","render","StrictMode","document","getElementById","navigator","serviceWorker","ready","then","registration","unregister","catch","error","console","message"],"mappings":"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","file":"static/js/main.e0b65bcc.chunk.js","sourcesContent":["// extracted by mini-css-extract-plugin\nmodule.exports = {\"lineChart\":\"LineChart_lineChart__kGPs0\",\"labelY\":\"LineChart_labelY__1qHyC\",\"labelX\":\"LineChart_labelX__nzAeo\"};","import { COLORS as colors } from './constants';\nimport React, { useState, useEffect, useRef } from 'react';\n\nexport function useInterval(callback, delay) {\n // https://overreacted.io/making-setinterval-declarative-with-react-hooks/\n // danke, dan\n const savedCallback = useRef();\n\n // Remember the latest callback.\n useEffect(() => {\n savedCallback.current = callback;\n }, [callback]);\n\n // Set up the interval.\n useEffect(() => {\n function tick() {\n savedCallback.current();\n }\n if (delay !== null) {\n let id = setInterval(tick, delay);\n return () => clearInterval(id);\n }\n }, [delay]);\n}\n\nfunction getRandom(length) { return Math.floor(Math.random() * length); }\n\nexport function randomSample(array, size) {\n // https://stackoverflow.com/a/37835673\n var r, i = array.length, end = i - size, temp, swaps = randomSample.swaps;\n\n while (i-- > end) {\n r = getRandom(i + 1);\n temp = array[r];\n array[r] = array[i];\n array[i] = temp;\n swaps.push(i);\n swaps.push(r);\n }\n\n var sample = array.slice(end);\n\n while(size--) {\n i = swaps.pop();\n r = swaps.pop();\n temp = array[i];\n array[i] = array[r];\n array[r] = temp;\n }\n\n return sample;\n}\nrandomSample.swaps = [];\n\nexport function randomChoice(array) {\n return array[Math.floor(Math.random() * array.length)]\n}\n\nexport function shuffle(array) {\n // https://stackoverflow.com/a/2450976\n var currentIndex = array.length, temporaryValue, randomIndex;\n\n // While there remain elements to shuffle...\n while (0 !== currentIndex) {\n\n // Pick a remaining element...\n randomIndex = Math.floor(Math.random() * currentIndex);\n currentIndex -= 1;\n\n // And swap it with the current element.\n temporaryValue = array[currentIndex];\n array[currentIndex] = array[randomIndex];\n array[randomIndex] = temporaryValue;\n }\n\n return array;\n}\n\nexport function weightedRandom(spec) {\n const random = Math.random();\n let sum = .0; \n\n for (var i in spec) {\n const [prob, value] = spec[i];\n sum += prob;\n if (random <= sum) {\n return value;\n };\n }\n};\n\nexport function rangeBetween(min, max) {\n return Math.random() * (max - min) + min;\n}\n\nexport function shade(value) {\n let sum = 0;\n\n for (var i in colors) {\n const color = colors[i];\n if (value <= sum) {\n return color;\n };\n\n sum += 1 / colors.length;\n }\n\n return colors[colors.length - 1];\n}\n\nexport function padding(value, max, padded) {\n return Math.min(Math.max(padded, value), max - 50);\n}\n\nexport function distance(source, target) {\n return Math.sqrt(Math.pow(target.x - source.x, 2) + Math.pow(target.y - source.y, 2))\n}\n","import React, { Component } from \"react\";\nimport classNames from \"classnames\";\n\nimport styles from \"./Graph.module.css\";\nimport { padding, shade } from \"./utils\";\nimport { VENUE, SUSCEPTIBLE, SICK, RECOVERED, DEAD } from \"./constants\";\n\nfunction noop() {}\n\nconst ICONS = {\n house: \"🏠\",\n hospital: \"🏥\",\n temple: \"🕍\",\n station: \"🚌\",\n supermarket: \"🛒\"\n};\n\nexport default function Node({\n layout,\n node,\n width,\n height,\n type,\n venue,\n onNodeClick = noop\n}) {\n const position = layout[node.id];\n const transform = `translate(\n ${padding(position.x, width, 7)},\n ${padding(position.y, height, 30)}\n )`;\n\n const classes = classNames({\n [styles.node]: true,\n [styles[type]]: true,\n [styles[venue]]: true,\n [styles.susceptible]: node.state === SUSCEPTIBLE,\n [styles.sick]: node.state === SICK,\n [styles.recovered]: node.state === RECOVERED,\n [styles.dead]: node.state === DEAD,\n [styles.locked]: node.locked\n });\n\n return (\n \n {node.type === VENUE ? (\n \n {ICONS[node.venue]}\n \n ) : node.state === 3 ? (\n \n 💀\n \n ) : (\n \n )}\n {false && node.type === VENUE && (\n \n {node.venue.toUpperCase()}\n \n )}\n {node.locked && (\n \n )}\n \n );\n}\n","const VENUE = 'venue';\nconst AGENT = 'agent';\n\nconst SUSCEPTIBLE = 0;\nconst SICK = 1;\nconst RECOVERED = 2;\nconst DEAD = 3;\n\nconst COLORS = ['#ECA6E1','#C28CBE','#9B729C','#76597B','#55415B','#362A3C'];\nconst FPS = 400;\n\nexport {\n SUSCEPTIBLE, SICK, RECOVERED, DEAD,\n VENUE, AGENT,\n COLORS,\n FPS,\n};\n","import React, { Component } from \"react\";\n\nimport {\n forceSimulation,\n forceLink,\n forceCollide\n} from \"d3-force\";\n\nimport Node from \"./Node\";\n\nexport default class Graph extends Component {\n constructor(props) {\n super(props);\n\n this.handleTick = this.handleTick.bind(this);\n this.setCurrent = this.setCurrent.bind(this);\n\n this.state = {\n current: null,\n layout: props.nodes.reduce(\n (prev, acc) => (\n (prev[acc.id] = {\n x: 0,\n y: 0\n }),\n prev\n ),\n {}\n )\n };\n }\n\n componentDidMount() {}\n\n componentWillMount() {\n this.runForceSimulation();\n\n this.simulation.on(\"tick\", this.handleTick);\n }\n\n componentWillUnmount() {\n this.simulation.on(\"tick\", null);\n }\n\n componentWillReceiveProps(nextProps) {\n if (this.props.tick !== nextProps.tick) {\n this.updateForceSimulation();\n }\n }\n\n updateForceSimulation() {\n this.simulation.alpha(0.2).restart();\n this.simulation.nodes(this.props.nodes);\n this.simulation.force(\"link\").links(this.props.edges);\n }\n\n shouldComponentUpdate(props, nextProps) {\n return props.tick !== nextProps.tick;\n }\n\n runForceSimulation() {\n const { nodes, edges } = this.props;\n\n const simulation = (this.simulation = forceSimulation(nodes)\n .force(\n \"link\",\n forceLink().id(node => node.id)\n )\n .force(\n \"collide\",\n forceCollide(() => 1)\n .iterations(1)\n .radius(9)\n ));\n\n simulation\n .force(\"link\")\n .links(edges)\n .distance(25);\n }\n\n handleTick() {\n const { simulation } = this;\n const { layout } = this.state;\n let updates = {};\n\n simulation.nodes().map(node => {\n updates[node.id] = node;\n });\n\n this.setState({\n layout: {\n ...layout,\n ...updates\n }\n });\n }\n\n setCurrent(nodeId) {\n return () => {\n this.setState({\n current: nodeId\n });\n };\n }\n\n render() {\n const { nodes, edges, width, height, onNodeClick } = this.props;\n const { layout, current } = this.state;\n return (\n (this.svgRef = ref)}\n height={height}\n style={{\n shapeRendering: \"geometricPrecision\"\n }}\n >\n {nodes.map((node, index) => (\n \n ))}\n \n );\n }\n}\n\nGraph.defaultProps = {\n width: 900,\n height: 600,\n nodes: [],\n edges: []\n};\n","import React from \"react\";\n\nimport styles from \"./LineChart.module.css\";\n\nfunction buildPolyLineString(points, xMultipier, yMultipier, maxXEntries) {\n return points\n .slice(Math.max(points.length - maxXEntries, 0))\n .reduce((acc, current, i) => {\n return acc.concat(`${i * xMultipier}, ${-(current * yMultipier)}`);\n }, [])\n .join(\" \");\n}\n\nexport default function LineChart({\n width,\n height,\n xOffset = 10,\n yOffset = 17,\n data = [],\n maxXEntries = 100\n}) {\n const maxYPoint = height - 50;\n const maxYRange = Math.max(\n maxYPoint,\n ...data.reduce((prev, { points }) => prev.concat(points), [])\n );\n\n const yMultipier = 1;\n const xMultipier = 2.5;\n const scaleY = maxYPoint / maxYRange;\n\n return (\n <>\n population →\n \n \n {data.map(({ points, color }, i) => (\n \n \n \n ))}\n \n \n days since the first case →\n \n );\n}\n","import React from 'react';\n\nimport styles from './SimulationSettings.module.css';\n\nexport default function SimulationSettings({\n simulationState,\n onSettingChange,\n onRestartButtonClick,\n}) {\n return (\n
\n
\n \n \n \n \n \n \n \n
\n\n
\n \n
\n
\n );\n}\n","import {\n SUSCEPTIBLE, SICK, RECOVERED, DEAD,\n} from '../constants';\n\nimport { randomChoice, weightedRandom } from '../utils';\n\nexport const STAY = 'stay';\nexport const BASE = 'base';\n\nconst SIR_TRANSITION_STATE = {\n [SUSCEPTIBLE]: [\n [1, SUSCEPTIBLE],\n ],\n [RECOVERED]: [\n [1, RECOVERED],\n ],\n [SICK]: [\n [0.995, SICK],\n [0.004, RECOVERED],\n [0.001, DEAD],\n ],\n [DEAD]: [\n [1, DEAD],\n ],\n};\n\nconst DISEASE_SPREAD_TRANSITION = {\n [SUSCEPTIBLE]: [\n [0.3, SICK],\n [0.7, SUSCEPTIBLE],\n ],\n [RECOVERED]: [\n [1, RECOVERED],\n ],\n [SICK]: [\n [1, SICK],\n ],\n [DEAD]: [\n [1, DEAD],\n ],\n};\n\nexport function getNextMarkovStateForAgent(agent, transitionMap) {\n const [agentLocation] = agent.location.split('-');\n\n if (agentLocation === 'house' && Math.random() < 0.9) {\n return STAY;\n }\n\n const map = transitionMap[agentLocation];\n\n return randomChoice(map);\n}\n\nexport function applySIRModel(nodes, edges) {\n for (const node of nodes) {\n if (node.type !== 'agent') {\n continue;\n }\n\n const location = nodes.find(({ id }) => node.location === id);\n const fellows = edges\n .filter(({ target }) => target.id === location.id)\n .map(({ source }) => source);\n\n fellows.forEach(\n (fellow) => {\n if (fellow.id === node.id) {\n return;\n }\n\n if (node.state === SICK) {\n fellow.state = weightedRandom(DISEASE_SPREAD_TRANSITION[fellow.state]);\n }\n\n fellow.state = weightedRandom(SIR_TRANSITION_STATE[fellow.state]);\n }\n )\n }\n}\n","import { shuffle } from '../utils';\n\nexport function applyFixedNodeGrid(nodes, height=600) {\n shuffle(nodes);\n\n const gridSize = 100;\n const nodesToAlign = nodes.filter(({ type }) => type === 'venue');\n const count = nodesToAlign.length;\n \n for (var i = 0; i < count; i++) {\n const node = nodesToAlign[i];\n\n const row = Math.floor(i / (height / gridSize)) + 1;\n const col = Math.floor(i % ((height) / gridSize)) + 1;\n \n const fx = row * gridSize;\n const fy = col * gridSize;\n\n node.fx = fx;\n node.fy = fy;\n }\n\n return nodes;\n}\n","import { randomSample, distance } from '../utils';\n\nimport { AGENT, SUSCEPTIBLE, SICK, DEAD } from '../constants';\n\nimport { getNextMarkovStateForAgent, STAY, BASE, applySIRModel } from './markov';\nimport { applyFixedNodeGrid } from './grid';\n\nconst VENUES = [\n {\n name: 'house',\n members: simulationState => simulationState.agentsPerHouse,\n isRoot: true,\n count: simulationState => simulationState.houses,\n },\n {\n name: 'temple',\n count: simulationState => simulationState.temples,\n },\n {\n name: 'hospital',\n count: simulationState => simulationState.hospitals,\n },\n {\n name: 'supermarket',\n count: simulationState => simulationState.supermarkets,\n },\n {\n name: 'station',\n count: simulationState => simulationState.busStations,\n },\n];\n\nconst VENUE_TRANSITIONS = {\n 'house': ['supermarket', 'station', 'hospital', 'house', 'house', 'house',\n 'house', 'house', 'house', 'house', 'house'],\n 'supermarket': ['base', 'base', 'base', 'supermarket'],\n 'hospital': ['hospital', 'base', 'base', 'base'],\n 'station': ['supermarket', 'base', 'base', 'base', 'temple'],\n 'temple': ['supermarket', 'base', 'base', 'base'],\n};\n\nfunction getInitialGraph(simulationState) {\n const nodes = [];\n const edges = [];\n\n VENUES.forEach(({\n name,\n members,\n isRoot,\n count,\n alignment,\n }) => {\n for (let i = 0, nodeIndex = 0; i < count(simulationState); i++, nodeIndex++) {\n const venueId = `${name}-${i}`;\n const venueIndex = nodeIndex;\n nodes.push({\n type: 'venue',\n venue: name,\n id: venueId,\n size: 1,\n });\n\n if (!members) {\n continue;\n }\n\n for (var j = 0; j < members(simulationState); j++, nodeIndex++) {\n const agentId = `${name}-${i}-${j}`;\n nodes.push({\n type: 'agent',\n location: venueId,\n base: venueId,\n id: agentId,\n size: 1,\n state: SUSCEPTIBLE,\n });\n edges.push({\n 'source': agentId,\n 'target': venueId,\n });\n }\n }\n });\n\n const sickAgents = randomSample(\n nodes.filter(({ type }) => type === 'agent'),\n simulationState.initialSickAgents\n );\n\n for (const agent of sickAgents) {\n agent.state = SICK;\n }\n\n return ({\n nodes: applyFixedNodeGrid(nodes),\n edges,\n });\n}\n\nfunction nextSimulationTick(state, nodes, edges) {\n const rootVenue = VENUES.find(({ isRoot }) => isRoot);\n\n nodes\n .filter(\n ({ type }) => type === AGENT\n )\n .forEach(\n (agent, i) => {\n const nextMarkovState = getNextMarkovStateForAgent(agent, VENUE_TRANSITIONS);\n const [agentLocation] = agent.location.split('-')\n\n if (\n agentLocation === nextMarkovState ||\n (nextMarkovState === BASE && agent.location === agent.base) ||\n nextMarkovState === STAY\n ) {\n return;\n } else if (agent.state === DEAD) {\n return;\n } else if (nextMarkovState === BASE) {\n moveAgent(\n nodes,\n edges,\n agent,\n nodes.find(({ id }) => id === agent.base)\n );\n } else {\n moveAgent(\n nodes,\n edges,\n agent,\n findClosestNode(agent, nodes.filter(({ venue }) => venue === nextMarkovState))\n );\n };\n\n }\n );\n\n\n nodes = applySIRModel(nodes, edges);\n\n return {\n nodes: nodes,\n edges: edges,\n state: { ...state, tick: state.tick + 1},\n }\n}\n\nfunction moveAgent(nodes, edges, agent, targetNode) {\n const sourceNode = nodes.find(({ id }) => id === agent.location);\n\n if (targetNode.locked || sourceNode.locked) {\n return;\n }\n\n const newEdges = edges.map((edge) => {\n if (edge.source.id === agent.id) {\n edge.target = targetNode;\n }\n });\n\n agent.location = targetNode.id;\n}\n\nfunction findClosestNode(source, targets) {\n const closest = targets.reduce(\n (prev, current) => distance(source, current) < distance(source, prev) ? current : prev\n );\n\n return closest;\n}\n\nexport {\n VENUES,\n VENUE_TRANSITIONS,\n getInitialGraph,\n nextSimulationTick,\n};\n","import React, { useEffect, useState, useRef } from \"react\";\n\nimport styles from \"./App.module.css\";\nimport Graph from \"./Graph\";\nimport LineChart from \"./LineChart\";\nimport SimulationSettings from \"./SimulationSettings\";\nimport { SICK, RECOVERED, DEAD } from \"./constants\";\nimport { useInterval, randomChoice } from \"./utils\";\nimport { nextSimulationTick, getInitialGraph } from \"./simulation\";\n\nconst INITIAL_SIMULATION_STATE = {\n tick: 0,\n agentsPerHouse: 9,\n houses: 42,\n busStations: 1,\n hospitals: 1,\n supermarkets: 3,\n temples: 1,\n initialSickAgents: 1,\n};\n\nconst INITIAL_GRAPH = getInitialGraph(INITIAL_SIMULATION_STATE);\n\nfunction App() {\n const [simulationState, setSimulationState] = useState(\n INITIAL_SIMULATION_STATE\n );\n const [nodes, setNodes] = useState(INITIAL_GRAPH.nodes);\n const [edges, setEdges] = useState(INITIAL_GRAPH.edges);\n const [historicalSickCount, setHistoricalSickCount] = useState([]);\n const [historicalRecoveredCount, setHistoricalRecoveredCount] = useState([]);\n const [historicalDeadCount, setHistoricalDeadCount] = useState([]);\n const [loading, setLoading] = useState(true);\n\n const graphRef = useRef(null);\n\n useInterval(() => {\n if (loading) {\n return;\n }\n\n const { nodes: _nodes, edges: _edges, state } = nextSimulationTick(\n simulationState,\n nodes,\n edges\n );\n\n setSimulationState(state);\n\n setHistoricalSickCount(\n historicalSickCount.concat(\n nodes.filter(({ state }) => state === SICK).length\n )\n );\n\n setHistoricalRecoveredCount(\n historicalRecoveredCount.concat(\n nodes.filter(({ state }) => state === RECOVERED).length\n )\n );\n\n setHistoricalDeadCount(\n historicalDeadCount.concat(\n nodes.filter(({ state }) => state === DEAD).length\n )\n );\n }, 1000);\n\n useEffect(() => {\n setLoading(false);\n }, [loading]);\n\n const onNodeClick = (nodeId) => {\n return () => {\n const node = nodes.find(({ id }) => nodeId === id);\n if (node.type !== \"venue\") {\n return;\n }\n node.locked = !node.locked;\n };\n };\n\n const onSettingChange = (key) => (event) => {\n setSimulationState({ ...simulationState, [key]: event.target.value });\n };\n\n const onRestartButtonClick = () => {\n const { nodes, edges } = getInitialGraph(simulationState);\n setLoading(true);\n setNodes(nodes);\n setEdges(edges);\n setHistoricalDeadCount([]);\n setHistoricalRecoveredCount([]);\n setHistoricalSickCount([]);\n setSimulationState({ ...simulationState, tick: 0 });\n };\n\n return (\n
\n
\n

Simulering af coronavirus efter SIR-modellen

\n

Et forenklet eksperiment til analyse af, hvordan virus spreder sig i samfund.

\n
\n
\n
\n Modtagelige\n Smittede\n Raske\n Klik på en bygning for at sætte i karantæne.\n
\n {!loading && (\n type === \"venue\").length / 6\n ) * 110\n }\n height={700}\n tick={simulationState.tick}\n nodes={nodes}\n edges={edges}\n onNodeClick={onNodeClick}\n ref={graphRef}\n />\n )}\n
\n
\n
\n

STATISTIK

\n
\n POPULATION: {nodes.filter(({ type }) => type === \"agent\").length}{\" \"}\n
\n DØDE: {nodes.filter(({ state }) => state === DEAD).length}
\n RASKE: {\n nodes.filter(({ state }) => state === RECOVERED).length\n }{\" \"}\n
\n SYGE: {nodes.filter(({ state }) => state === SICK).length}
\n
\n \n
\n
\n

Indstillinger

\n
\n Klik på en bygning for at sætte i karantæne.\n
\n \n
\n\n
\n\n
\n );\n}\n\nexport default App;\n","// This optional code is used to register a service worker.\n// register() is not called by default.\n\n// This lets the app load faster on subsequent visits in production, and gives\n// it offline capabilities. However, it also means that developers (and users)\n// will only see deployed updates on subsequent visits to a page, after all the\n// existing tabs open on the page have been closed, since previously cached\n// resources are updated in the background.\n\n// To learn more about the benefits of this model and instructions on how to\n// opt-in, read https://bit.ly/CRA-PWA\n\nconst isLocalhost = Boolean(\n window.location.hostname === 'localhost' ||\n // [::1] is the IPv6 localhost address.\n window.location.hostname === '[::1]' ||\n // 127.0.0.0/8 are considered localhost for IPv4.\n window.location.hostname.match(\n /^127(?:\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/\n )\n);\n\nexport function register(config) {\n if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {\n // The URL constructor is available in all browsers that support SW.\n const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);\n if (publicUrl.origin !== window.location.origin) {\n // Our service worker won't work if PUBLIC_URL is on a different origin\n // from what our page is served on. This might happen if a CDN is used to\n // serve assets; see https://github.com/facebook/create-react-app/issues/2374\n return;\n }\n\n window.addEventListener('load', () => {\n const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;\n\n if (isLocalhost) {\n // This is running on localhost. Let's check if a service worker still exists or not.\n checkValidServiceWorker(swUrl, config);\n\n // Add some additional logging to localhost, pointing developers to the\n // service worker/PWA documentation.\n navigator.serviceWorker.ready.then(() => {\n console.log(\n 'This web app is being served cache-first by a service ' +\n 'worker. To learn more, visit https://bit.ly/CRA-PWA'\n );\n });\n } else {\n // Is not localhost. Just register service worker\n registerValidSW(swUrl, config);\n }\n });\n }\n}\n\nfunction registerValidSW(swUrl, config) {\n navigator.serviceWorker\n .register(swUrl)\n .then(registration => {\n registration.onupdatefound = () => {\n const installingWorker = registration.installing;\n if (installingWorker == null) {\n return;\n }\n installingWorker.onstatechange = () => {\n if (installingWorker.state === 'installed') {\n if (navigator.serviceWorker.controller) {\n // At this point, the updated precached content has been fetched,\n // but the previous service worker will still serve the older\n // content until all client tabs are closed.\n console.log(\n 'New content is available and will be used when all ' +\n 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'\n );\n\n // Execute callback\n if (config && config.onUpdate) {\n config.onUpdate(registration);\n }\n } else {\n // At this point, everything has been precached.\n // It's the perfect time to display a\n // \"Content is cached for offline use.\" message.\n console.log('Content is cached for offline use.');\n\n // Execute callback\n if (config && config.onSuccess) {\n config.onSuccess(registration);\n }\n }\n }\n };\n };\n })\n .catch(error => {\n console.error('Error during service worker registration:', error);\n });\n}\n\nfunction checkValidServiceWorker(swUrl, config) {\n // Check if the service worker can be found. If it can't reload the page.\n fetch(swUrl, {\n headers: { 'Service-Worker': 'script' },\n })\n .then(response => {\n // Ensure service worker exists, and that we really are getting a JS file.\n const contentType = response.headers.get('content-type');\n if (\n response.status === 404 ||\n (contentType != null && contentType.indexOf('javascript') === -1)\n ) {\n // No service worker found. Probably a different app. Reload the page.\n navigator.serviceWorker.ready.then(registration => {\n registration.unregister().then(() => {\n window.location.reload();\n });\n });\n } else {\n // Service worker found. Proceed as normal.\n registerValidSW(swUrl, config);\n }\n })\n .catch(() => {\n console.log(\n 'No internet connection found. App is running in offline mode.'\n );\n });\n}\n\nexport function unregister() {\n if ('serviceWorker' in navigator) {\n navigator.serviceWorker.ready\n .then(registration => {\n registration.unregister();\n })\n .catch(error => {\n console.error(error.message);\n });\n }\n}\n","import React from 'react';\nimport ReactDOM from 'react-dom';\nimport './index.css';\nimport App from './App';\nimport * as serviceWorker from './serviceWorker';\n\nReactDOM.render(\n \n \n ,\n document.getElementById('root')\n);\n\n// If you want your app to work offline and load faster, you can change\n// unregister() to register() below. Note this comes with some pitfalls.\n// Learn more about service workers: https://bit.ly/CRA-PWA\nserviceWorker.unregister();\n","// extracted by mini-css-extract-plugin\nmodule.exports = {\"container\":\"App_container__1BgkU\",\"header\":\"App_header__1HRDj\",\"simulationContainer\":\"App_simulationContainer__oynuc\",\"simulation\":\"App_simulation__HhNoC\",\"section\":\"App_section__QRt9k\",\"simulationSettings\":\"App_simulationSettings__2d9tM\",\"stats\":\"App_stats__1l6QZ\",\"population\":\"App_population__3Bldr\",\"simulationInfo\":\"App_simulationInfo__13QHu\",\"pageInfo\":\"App_pageInfo__39ttF\",\"samples\":\"App_samples__2q0yA\",\"sampleSusceptible\":\"App_sampleSusceptible__2in3j\",\"sampleInfected\":\"App_sampleInfected__3yojZ\",\"sampleRecovered\":\"App_sampleRecovered__1xsCh\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"container\":\"SimulationSettings_container__3baTK\",\"form\":\"SimulationSettings_form__3Qt6w\",\"value\":\"SimulationSettings_value__3Fq05\",\"footer\":\"SimulationSettings_footer__1VBhM\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"node\":\"Graph_node__5l2xq\",\"venue\":\"Graph_venue__u_zAR\",\"hospital\":\"Graph_hospital__y2wMz\",\"teatr\":\"Graph_teatr__3-iNj\",\"station\":\"Graph_station__13IKM\",\"supermarket\":\"Graph_supermarket__1CFiW\",\"susceptible\":\"Graph_susceptible__2t045\",\"dead\":\"Graph_dead__19_fK\",\"sick\":\"Graph_sick__3bkF6\",\"recovered\":\"Graph_recovered__92hH3\",\"lockedSymbol\":\"Graph_lockedSymbol__2J58g\"};"],"sourceRoot":""}