Sync codebase

Ref: f65cafb8d1
This commit is contained in:
Hans5958 2023-09-13 19:16:08 +07:00
parent 39440f263e
commit b6d39f7e39
12 changed files with 424 additions and 338 deletions

View file

@ -52,6 +52,14 @@ .offcanvas {
padding-left: env(safe-area-inset-left);
}
.opacity-0 {
opacity: 0;
}
.transition-opacity {
transition: opacity .15s linear;
}
@supports (backdrop-filter: blur()) or (-webkit-backdrop-filter: blur()) {
.navbar, .offcanvas {
-webkit-backdrop-filter: saturate(180%) blur(15px);

View file

@ -69,7 +69,7 @@ const periodClipboard = {
const drawBackButton = document.createElement("a")
drawBackButton.className = "btn btn-outline-primary"
drawBackButton.id = "drawBackButton"
drawBackButton.textContent = "Exit Draw Mode"
drawBackButton.textContent = "Exit Drawing"
const baseInputAddon = document.createElement("span")
baseInputAddon.className = "input-group-text"
@ -106,12 +106,13 @@ function initDraw() {
showListButton.insertAdjacentHTML("afterend", '<button class="btn btn-outline-primary" type="button" data-bs-toggle="offcanvas" data-bs-target="#offcanvasDraw" aria-controls="offcanvasDraw">Menu</button>')
showListButton.parentElement.appendChild(drawBackButton)
showListButton.remove()
drawButton.remove()
// Opens draw menu
wrapper.classList.remove('listHidden')
bsOffcanvasDraw.show()
window.render = render
window.renderHighlight = renderHighlight
window.renderBackground = renderBackground
window.updateHovering = updateHovering
@ -125,13 +126,18 @@ function initDraw() {
let highlightUncharted = highlightUnchartedEl.checked
renderBackground(atlas)
window.updateAtlas = updateAtlas
updateAtlas()
document.addEventListener('timeupdate', () => {
updateAtlas()
})
applyView()
container.style.cursor = "crosshair"
render(path)
container.addEventListener("mousedown", e => {
lastPos = [
e.clientX,
@ -168,7 +174,7 @@ function initDraw() {
const coords = getCanvasCoords(e.clientX, e.clientY)
path.push(coords)
render(path)
renderHighlight(path)
undoHistory = []
redoButton.disabled = true
@ -185,13 +191,13 @@ function initDraw() {
container.addEventListener("mousemove", e => {
if (!dragging && drawing && path.length > 0) {
const coords = getCanvasCoords(e.clientX, e.clientY)
render([...path, coords])
renderHighlight([...path, coords])
}
})
container.addEventListener("mouseout", function () {
if (!dragging && drawing && path.length > 0) {
render(path)
renderHighlight(path)
}
})
@ -228,19 +234,19 @@ function initDraw() {
undoButton.addEventListener("click", e => {
undo()
const coords = getCanvasCoords(e.clientX, e.clientY)
render([...path, coords])
renderHighlight([...path, coords])
})
redoButton.addEventListener("click", e => {
redo()
const coords = getCanvasCoords(e.clientX, e.clientY)
render([...path, coords])
renderHighlight([...path, coords])
})
resetButton.addEventListener("click", e => {
reset()
const coords = getCanvasCoords(e.clientX, e.clientY)
render([...path, coords])
renderHighlight([...path, coords])
})
resetButton.addEventListener("blur", function () {
@ -269,7 +275,7 @@ function initDraw() {
highlightUnchartedEl.addEventListener("click", function () {
highlightUncharted = this.checked
render(path)
renderHighlight(path)
})
function generateExportObject() {
@ -385,13 +391,11 @@ function initDraw() {
}
githubPostButton.href = githubPostUrl
console.log(githubPostUrl)
exportModal.show()
}
function preview() {
let infoElement = createInfoBlock(generateExportObject(), true)
let infoElement = createInfoBlock(generateExportObject(), 2)
objectsContainer.replaceChildren()
objectsContainer.appendChild(infoElement)
closeObjectsListButton.classList.remove("d-none")
@ -483,17 +487,17 @@ function initDraw() {
closeObjectsListButton.classList.add("d-none")
}
function renderBackground() {
function renderBackground(atlas) {
backgroundContext.clearRect(0, 0, highlightCanvas.width, highlightCanvas.height)
backgroundContext.fillStyle = "rgba(0, 0, 0, 1)"
//backgroundContext.fillRect(0, 0, canvas.width, canvas.height)
//backgroundContext.fillRect(0, 0, canvas.width, renderBackgroundcanvas.height)
for (let i = 0; i < atlas.length; i++) {
const path = atlas[i].path
for (const entry of Object.values(atlas)) {
const path = entry.path
backgroundContext.beginPath()
if (path[0]) {
@ -510,7 +514,7 @@ function initDraw() {
}
}
function render(path) {
function renderHighlight(path) {
if (!Array.isArray(path)) return
@ -553,10 +557,16 @@ function initDraw() {
updateCoordsDisplay(e)
}
function updateAtlas() {
;[atlas, atlasOrder] = filterAtlas(atlasAll)
;[atlasDisplay, atlasOrder] = generateAtlasDisplay(atlas, atlasOrder, currentPeriod, currentVariation)
renderBackground(atlasDisplay)
renderHighlight(atlasDisplay)
}
const getEntry = id => {
if (!id) return
const entries = atlasAll.filter(entry => entry.id.toString() === id.toString())
if (entries.length === 1) return entries[0]
return atlasAll[id]
}
function addFieldButton(inputButton, inputGroup, array, index, name) {
@ -790,7 +800,7 @@ function initDraw() {
} else {
document.getElementById("offcanvasDrawLabel").textContent = "New Entry"
pathWithPeriods.push([formatPeriod(currentPeriod, currentPeriod, currentVariation), []])
pathWithPeriods.push([formatPeriod(currentPeriod, null, currentVariation), []])
// Builds multi-input list
addWebsiteFields("", 0, [0])
@ -811,19 +821,19 @@ function initDraw() {
)
document.addEventListener('timeupdate', () => {
renderBackground(atlas)
renderBackground(atlasDisplay)
updatePeriodGroups()
})
periodsAdd.addEventListener('click', () => {
pathWithPeriods.push([formatPeriod(currentPeriod, currentPeriod, currentVariation), []])
pathWithPeriods.push([formatPeriod(currentPeriod, null, currentVariation), []])
initPeriodGroups()
})
drawBackButton.href = "./" + formatHash(entry?.id)
document.addEventListener('timeupdate', event => {
drawBackButton.href = "./" + formatHash(entry?.id, event.detail.period, event.detail.period, event.detail.variation)
drawBackButton.href = "./" + formatHash(entry?.id, event.detail.period, event.detail.variation)
})
}
@ -1224,7 +1234,7 @@ function updatePeriodGroups() {
function updatePath(newPath, newUndoHistory) {
path = newPath || path
if (path.length > 3) center = calculateCenter(path)
render(path)
renderHighlight(path)
undoButton.disabled = path.length === 0; // Maybe make it undo the cancel action in the future
undoHistory = newUndoHistory || []
redoButton.disabled = (!undoHistory.length)

View file

@ -31,7 +31,10 @@ function createInfoListItem(name, value) {
return entryInfoListElement
}
function createInfoBlock(entry, isPreview) {
// mode 0 = normal
// mode 1 = entry list but none on atlas
// mode 2 = preview
function createInfoBlock(entry, mode = 0) {
const element = document.createElement("div")
element.className = "card mb-2 overflow-hidden shadow"
@ -40,21 +43,31 @@ function createInfoBlock(entry, isPreview) {
const linkElement = document.createElement("a")
linkElement.className = "text-decoration-none d-flex justify-content-between text-body"
if (isPreview) linkElement.href = "#"
else {
linkElement.href = formatHash(entry.id, null, null, null, false, false, false)
linkElement.addEventListener('click', e => {
let nearestPeriod = currentPeriod
let nearestVariation = currentVariation
if (!atlasDisplay[entry.id]) {
[nearestPeriod, nearestVariation] = getNearestPeriod(entry, currentPeriod, currentVariation)
}
if (mode === 2) {
linkElement.href = "#"
} else {
const hash = formatHash(entry.id, nearestPeriod, nearestVariation, false, false, false)
linkElement.href = hash
if (mode === 0) linkElement.addEventListener('click', e => {
e.preventDefault()
location.hash = formatHash(entry.id, null, null, null, false, false, false)
location.hash = hash
window.dispatchEvent(new HashChangeEvent("hashchange"))
})
}
const linkNameElement = document.createElement("span")
linkNameElement.className = "flex-grow-1 text-break"
linkNameElement.textContent = entry.name
headerElement.appendChild(linkElement)
linkElement.appendChild(linkNameElement)
linkElement.insertAdjacentHTML("beforeend", '<i class="bi bi-link-45deg align-self-center link-primary" aria-hidden="true"></i>')
linkElement.insertAdjacentHTML("beforeend", '<i class="bi bi-link-45deg align-self-center link-primary" aria-hidden="true" title="Copy direct link"></i>')
element.appendChild(headerElement)
const bodyElement = document.createElement("div")
@ -92,7 +105,7 @@ function createInfoBlock(entry, isPreview) {
}
// Entry data submitted to preview does not include center or path
if (!isPreview) {
if (mode === 0) {
const [x, y] = entry?.center
listElement.appendChild(createInfoListItem("Position: ", `${Math.floor(x)}, ${Math.floor(y)}`))
@ -170,11 +183,11 @@ function createInfoBlock(entry, isPreview) {
element.appendChild(idElementContainer)
// Adds edit button only if element is not deleted
if (!isPreview && (!entry.diff || entry.diff !== "delete")) {
if (mode < 2 && (!entry.diff || entry.diff !== "delete")) {
const editElement = document.createElement("a")
editElement.textContent = "Edit"
editElement.innerHTML = '<i class="bi bi-pencil-fill" aria-hidden="true"></i> Edit'
editElement.className = "btn btn-sm btn-outline-primary"
editElement.href = "./?mode=draw&id=" + entry.id + formatHash(false)
editElement.href = "./?mode=draw&id=" + entry.id + formatHash(false, nearestPeriod, nearestVariation, false, false, false)
editElement.title = "Edit " + entry.name
idElementContainer.appendChild(editElement)
}

View file

@ -26,7 +26,7 @@ const maxZoom = 128
const minZoom = 0.125
let zoomOrigin = [0, 0]
let scaleZoomOrigin = [0, 0]
let scaleZoomOrigin = [canvasCenter.x, canvasCenter.y]
let dragging = false
let lastPosition = [0, 0]
@ -54,7 +54,7 @@ function applyView() {
}
function setView(targetX, targetY, targetZoom) {
if (isNaN(targetX)) targetX = null
if (isNaN(targetY)) targetY = null
@ -72,8 +72,6 @@ function updateHash(...args) {
if (location.hash !== newLocation.hash) history.replaceState({}, "", newLocation)
}
let atlas = null
window.atlas = atlas
let atlasAll = null
window.atlasAll = atlasAll
@ -106,9 +104,9 @@ async function init() {
// For Reviewing Reddit Changes
// const atlasRef = '../tools/temp-atlas.json'
const atlasRef = params.get('atlas') || './atlas.json'
const atlasResp = await fetch(atlasRef)
atlasAll = updateAtlasAll(await atlasResp.json())
const atlasAllUrl = params.get('atlas') || './atlas.json'
atlasAll = generateAtlasAll(await (await fetch(atlasAllUrl)).json())
// console.log(atlas, atlasOrder)
const hash = window.location.hash.substring(1)
const [, hashPeriod, hashX, hashY, hashZoom] = hash.split('/')
@ -148,45 +146,33 @@ async function init() {
initExplore()
} else if (mode.startsWith("diff")) {
try {
const liveAtlasRef = params.get('liveatlas') || `https://${prodDomain}/atlas.json`
const liveAtlasResp = await fetch(liveAtlasRef)
let liveAtlas = await liveAtlasResp.json()
liveAtlas = updateAtlasAll(liveAtlas)
const liveAtlasUrl = params.get('liveatlas') || `https://${prodDomain}/atlas.json`
let liveAtlasAll = generateAtlasAll(await (await fetch(liveAtlasUrl)).json())
const liveAtlasReduced = liveAtlas.reduce((atlas, entry) => {
delete entry._index
atlas[entry.id] = entry
return atlas
}, {})
// Mark added/edited entries
atlasAll = atlasAll.map(function (entry) {
delete entry._index
if (!liveAtlasReduced[entry.id]) {
for (const entry of Object.values(atlasAll)) {
if (!liveAtlasAll[entry.id]) {
entry.diff = "add"
} else if (JSON.stringify(entry) !== JSON.stringify(liveAtlasReduced[entry.id])) {
} else {
if (JSON.stringify({ ...entry, _index: undefined }) === JSON.stringify({ ...liveAtlasAll[entry.id], _index: undefined })) continue
entry.diff = "edit"
}
return entry
})
// Mark removed entries
const atlasReduced = atlasAll.reduce((atlas, entry) => {
delete entry._index
atlas[entry.id] = entry
return atlas
}, {})
const removedEntries = liveAtlas.filter(entry => !atlasReduced[entry.id]).map(entry => {
delete entry._index
entry.diff = "delete"
return entry
})
atlasAll.push(...removedEntries)
if (mode.includes("only")) {
atlasAll = atlasAll.filter(entry => entry.diff)
}
atlas = generateAtlasForPeriod()
// Mark removed entries
for (const entry of Object.values(liveAtlasAll)) {
if (!atlasAll[entry.id]) {
entry.diff = "delete"
atlasAll[entry.id] = entry
}
}
if (mode.includes('only')) {
for (const key of Object.keys(atlasAll)) {
if (atlasAll[key].diff) continue
delete atlasAll[key]
}
}
} catch (error) {
console.warn("Diff mode failed to load, reverting to normal view.", error)
@ -255,7 +241,7 @@ async function init() {
zoom = 1
zoomOrigin = [0, 0]
scaleZoomOrigin = [0, 0]
updateLines()
renderLines()
applyView()
})
@ -384,7 +370,6 @@ async function init() {
}
window.addEventListener("mousemove", e => {
// updateLines()
mousemove(e.clientX, e.clientY)
if (dragging) {
e.preventDefault()
@ -417,7 +402,7 @@ async function init() {
scaleZoomOrigin[0] += deltaX / zoom
scaleZoomOrigin[1] += deltaY / zoom
updateLines()
renderLines()
applyView()
}
@ -460,7 +445,7 @@ async function init() {
zoomOrigin[1] = scaleZoomOrigin[1] * zoom
applyView()
updateLines()
renderLines()
}
window.addEventListener("mouseup", e => {
@ -478,7 +463,7 @@ async function init() {
})
window.addEventListener("touchend", touchend)
function mouseup(x, y) {
function mouseup() {
dragging = false
updateHash()
}
@ -486,7 +471,7 @@ async function init() {
function touchend(e) {
if (e.touches.length === 0) {
mouseup()
setTimeout(() => updateLines(), 0)
renderLines()
dragging = false
} else if (e.touches.length === 1) {
@ -506,7 +491,8 @@ async function init() {
}
function updateAtlasAll(atlas = atlasAll) {
function generateAtlasAll(atlas = atlasAll) {
const newAtlas = {}
for (const index in atlas) {
const entry = atlas[index]
entry._index = index
@ -528,8 +514,10 @@ function updateAtlasAll(atlas = atlasAll) {
}
entry.path = currentPath
entry.center = currentCenter
newAtlas[entry.id] = entry
}
return atlas
// console.log(newAtlas)
return newAtlas
}
// Announcement system

View file

@ -9,22 +9,16 @@ function initOverlap() {
window.renderBackground = renderBackground
updateAtlas()
// const hovered = []
resetEntriesList()
renderBackground(atlas)
render()
document.addEventListener('timeupdate', () => {
atlasDisplay = atlas.slice()
resetEntriesList()
renderBackground(atlasDisplay)
render()
updateAtlas()
})
applyView()
render()
updateLines()
renderLines()
if (window.location.hash) {
updateViewFromHash()
@ -37,7 +31,7 @@ function initOverlap() {
backgroundContext.fillStyle = "rgba(255, 255, 255, 1)"
backgroundContext.fillRect(0, 0, highlightCanvas.width, highlightCanvas.height)
for (const entry of atlas) {
for (const entry of Object.values(atlas)) {
const path = entry.path

View file

@ -32,6 +32,9 @@ let currentPeriod = defaultPeriod
window.currentVariation = currentVariation
window.currentPeriod = currentPeriod
let atlasDisplay = {}
window.atlasDisplay = atlasDisplay
// SETUP
if (variationsConfig[currentVariation].versions.length === 1) bottomBar.classList.add('no-time-slider')
@ -70,7 +73,7 @@ const dispatchTimeUpdateEvent = (period = currentPeriod, variation = currentVari
detail: {
period: period,
variation: variation,
periodString: formatPeriod(period, period, variation),
periodString: formatPeriod(period, null, variation),
atlas: atlas
}
})
@ -79,7 +82,7 @@ const dispatchTimeUpdateEvent = (period = currentPeriod, variation = currentVari
async function updateBackground(newPeriod = currentPeriod, newVariation = currentVariation) {
abortController.abort()
myAbortController = new AbortController()
const myAbortController = new AbortController()
abortController = myAbortController
currentUpdateIndex++
const myUpdateIndex = currentUpdateIndex
@ -96,7 +99,7 @@ async function updateBackground(newPeriod = currentPeriod, newVariation = curren
variantsEl.parentElement.classList.remove('input-group')
}
const configObject = variationConfig.versions[currentPeriod]
const configObject = variationConfig.versions[newPeriod]
let layerUrls = []
let layers = []
@ -110,39 +113,59 @@ async function updateBackground(newPeriod = currentPeriod, newVariation = curren
layers.length = layerUrls.length
await Promise.all(layerUrls.map(async (url, i) => {
const imageBlob = await (await fetch(url, { signal: myAbortController.signal })).blob()
const imageLayer = new Image()
await new Promise(resolve => {
imageLayer.onload = () => {
context.canvas.width = Math.max(imageLayer.width, context.canvas.width)
context.canvas.height = Math.max(imageLayer.height, context.canvas.height)
layers[i] = imageLayer
resolve()
}
imageLayer.src = URL.createObjectURL(imageBlob)
})
try {
const imageBlob = await (await fetch(url, { signal: myAbortController.signal })).blob()
const imageLayer = new Image()
await new Promise(resolve => {
imageLayer.onload = () => {
context.canvas.width = Math.max(imageLayer.width, context.canvas.width)
context.canvas.height = Math.max(imageLayer.height, context.canvas.height)
layers[i] = imageLayer
resolve()
}
imageLayer.src = URL.createObjectURL(imageBlob)
})
} catch (e) {
const aborted = myAbortController.signal.aborted
if (!aborted) throw e
}
}))
if (myAbortController.signal.aborted || newPeriod !== currentPeriod || newVariation !== currentVariation) {
return
if (currentUpdateIndex !== myUpdateIndex) {
return false
}
for (const imageLayer of layers) {
context.drawImage(imageLayer, 0, 0)
}
if (currentUpdateIndex !== myUpdateIndex) return [configObject, newPeriod, newVariation]
const blob = await new Promise(resolve => canvas.toBlob(resolve))
canvasUrl = URL.createObjectURL(blob)
image.src = canvasUrl
return true
}
let loadingTimeout = setTimeout(() => {}, 0)
async function updateTime(newPeriod = currentPeriod, newVariation = currentVariation, forceLoad = false) {
if (newPeriod === currentPeriod && !forceLoad) {
return;
if (newPeriod === currentPeriod && newVariation === currentVariation && !forceLoad) {
return
}
document.body.dataset.canvasLoading = ""
const loadingEl = document.getElementById("loading")
const previouslyHidden = loadingEl.classList.contains("d-none")
if (previouslyHidden) loadingEl.classList.add("opacity-0", "transition-opacity")
clearTimeout(loadingTimeout)
loadingTimeout = setTimeout(() => {
loadingEl.classList.remove("d-none")
if (previouslyHidden) setTimeout(() => {
loadingEl.classList.remove("opacity-0")
}, 0)
}, 2000)
// const oldPeriod = currentPeriod
const oldVariation = currentVariation
@ -167,12 +190,16 @@ async function updateTime(newPeriod = currentPeriod, newVariation = currentVaria
timelineSlider.value = currentPeriod
updateTooltip(newPeriod, newVariation)
await updateBackground(newPeriod, newVariation)
const updateBackgroundResult = await updateBackground(newPeriod, newVariation)
atlas = generateAtlasForPeriod(newPeriod, newVariation)
if (!updateBackgroundResult) return
dispatchTimeUpdateEvent(newPeriod, newVariation, atlas)
delete document.body.dataset.canvasLoading
clearTimeout(loadingTimeout)
document.getElementById("loading").classList.add("d-none")
document.getElementById("loading").classList.remove("opacity-0", "opacity-100", "transition-opacity")
tooltip.dataset.forceVisible = ""
clearTimeout(tooltipDelayHide)
tooltipDelayHide = setTimeout(() => {
@ -181,24 +208,30 @@ async function updateTime(newPeriod = currentPeriod, newVariation = currentVaria
}
function generateAtlasForPeriod(newPeriod = currentPeriod, newVariation = currentVariation) {
function generateAtlasDisplay(prevAtlas, prevAtlasOrder, newPeriod = currentPeriod, newVariation = currentVariation) {
const newAtlas = {}
const newAtlasOrderDisplayed = []
const newAtlasOrderNotDisplayed = []
for (const id of prevAtlasOrder) {
newAtlasOrderNotDisplayed.push(id)
const entry = prevAtlas[id]
const atlas = []
for (const entry of atlasAll) {
let chosenIndex
const validPeriods2 = Object.keys(entry.path)
for (const i in validPeriods2) {
periodCheck: for (const i in validPeriods2) {
const validPeriods = validPeriods2[i].split(', ')
for (const j in validPeriods) {
const [start, end, variation] = parsePeriod(validPeriods[j])
if (isOnPeriod(start, end, variation, newPeriod, newVariation)) {
chosenIndex = i
break
break periodCheck
}
}
if (chosenIndex !== undefined) break
}
if (chosenIndex === undefined) continue
@ -207,14 +240,18 @@ function generateAtlasForPeriod(newPeriod = currentPeriod, newVariation = curren
if (pathChosen === undefined) continue
atlas.push({
newAtlas[id] = {
...entry,
path: pathChosen,
center: centerChosen,
})
}
newAtlasOrderNotDisplayed.pop()
newAtlasOrderDisplayed.push(id)
}
return atlas
return [newAtlas, [...newAtlasOrderDisplayed, ...newAtlasOrderNotDisplayed]]
}
@ -273,7 +310,7 @@ function parsePeriod(periodString) {
function formatPeriod(targetStart, targetEnd, targetVariation, forUrl = false) {
targetStart ??= currentPeriod
targetEnd ??= currentPeriod
targetEnd ??= targetStart
targetVariation ??= currentVariation
let periodString, variationString
@ -297,12 +334,11 @@ function setReferenceVal(reference, newValue) {
else return reference ?? newValue
}
function formatHash(targetEntry, targetPeriodStart, targetPeriodEnd, targetVariation, targetX, targetY, targetZoom) {
function formatHash(targetEntry, targetPeriod, targetVariation, targetX, targetY, targetZoom) {
let hashData = window.location.hash.substring(1).split('/')
targetEntry = setReferenceVal(targetEntry, hashData[0])
targetPeriodStart = setReferenceVal(targetPeriodStart, currentPeriod)
targetPeriodEnd = setReferenceVal(targetPeriodEnd, currentPeriod)
targetPeriod = setReferenceVal(targetPeriod, currentPeriod)
targetVariation = setReferenceVal(targetVariation, currentVariation)
targetX = setReferenceVal(targetX, canvasCenter.x - scaleZoomOrigin[0])
targetY = setReferenceVal(targetY, canvasCenter.y - scaleZoomOrigin[1])
@ -313,8 +349,8 @@ function formatHash(targetEntry, targetPeriodStart, targetPeriodEnd, targetVaria
if (targetZoom) targetZoom = targetZoom.toFixed(3).replace(/\.?0+$/, '')
const result = [targetEntry]
const targetPeriod = formatPeriod(targetPeriodStart, targetPeriodEnd, targetVariation, true)
result.push(targetPeriod, targetX, targetY, targetZoom)
const targetPeriodFormat = formatPeriod(targetPeriod, null, targetVariation, true)
result.push(targetPeriodFormat, targetX, targetY, targetZoom)
if (!result.some(el => el || el === 0)) return ''
return '#' + result.join('/').replace(/\/+$/, '')
}
@ -327,4 +363,42 @@ function downloadCanvas() {
document.body.appendChild(linkEl)
linkEl.click()
document.body.removeChild(linkEl)
}
}
function getNearestPeriod(entry, targetPeriod, targetVariation) {
const pathKeys = Object.keys(entry.path)
let nearestScore, nearestPeriod, nearestVariation, nearestKey
function updateNearest(newScore, newPeriod, newVariation, newKey) {
if (newScore >= nearestScore) return
nearestScore = newScore
nearestPeriod = newPeriod
nearestVariation = newVariation
nearestKey = newKey
}
checkPaths: for (const pathKey of pathKeys) {
const pathPeriods = pathKey.split(', ')
checkPathPeriod: for (const j in pathPeriods) {
const [pathStart, pathEnd, pathVariation] = parsePeriod(pathPeriods[j])
if (isOnPeriod(pathStart, pathEnd, pathVariation, targetPeriod, targetVariation)) {
updateNearest(0, targetPeriod, targetVariation)
break checkPaths
} else if (pathVariation !== targetVariation) {
updateNearest(Infinity, pathStart, pathVariation, pathKey)
continue checkPathPeriod
} else if (Math.abs(pathStart - targetPeriod) < Math.abs(pathEnd - targetPeriod)) {
updateNearest(Math.abs(pathStart - targetPeriod), pathStart, pathVariation, pathKey)
} else {
updateNearest(Math.abs(pathEnd - targetPeriod), pathStart, pathVariation, pathKey)
}
}
}
return [ nearestPeriod, nearestVariation, nearestKey ]
}

View file

@ -43,7 +43,11 @@ objectEditNav.className = "btn btn-outline-primary"
objectEditNav.id = "objectEditNav"
objectEditNav.textContent = "Edit"
let atlasDisplay
let atlas = null
window.atlas = atlas
let atlasOrder = []
window.atlasOrder = atlasOrder
const entriesLimit = 50
let entriesOffset = 0
@ -72,11 +76,11 @@ let lastPos = [0, 0]
let fixed = false; // Fix hovered items in place, so that clicking on links is possible
searchInput.addEventListener("input", function () {
resetEntriesList()
updateAtlas()
})
sortInput.addEventListener("input", function () {
resetEntriesList()
updateAtlas()
})
offcanvasDraw.addEventListener('show.bs.offcanvas', () => {
@ -112,8 +116,8 @@ offcanvasList.addEventListener('shown.bs.offcanvas', e => {
wrapper.classList.remove('listTransitioning')
updateHovering(e)
applyView()
render()
updateLines()
renderHighlight()
renderLines()
})
offcanvasList.addEventListener('hide.bs.offcanvas', () => {
@ -127,8 +131,8 @@ offcanvasList.addEventListener('hidden.bs.offcanvas', e => {
wrapper.classList.remove('listTransitioning')
updateHovering(e)
applyView()
render()
updateLines()
renderHighlight()
renderLines()
})
closeObjectsListButton.addEventListener("click", clearObjectsList)
@ -138,52 +142,52 @@ bottomBar.addEventListener("mouseover", () => {
})
function clearObjectsList() {
hovered = []
fixed = false
renderLines()
renderHighlight()
document.title = pageTitle
closeObjectsListButton.classList.add("d-none")
objectsListOverflowNotice.classList.add("d-none")
entriesList.classList.remove("disableHover")
hovered = []
objectsContainer.replaceChildren()
updateLines()
fixed = false
render()
objectEditNav.remove()
updateHash(false)
document.title = pageTitle
}
function toggleFixed(e, tapped) {
if (!fixed && hovered.length === 0) {
entriesList.classList.remove("disableHover")
return 0
return
}
fixed = !fixed
if (!fixed) {
updateHovering(e, tapped)
render()
renderHighlight()
}
entriesList.classList.add("disableHover")
objectsListOverflowNotice.classList.add("d-none")
}
window.addEventListener("resize", updateLines)
window.addEventListener("mousemove", updateLines)
window.addEventListener("dblClick", updateLines)
window.addEventListener("wheel", updateLines)
window.addEventListener("dblClick", renderLines)
window.addEventListener("wheel", renderLines)
objectsContainer.addEventListener("scroll", () => {
updateLines()
renderLines()
})
window.addEventListener("resize", () => {
applyView()
render()
updateLines()
renderHighlight()
renderLines()
})
function updateLines() {
async function renderLines() {
if (hovered.length === 0) {
linesContext.clearRect(0, 0, linesCanvas.width, linesCanvas.height)
return
}
// Line border
linesCanvas.width = linesCanvas.clientWidth
linesCanvas.height = linesCanvas.clientHeight
@ -260,9 +264,9 @@ function renderBackground(atlas) {
backgroundContext.fillStyle = "rgba(0, 0, 0, 0.6)"
backgroundContext.fillRect(0, 0, backgroundCanvas.width, backgroundCanvas.height)
for (let i = 0; i < atlas.length; i++) {
for (const entry of Object.values(atlas)) {
const path = atlas[i].path
const path = entry.path
backgroundContext.beginPath()
@ -279,7 +283,7 @@ function renderBackground(atlas) {
backgroundContext.closePath()
let bgStrokeStyle
switch (atlas[i].diff) {
switch (entry.diff) {
case "add":
bgStrokeStyle = "rgba(0, 255, 0, 1)"
backgroundContext.lineWidth = 2
@ -299,45 +303,39 @@ function renderBackground(atlas) {
backgroundContext.strokeStyle = bgStrokeStyle
backgroundContext.stroke()
backgroundContext.lineWidth = 1
}
}
function buildObjectsList(filter, sort) {
function filterAtlas(prevAtlas) {
atlasDisplay = atlas.slice()
const sort = sortInput.value || defaultSort
const search = searchInput?.value.toLowerCase()
let newAtlas = Object.assign({}, prevAtlas)
let newAtlasOrder = []
if (filter) {
atlasDisplay = atlas.filter(entry => {
return (
entry.name.toLowerCase().includes(filter.toLowerCase())
|| entry.description?.toLowerCase().includes(filter.toLowerCase())
|| Object.values(entry.links).flat().some(str => str.toLowerCase().includes(filter))
|| entry.id.toString() === filter
)
})
document.getElementById("atlasSize").innerHTML = "Found " + atlasDisplay.length + " entries."
} else {
document.getElementById("atlasSize").innerHTML = "The Atlas contains " + atlasDisplay.length + " entries."
document.getElementById("atlasSize").innerHTML = ""
if (search) {
for (const [id, entry] of Object.entries(prevAtlas)) {
if (!(
entry.name.toLowerCase().includes(search.toLowerCase()) ||
entry.description?.toLowerCase().includes(search.toLowerCase()) ||
Object.values(entry.links).flat().some(str => str.toLowerCase().includes(search)) ||
id.toString() === search
)) delete newAtlas[id]
}
}
renderBackground(atlasDisplay)
render()
sort ||= defaultSort
document.getElementById("sort").value = sort
//console.log(sort)
// document.getElementById("sort").value = sort
let sortFunction
//console.log(sort)
switch (sort) {
case "shuffle":
sortFunction = null
if (entriesOffset === 0) {
shuffle()
}
sortFunction = () => Math.random() - 0.5
break
case "alphaAsc":
sortFunction = (a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase())
@ -369,10 +367,37 @@ function buildObjectsList(filter, sort) {
break
}
newAtlasOrder = Object.keys(newAtlas)
if (sortFunction) {
atlasDisplay.sort(sortFunction)
newAtlasOrder = newAtlasOrder.sort((a, b) => sortFunction(prevAtlas[a], prevAtlas[b]))
}
// console.log(newAtlas, newAtlasOrder)
return [newAtlas, newAtlasOrder]
}
function updateAtlas() {
;[atlas, atlasOrder] = filterAtlas(atlasAll)
;[atlasDisplay, atlasOrder] = generateAtlasDisplay(atlas, atlasOrder, currentPeriod, currentVariation)
const atlasSizeEl = document.getElementById("atlasSize")
if (Object.keys(atlas).length === Object.keys(atlasAll).length) {
atlasSizeEl.innerHTML = Object.keys(atlasAll).length + " entries in total."
} else {
atlasSizeEl.innerHTML = "Found " + Object.keys(atlas).length + " entries."
}
atlasSizeEl.innerHTML += " Displaying " + Object.keys(atlasDisplay).length + " entries."
resetEntriesList()
renderBackground(atlasDisplay)
renderHighlight(atlasDisplay)
}
async function resetEntriesList() {
entriesOffset = 0
entriesList.replaceChildren()
entriesList.appendChild(moreEntriesButton)
moreEntriesButton.removeEventListener('click', showMoreEntries)
showMoreEntries = () => {
@ -380,89 +405,92 @@ function buildObjectsList(filter, sort) {
entriesList.removeChild(moreEntriesButton)
}
for (let i = entriesOffset; i < entriesOffset + entriesLimit; i++) {
let entriesLeft = entriesLimit
let element
if (i >= atlasDisplay.length) break
while (entriesLeft > 0 && atlasOrder.length > entriesOffset) {
if (atlasDisplay[atlasOrder[entriesOffset]]) {
// console.log(i, entriesLeft)
const element = createInfoBlock(atlasDisplay[i])
const entry = atlasDisplay[i]
element.addEventListener("mouseenter", function () {
if (fixed || dragging) return
objectsContainer.replaceChildren()
previousScaleZoomOrigin ??= [...scaleZoomOrigin]
previousZoom ??= zoom
setView(entry.center[0], entry.center[1], setZoomByPath(entry.path))
hovered = [entry]
render()
hovered[0].element = this
updateLines()
})
element.addEventListener("click", e => {
toggleFixed(e)
if (!fixed) return
previousScaleZoomOrigin ??= [...scaleZoomOrigin]
previousZoom ??= zoom
applyView()
})
element.addEventListener("mouseleave", () => {
if (fixed || dragging) return
scaleZoomOrigin = [...previousScaleZoomOrigin]
zoom = previousZoom
previousScaleZoomOrigin = undefined
previousZoom = undefined
applyView()
hovered = []
updateLines()
render()
})
let entry = atlasDisplay[atlasOrder[entriesOffset]]
element = createInfoBlock(entry)
element.addEventListener("mouseenter", function () {
if (fixed || dragging) return
objectsContainer.replaceChildren()
previousScaleZoomOrigin ??= [...scaleZoomOrigin]
previousZoom ??= zoom
setView(entry.center[0], entry.center[1], calculateZoomFromPath(entry.path))
hovered = [entry]
renderHighlight()
hovered[0].element = this
renderLines()
})
element.addEventListener("click", e => {
fixed = true
previousScaleZoomOrigin ??= [...scaleZoomOrigin]
previousZoom ??= zoom
applyView()
})
element.addEventListener("mouseleave", () => {
if (fixed || dragging) return
scaleZoomOrigin = [...previousScaleZoomOrigin]
zoom = previousZoom
previousScaleZoomOrigin = undefined
previousZoom = undefined
applyView()
hovered = []
renderLines()
renderHighlight()
})
} else {
let entry = atlas[atlasOrder[entriesOffset]]
element = createInfoBlock(entry, 1)
element.addEventListener("click", async e => {
e.preventDefault()
const [nearestPeriod, nearestVariation] = getNearestPeriod(entry, currentPeriod, currentVariation)
await updateTime(nearestPeriod, nearestVariation, true)
entry = atlasDisplay[entry.id]
element = createInfoBlock(entry)
hovered = [{ ...entry, element }]
fixed = true
previousScaleZoomOrigin = undefined
previousZoom = undefined
const hash = formatHash(entry.id, nearestPeriod, nearestVariation, entry.center[0], entry.center[1], calculateZoomFromPath(entry.path))
location.hash = hash
})
}
entriesOffset += 1
entriesLeft -= 1
entriesList.appendChild(element)
}
entriesOffset += entriesLimit
if (atlasDisplay.length > entriesOffset) {
moreEntriesButton.innerHTML = "Show " + Math.min(entriesLimit, atlasDisplay.length - entriesOffset) + " more"
if (atlasOrder.length > entriesOffset) {
moreEntriesButton.innerHTML = "Show " + Math.min(entriesLimit, atlasOrder.length - entriesOffset) + " more"
entriesList.appendChild(moreEntriesButton)
}
}
moreEntriesButton.addEventListener('click', showMoreEntries)
showMoreEntries()
}
function shuffle() {
//console.log("shuffled atlas")
for (let i = atlasDisplay.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1))
const temp = atlasDisplay[i]
atlasDisplay[i] = atlasDisplay[j]
atlasDisplay[j] = temp
}
}
function resetEntriesList() {
entriesOffset = 0
entriesList.replaceChildren()
entriesList.appendChild(moreEntriesButton)
const sort = sortInput.value || defaultSort
const search = searchInput?.value.toLowerCase()
buildObjectsList(search, sort)
}
async function render() {
async function renderHighlight() {
highlightContext.clearRect(0, 0, highlightCanvas.width, highlightCanvas.height)
@ -478,10 +506,8 @@ async function render() {
container.style.cursor = "default"
}
for (let i = 0; i < hovered.length; i++) {
const path = hovered[i].path
highlightContext.beginPath()
@ -507,28 +533,6 @@ async function render() {
highlightContext.globalCompositeOperation = "source-out"
highlightContext.drawImage(backgroundCanvas, 0, 0)
if (hovered.length === 1 && hovered[0].path.length && hovered[0].overrideImage) {
const undisputableHovered = hovered[0]
// Find the left-topmost point of all the paths
const entryPosition = getPositionOfEntry(undisputableHovered)
if (entryPosition) {
const [startX, startY] = entryPosition
const overrideImage = new Image()
const loadingPromise = new Promise((res, rej) => {
overrideImage.onerror = rej
overrideImage.onload = res
})
overrideImage.src = "imageOverrides/" + undisputableHovered.overrideImage
try {
await loadingPromise
highlightContext.globalCompositeOperation = "source-over"
highlightContext.drawImage(overrideImage, startX, startY)
} catch (ex) {
console.error("Cannot override image.", ex)
}
}
}
for (let i = 0; i < hovered.length; i++) {
const path = hovered[i].path
@ -592,11 +596,15 @@ function updateHovering(e, tapped) {
if (!(pos[0] <= canvasSize.x + canvasOffset.x + 200 && pos[0] >= canvasOffset.x - 200 && pos[1] <= canvasSize.y + canvasOffset.y + 200 && pos[1] >= canvasOffset.x - 200)) return
const newHovered = []
for (const entry of atlasDisplay) {
let newHovered = []
for (const entry of Object.values(atlasDisplay)) {
if (pointIsInPolygon(pos, entry.path)) newHovered.push(entry)
}
newHovered = newHovered.sort(function (a, b) {
return calcPolygonArea(a.path) - calcPolygonArea(b.path)
})
let changed = false
if (hovered.length === newHovered.length) {
@ -612,9 +620,7 @@ function updateHovering(e, tapped) {
if (!changed) return
hovered = newHovered.sort(function (a, b) {
return calcPolygonArea(a.path) - calcPolygonArea(b.path)
})
hovered = newHovered
objectsContainer.replaceChildren()
@ -639,12 +645,13 @@ function updateHovering(e, tapped) {
objectsListOverflowNotice.classList.add("d-none")
entriesList.classList.remove("disableHover")
}
render()
renderLines()
renderHighlight()
}
window.addEventListener("hashchange", updateViewFromHash)
function updateViewFromHash() {
async function updateViewFromHash() {
const hash = window.location.hash.substring(1); //Remove hash prefix
let [hashEntryId, hashPeriod, hashX, hashY, hashZoom] = hash.split('/')
@ -666,7 +673,7 @@ function updateViewFromHash() {
targetPeriod = defaultPeriod
targetVariation = defaultVariation
}
updateTime(targetPeriod, targetVariation, true)
await updateTime(targetPeriod, targetVariation)
setView(
(isNaN(hashX) || hashX === '') ? undefined : Number(hashX),
@ -678,13 +685,8 @@ function updateViewFromHash() {
// Highlight entry from hash
const entries = atlas.filter(e => {
return e.id.toString() === hashEntryId
})
if (entries.length !== 1) return
const entry = entries[0]
const entry = atlasDisplay[hashEntryId]
if (!entry) return
document.title = entry.name + " on " + pageTitle
@ -702,24 +704,24 @@ function updateViewFromHash() {
objectsContainer.replaceChildren()
objectsContainer.appendChild(infoElement)
renderBackground(atlas)
setView(
(isNaN(hashX) || hashX === '') ? entry.center[0] : Number(hashX),
(isNaN(hashY) || hashY === '') ? entry.center[1] : Number(hashY),
(isNaN(hashZoom) || hashZoom === '') ? setZoomByPath(entry.path) : Number(hashZoom)
(isNaN(hashZoom) || hashZoom === '') ? calculateZoomFromPath(entry.path) : Number(hashZoom)
)
closeObjectsListButton.classList.remove("d-none")
entriesList.classList.add("disableHover")
hovered = [entry]
render()
hovered[0].element = infoElement
updateLines()
hovered = [{...entry, element: infoElement}]
renderBackground(atlasDisplay)
renderHighlight(atlasDisplay)
renderLines()
}
function setZoomByPath(path) {
function calculateZoomFromPath(path) {
let zoom
let boundingBox = [canvasSize.x + canvasOffset.x, canvasOffset.x, canvasSize.y + canvasOffset.y, canvasOffset.y]
path?.forEach(([x, y]) => {
boundingBox[0] = Math.min(boundingBox[0], x)
@ -741,13 +743,10 @@ function setZoomByPath(path) {
function initView() {
buildObjectsList(null, null)
renderBackground(atlas)
render()
updateAtlas()
document.addEventListener('timeupdate', () => {
atlasDisplay = atlas.slice()
resetEntriesList()
updateAtlas()
})
// parse linked atlas entry id from link hash
@ -758,22 +757,20 @@ function initView() {
}*/
applyView()
render()
updateLines()
renderLines()
}
function initExplore() {
window.updateHovering = updateHovering
window.render = () => { }
window.renderHighlight = () => { }
function updateHovering(e, tapped) {
if (dragging || (fixed && !tapped)) return
updateCoordsDisplay(e)
}
renderBackground(atlas)
renderBackground({})
applyView()
@ -837,6 +834,6 @@ function initViewGlobal() {
}
document.addEventListener('timeupdate', event => {
drawButton.href = "./?mode=draw" + formatHash(null, event.detail.period, event.detail.period, event.detail.variation)
drawButton.href = "./?mode=draw" + formatHash(null, event.detail.period, event.detail.variation)
})
}

View file

@ -35,6 +35,8 @@
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1, viewport-fit=cover">
<meta name="color-scheme" content="light dark">
<meta name="theme-color" media="(prefers-color-scheme: light)" content="white">
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#222">
<link rel="apple-touch-icon" href="_img/apple-touch-icon.png" sizes="180x180">
<link rel="icon alternate" href="_img/favicon.png" type="image/png" class="js-site-favicon">

View file

@ -9,16 +9,16 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<title>The 2022 r/place Atlas</title>
<meta name="description" content="An interactive map of Reddit's 2022 r/place, with information to each artwork of the canvas provided by the community.">
<title>The 2023 r/place Atlas</title>
<meta name="description" content="An interactive map of Reddit's 2023 r/place, with information to each artwork of the canvas provided by the community.">
<meta name="author" content="Place Atlas contributors (original by Roland Rytz)">
<meta name="application-name" content="The r/place Atlas 2022">
<meta name="application-name" content="The r/place Atlas 2023">
<meta name="robots" content="index, follow">
<meta property="og:title" content="The 2022 r/place Atlas">
<meta property="og:title" content="The 2023 r/place Atlas">
<meta property="og:type" content="website">
<meta property="og:url" content="https://2022.place-atlas.stefanocoding.me/">
<meta property="og:image" content="https://2022.place-atlas.stefanocoding.me/_img/logo.png">
<meta property="og:url" content="https://2023.place-atlas.stefanocoding.me/">
<meta property="og:image" content="https://2023.place-atlas.stefanocoding.me/_img/logo.png">
<meta property="og:image:type" content="image/png">
<meta property="og:image:width" content="512">
<meta property="og:image:height" content="512">
@ -34,17 +34,17 @@
<meta name="twitter:image:alt" content="The 2022 r/place Atlas logo">
<!-- <meta name="google-site-verification" content="gZGHpBSMzffAbIn0qB8b00We6EwSGkDTfDoQVv-NWss"/> -->
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1, minimum-scale=1, maximum-scale=1, shrink-to-fit=no, viewport-fit=cover"> <!-- user-scalable=no -->
<meta name="mobile-web-app-capable" content="yes">
<meta name="color-scheme" content="light dark">
<meta name="theme-color" media="(prefers-color-scheme: light)" content="white">
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#222">
<link rel="apple-touch-icon" href="_img/apple-touch-icon.png" sizes="180x180">
<link rel="icon alternate" href="_img/favicon.png" type="image/png" class="js-site-favicon">
<link rel="icon" href="_img/favicon.svg" type="image/svg+xml" class="js-site-favicon">
<link rel="stylesheet" href="./_css/style.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-dark-5@1.1.3/dist/css/bootstrap-dark.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css">
@ -138,18 +138,19 @@
<ul class="navbar-nav me-auto my-2 my-md-0">
<li class="nav-item">
<div class="btn-group" role="group">
<button id="showListButton" class="btn btn-outline-primary" type="button" data-bs-toggle="offcanvas" data-bs-target="#offcanvasList" aria-controls="offcanvasList">Entries List</button>
<button id="showListButton" class="btn btn-outline-primary" type="button" data-bs-toggle="offcanvas" data-bs-target="#offcanvasList" aria-controls="offcanvasList">Menu</button>
<a class="btn btn-outline-primary" id="drawLink" href="./?mode=draw">Draw</a>
</div>
</li>
</ul>
<ul class="navbar-nav mx-auto d-block d-md-none d-lg-block">
<li class="nav-item d-flex align-items-center gap-2">
<span class="p-2">Coordinates: <span class="badge bg-secondary" id="coords_p">0, 0</span></span>
<span class="p-md-2">Coordinates: <span class="badge bg-secondary" id="coords_p">0, 0</span></span>
</li>
</ul>
<hr class="d-md-none">
<ul class="navbar-nav flex-row flex-wrap ms-auto">
<li class="nav-item dropdown">
<li class="nav-item col-6 col-md-auto dropdown">
<a class="nav-link dropdown-toggle active" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false" aria-current="page">
Atlas Map
</a>
@ -199,8 +200,7 @@ <h5 class="offcanvas-title" id="offcanvasListLabel">Atlas Entries List</h5>
</header>
<div class="py-3 mx-3 border-bottom">
<div class="d-flex gap-2 mb-2">
<a class="btn btn-primary w-50" id="drawLink" href="./?mode=draw">Draw</a>
<button type="button" class="btn btn-primary dropdown-toggle w-50" id="dropdownModes" data-bs-toggle="dropdown" aria-expanded="false">Modes</button>
<button type="button" class="btn btn-primary dropdown-toggle w-100" id="dropdownModes" data-bs-toggle="dropdown" aria-expanded="false">Layer Views</button>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="dropdownModes">
<li><a class="dropdown-item" href="./">Normal</a></li>
<li><a class="dropdown-item" href="./?mode=explore">Explore</a></li>
@ -295,7 +295,7 @@ <h5>Draw</h5>
Redo
</button>
</div>
<button type="button" class="btn btn-primary" id="finishButton" title="Finish drawing" disabled>Finish</button>
<button type="button" class="btn btn-primary" id="finishButton" title="Finish drawing" disabled>Next</button>
<button type="button" class="btn btn-secondary" id="resetButton">Reset</button>
<div class="form-check" id="highlightUnchartedLabel">
@ -332,7 +332,7 @@ <h6>Need Help?</h6>
</div>
<div class="mb-3">
<label for="descriptionField" class="form-label">Description</label>
<textarea id="descriptionField" class="form-control overflow-hidden" placeholder="A short description to be understood by everyone"></textarea>
<textarea id="descriptionField" class="form-control overflow-hidden" placeholder="A short description to be understood by everyone."></textarea>
</div>
<label id="websiteLabel" class="form-label">Website</label>
<div id="websiteGroup" class="mb-3 d-flex flex-column gap-2"></div>

View file

@ -3,9 +3,9 @@
"display": "standalone",
"scope": "/",
"start_url": "/",
"name": "The r/place Atlas",
"short_name": "r/placeAtlas2",
"description": "The atlas for the r/place event of 2022 hosted on Reddit.",
"name": "The 2022 r/place Atlas",
"short_name": "r/placeAtlas2022",
"description": "The atlas for Reddit's r/place event of 2022.",
"icons": [
{
"src": "./_img/pwa/logo-round-192x192.png",

View file

@ -24,7 +24,7 @@ workbox.routing.registerRoute(
workbox.routing.registerRoute(
({ url }) => url.pathname.startsWith('/_img/canvas/'),
new workbox.strategies.CacheFirst({
new workbox.strategies.StaleWhileRevalidate({
cacheName: "canvas",
plugins: [
new workbox.backgroundSync.BackgroundSyncPlugin(