mirror of
https://github.com/osmarks/website
synced 2025-10-18 15:37:39 +00:00
initial commit
This commit is contained in:
36
experiments/points/index.html
Normal file
36
experiments/points/index.html
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
title: Arbitrary Points
|
||||
description: Collect Arbitrary Points and achievements by doing things on this website! See how many you have! Do nothing with them because you can't! This is the final form of gamification.
|
||||
slug: points
|
||||
---
|
||||
|
||||
<div id="app">
|
||||
<noscript>You need JS on for this to work. I really don't know what you expected at this point.</noscript>
|
||||
</div>
|
||||
<div class="smallinfo">All Arbitrary Points data is stored and processed only on your device.
|
||||
There is no serverside component whatsoever.
|
||||
If you don't like this regardless, you can bug me to implement an off switch, attempt to ignore it, or use Internet Explorer 6.
|
||||
Ideas for features and achievements and whatever else wanted and may be accepted.
|
||||
This is very easy to meddle with using the browser console, as I haven't tried to prevent that, but if you cheat all the time you may ruin any fun this might have brought.
|
||||
</div>
|
||||
|
||||
<script src="/assets/js/mithril.js"></script>
|
||||
<script>
|
||||
if (!("Promise" in window)) {
|
||||
document.getElementById("app").innerHTML = "Your browser is seemingly too outdated for this to work. Sorry! Try installing/updating Firefox."
|
||||
}
|
||||
</script>
|
||||
<script defer src="./index.js">
|
||||
</script>
|
||||
<style>
|
||||
button {
|
||||
border: 1px solid black;
|
||||
background: lightgray;
|
||||
border-radius: 0;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.metricname {
|
||||
padding-right: 1em;
|
||||
}
|
||||
</style>
|
110
experiments/points/index.js
Normal file
110
experiments/points/index.js
Normal file
@@ -0,0 +1,110 @@
|
||||
const round = x => Math.round(x * 1e10) / 1e10
|
||||
|
||||
const prefixes = ["", "kilo", "mega", "giga", "tera", "peta", "exa", "zetta", "yotta"]
|
||||
const siPrefix = (value, unit) => {
|
||||
let i = 0
|
||||
let b = value
|
||||
while (b > 1000) {
|
||||
b /= 1000
|
||||
i++
|
||||
}
|
||||
return `${round(b).toString()} ${prefixes[i]}${unit}${value !== 1 ? "s" : ""}`
|
||||
}
|
||||
|
||||
const metricDisplayInfo = {
|
||||
pagesVisited: { name: "Pages visited", units: "page" },
|
||||
timeSpent: { name: "Time spent", units: "second" },
|
||||
achievements: { name: "Achievements" },
|
||||
foesVanquished: { name: "Foes vanquished", units: "foe" },
|
||||
deaths: { name: "Deaths", units: "death" },
|
||||
loremParagraphs: { name: "Lorem Ipsum paragraphs seen", units: "paragraph" },
|
||||
commentsPosted: { name: "Comments posted", units: "comment" },
|
||||
greatestInfipage: { name: "Largest infipage visited" }
|
||||
}
|
||||
|
||||
const displayMetric = metric => {
|
||||
let name = metric[0]
|
||||
let value = metric[1]
|
||||
const displayInfo = metricDisplayInfo[name]
|
||||
if (displayInfo) {
|
||||
name = displayInfo.name
|
||||
if (displayInfo.units) {
|
||||
value = siPrefix(value, displayInfo.units)
|
||||
}
|
||||
}
|
||||
return m("tr", m("td.metricname", name), m("td.metricvalue", value))
|
||||
}
|
||||
|
||||
const Metrics = {
|
||||
metrics: null,
|
||||
load: async () => {
|
||||
Metrics.metrics = await points.readAllMetrics()
|
||||
m.redraw()
|
||||
},
|
||||
view: () => m("p", Metrics.metrics === null ? "Loading..." : m("table.metrics", Array.from(Metrics.metrics.entries()).map(displayMetric)))
|
||||
}
|
||||
|
||||
const zfill = (num, z) => num.toString().padStart(z, "0")
|
||||
const formatDate = x => `${zfill(x.getHours(), 2)}:${zfill(x.getMinutes(), 2)}:${zfill(x.getSeconds(), 2)} ${zfill(x.getDate(), 2)}/${zfill(x.getMonth() + 1, 2)}/${zfill(x.getFullYear(), 4)}`
|
||||
|
||||
const renderAchievement = a =>
|
||||
m(".achievement", { style: `background-color: ${colHash(a.title)}` }, [
|
||||
a.timestamp && m(".title", { title: a.id }, `Achievement achieved at ${formatDate(new Date(a.timestamp))}`),
|
||||
m(".title", a.title),
|
||||
m(".description", a.description),
|
||||
m(".conditions", `Unlocked by: ${a.conditions}`),
|
||||
a.points && m(".points", `${a.points} points`)
|
||||
])
|
||||
|
||||
const Achievements = {
|
||||
achievements: [],
|
||||
load: async () => {
|
||||
const raw = await points.getAchievements()
|
||||
Achievements.achievements = raw.map(ach => {
|
||||
const info = points.achievementInfo[ach.id]
|
||||
const out = {
|
||||
title: ach.id || "???",
|
||||
description: `Unrecognized achievement ${ach.id}.`,
|
||||
conditions: "???",
|
||||
...ach
|
||||
}
|
||||
if (info) { Object.assign(out, info) }
|
||||
m.redraw()
|
||||
return out
|
||||
})
|
||||
Achievements.achievements.sort((a, b) => a.timestamp < b.timestamp)
|
||||
},
|
||||
view: () => m(".achievements-listing", Achievements.achievements.map(renderAchievement))
|
||||
}
|
||||
|
||||
const reset = async () => {
|
||||
if (prompt(`This will reset your points, achievements and metrics. If you are sure, please type "yes I am definitely sure".`) === "yes I am definitely sure") {
|
||||
if (confirm("Are you really very sure?")) {
|
||||
await points.reset()
|
||||
window.location.reload()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let pointsCount = "[loading...]"
|
||||
|
||||
const reloadPoints = async () => { pointsCount = await points.getPoints() }
|
||||
|
||||
const App = {
|
||||
view: () => m("div", [
|
||||
m("h2", `Points: ${pointsCount}`),
|
||||
m("button", { onclick: reset }, "Reset"),
|
||||
m(Metrics),
|
||||
m(Achievements)
|
||||
])
|
||||
}
|
||||
|
||||
m.mount(document.getElementById("app"), App)
|
||||
|
||||
Metrics.load()
|
||||
reloadPoints()
|
||||
Achievements.load()
|
||||
|
||||
points.unlockAchievement("visitArbitraryPoints")
|
||||
|
||||
document.addEventListener("points-update", () => { reloadPoints(); Achievements.load() })
|
Reference in New Issue
Block a user