Add js web stuff to landing page + documentation
This commit is contained in:
300
go/jsruntime/superfun.ts
Normal file
300
go/jsruntime/superfun.ts
Normal file
@@ -0,0 +1,300 @@
|
||||
// SuperFun - stupid easter eggs to mess with your coworkers :D
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
SuperFun?: () => void;
|
||||
SuperFun2?: () => void;
|
||||
SuperFun3?: () => void;
|
||||
SuperFun4?: () => void;
|
||||
SuperFunAll?: () => void;
|
||||
}
|
||||
}
|
||||
|
||||
interface SuperFunEgg {
|
||||
isActive(): boolean;
|
||||
enable(): void;
|
||||
disable(): void;
|
||||
}
|
||||
|
||||
// New eggs just push themselves here and SuperFunAll() picks them up for free.
|
||||
const superFunEggs: SuperFunEgg[] = [];
|
||||
|
||||
function superFunToggle(egg: SuperFunEgg) {
|
||||
if (egg.isActive()) egg.disable();
|
||||
else egg.enable();
|
||||
}
|
||||
|
||||
// --- SuperFun: Flip the screen and text
|
||||
|
||||
let superFunActive = false;
|
||||
// Maps each touched text node back to its untouched content so we can restore it.
|
||||
const superFunOriginals = new Map<Text, string>();
|
||||
|
||||
function superFunReverse(input: string): string {
|
||||
return [...input].reverse().join("");
|
||||
}
|
||||
|
||||
function superFunTextNodes(root: Node): Text[] {
|
||||
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
|
||||
acceptNode(node) {
|
||||
if (!node.nodeValue || !node.nodeValue.trim()) return NodeFilter.FILTER_REJECT;
|
||||
const tag = node.parentElement?.tagName;
|
||||
if (tag === "SCRIPT" || tag === "STYLE" || tag === "NOSCRIPT") return NodeFilter.FILTER_REJECT;
|
||||
return NodeFilter.FILTER_ACCEPT;
|
||||
},
|
||||
});
|
||||
|
||||
const nodes: Text[] = [];
|
||||
let node: Node | null;
|
||||
while ((node = walker.nextNode())) nodes.push(node as Text);
|
||||
return nodes;
|
||||
}
|
||||
|
||||
// The flip lives on <body> while the 3D tilt (SuperFun2) lives on <html>, so the
|
||||
// two transforms stack instead of clobbering each other when both are running.
|
||||
const superFunFlip: SuperFunEgg = {
|
||||
isActive: () => superFunActive,
|
||||
enable() {
|
||||
document.body.style.transform = "rotate(180deg)";
|
||||
for (const node of superFunTextNodes(document.body)) {
|
||||
superFunOriginals.set(node, node.nodeValue ?? "");
|
||||
node.nodeValue = superFunReverse(node.nodeValue ?? "");
|
||||
}
|
||||
superFunActive = true;
|
||||
},
|
||||
disable() {
|
||||
document.body.style.transform = "";
|
||||
for (const [node, original] of superFunOriginals) node.nodeValue = original;
|
||||
superFunOriginals.clear();
|
||||
superFunActive = false;
|
||||
},
|
||||
};
|
||||
superFunEggs.push(superFunFlip);
|
||||
|
||||
// --- SuperFun 2: Rotate viewport in 3d
|
||||
|
||||
let superFun2Active = false;
|
||||
let superFun2Timer = 0;
|
||||
const SUPER_FUN_2_INTERVAL = 1800;
|
||||
const SUPER_FUN_2_MAX_TILT = 12; // degrees
|
||||
|
||||
function superFun2Angle(): number {
|
||||
return (Math.random() * 2 - 1) * SUPER_FUN_2_MAX_TILT;
|
||||
}
|
||||
|
||||
function superFun2Tumble() {
|
||||
// perspective() inside the transform gives the page real depth as it tilts.
|
||||
document.documentElement.style.transform =
|
||||
`perspective(1200px) rotateX(${superFun2Angle().toFixed(2)}deg) ` +
|
||||
`rotateY(${superFun2Angle().toFixed(2)}deg) rotateZ(${superFun2Angle().toFixed(2)}deg)`;
|
||||
}
|
||||
|
||||
const superFun3d: SuperFunEgg = {
|
||||
isActive: () => superFun2Active,
|
||||
enable() {
|
||||
document.documentElement.style.transition = `transform ${SUPER_FUN_2_INTERVAL}ms ease-in-out`;
|
||||
superFun2Tumble();
|
||||
superFun2Timer = window.setInterval(superFun2Tumble, SUPER_FUN_2_INTERVAL);
|
||||
superFun2Active = true;
|
||||
},
|
||||
disable() {
|
||||
window.clearInterval(superFun2Timer);
|
||||
superFun2Timer = 0;
|
||||
document.documentElement.style.transition = "";
|
||||
document.documentElement.style.transform = "";
|
||||
superFun2Active = false;
|
||||
},
|
||||
};
|
||||
superFunEggs.push(superFun3d);
|
||||
|
||||
// --- SuperFun3: freecam - fly around a 3D version of the page ---
|
||||
// Treats <body> as a flat plane floating in 3D and drives a virtual camera over
|
||||
// it. Mouse (pointer-locked) looks, WASD moves relative to where you're facing,
|
||||
// Space/Shift go up/down. The page transform each frame is the inverse of the
|
||||
// camera's pose, which is what makes it feel like you're the one moving.
|
||||
|
||||
let superFun3Active = false;
|
||||
let superFun3Raf = 0;
|
||||
let superFun3Hud: HTMLDivElement | null = null;
|
||||
const superFun3Keys: Record<string, boolean> = {};
|
||||
const superFun3Cam = { x: 0, y: 0, z: 0, yaw: 0, pitch: 0 };
|
||||
const SUPER_FUN_3_SPEED = 14; // px moved per frame while a key is held
|
||||
const SUPER_FUN_3_SENS = 0.15; // degrees of look per px of mouse movement
|
||||
const SUPER_FUN_3_PERSPECTIVE = 1200;
|
||||
|
||||
function superFun3IsMoveKey(code: string): boolean {
|
||||
return code === "KeyW" || code === "KeyA" || code === "KeyS" || code === "KeyD"
|
||||
|| code === "Space" || code === "ShiftLeft" || code === "ShiftRight";
|
||||
}
|
||||
|
||||
function superFun3KeyDown(e: KeyboardEvent) {
|
||||
// F toggles camera control: grab the pointer for look/move, or release it
|
||||
// back to the cursor so you can actually click on the page.
|
||||
if (e.code === "KeyF") {
|
||||
e.preventDefault();
|
||||
if (document.pointerLockElement == null) document.body.requestPointerLock();
|
||||
else document.exitPointerLock();
|
||||
return;
|
||||
}
|
||||
|
||||
// Only hijack movement keys while we hold camera control; otherwise let the
|
||||
// page handle them so the freed cursor can scroll/type/click normally.
|
||||
if (document.pointerLockElement == null) return;
|
||||
if (superFun3IsMoveKey(e.code)) e.preventDefault();
|
||||
superFun3Keys[e.code] = true;
|
||||
}
|
||||
|
||||
function superFun3KeyUp(e: KeyboardEvent) {
|
||||
superFun3Keys[e.code] = false;
|
||||
}
|
||||
|
||||
function superFun3MouseMove(e: MouseEvent) {
|
||||
if (document.pointerLockElement == null) return;
|
||||
superFun3Cam.yaw -= e.movementX * SUPER_FUN_3_SENS;
|
||||
superFun3Cam.pitch += e.movementY * SUPER_FUN_3_SENS;
|
||||
superFun3Cam.pitch = Math.max(-89, Math.min(89, superFun3Cam.pitch));
|
||||
}
|
||||
|
||||
function superFun3UpdateHud() {
|
||||
if (!superFun3Hud) return;
|
||||
superFun3Hud.textContent = document.pointerLockElement != null
|
||||
? "FREECAM - WASD move · mouse looks · Space / Shift up / down · F releases the cursor · SuperFun3() exits"
|
||||
: "FREECAM - press F to grab the camera · cursor is free to click · SuperFun3() exits";
|
||||
}
|
||||
|
||||
function superFun3LockChange() {
|
||||
// Releasing control (via F or Esc) shouldn't leave movement keys stuck on.
|
||||
if (document.pointerLockElement == null) {
|
||||
for (const code in superFun3Keys) superFun3Keys[code] = false;
|
||||
}
|
||||
superFun3UpdateHud();
|
||||
}
|
||||
|
||||
function superFun3Frame() {
|
||||
// A rotation-only matrix gives us the camera's facing/right vectors (w=0 so
|
||||
// the translation part is ignored) to move relative to where we're looking.
|
||||
const rot = new DOMMatrix();
|
||||
rot.rotateSelf(0, superFun3Cam.yaw, 0);
|
||||
rot.rotateSelf(superFun3Cam.pitch, 0, 0);
|
||||
const fwd = rot.transformPoint(new DOMPoint(0, 0, -1, 0));
|
||||
const right = rot.transformPoint(new DOMPoint(1, 0, 0, 0));
|
||||
|
||||
const s = SUPER_FUN_3_SPEED;
|
||||
if (superFun3Keys["KeyW"]) { superFun3Cam.x += fwd.x * s; superFun3Cam.y += fwd.y * s; superFun3Cam.z += fwd.z * s; }
|
||||
if (superFun3Keys["KeyS"]) { superFun3Cam.x -= fwd.x * s; superFun3Cam.y -= fwd.y * s; superFun3Cam.z -= fwd.z * s; }
|
||||
if (superFun3Keys["KeyD"]) { superFun3Cam.x += right.x * s; superFun3Cam.y += right.y * s; superFun3Cam.z += right.z * s; }
|
||||
if (superFun3Keys["KeyA"]) { superFun3Cam.x -= right.x * s; superFun3Cam.y -= right.y * s; superFun3Cam.z -= right.z * s; }
|
||||
if (superFun3Keys["Space"]) superFun3Cam.y -= s; // y grows downward in CSS, so up = subtract
|
||||
if (superFun3Keys["ShiftLeft"] || superFun3Keys["ShiftRight"]) superFun3Cam.y += s;
|
||||
|
||||
// World transform = inverse of the camera pose T(cam) * Ry(yaw) * Rx(pitch).
|
||||
const pose = new DOMMatrix();
|
||||
pose.translateSelf(superFun3Cam.x, superFun3Cam.y, superFun3Cam.z);
|
||||
pose.rotateSelf(0, superFun3Cam.yaw, 0);
|
||||
pose.rotateSelf(superFun3Cam.pitch, 0, 0);
|
||||
|
||||
document.body.style.transform = `perspective(${SUPER_FUN_3_PERSPECTIVE}px) ${pose.inverse().toString()}`;
|
||||
superFun3Raf = window.requestAnimationFrame(superFun3Frame);
|
||||
}
|
||||
|
||||
function superFun3MakeHud(): HTMLDivElement {
|
||||
const hud = document.createElement("div");
|
||||
hud.textContent =
|
||||
"FREECAM - press F to grab the camera · cursor is free to click · SuperFun3() exits";
|
||||
hud.style.cssText = [
|
||||
"position:fixed", "left:50%", "bottom:16px", "transform:translateX(-50%)",
|
||||
"z-index:2147483647", "padding:8px 14px", "border-radius:8px",
|
||||
"background:rgba(0,0,0,0.78)", "color:#fff", "font:13px/1.4 system-ui,sans-serif",
|
||||
"pointer-events:none", "white-space:nowrap",
|
||||
].join(";");
|
||||
return hud;
|
||||
}
|
||||
|
||||
const superFun3Freecam: SuperFunEgg = {
|
||||
isActive: () => superFun3Active,
|
||||
enable() {
|
||||
superFun3Cam.x = 0; superFun3Cam.y = 0; superFun3Cam.z = 0;
|
||||
superFun3Cam.yaw = 0; superFun3Cam.pitch = 0;
|
||||
|
||||
// Pivot the world around the current viewport center, and kill scrolling
|
||||
// and per-frame transitions so the camera responds instantly.
|
||||
document.body.style.transition = "none";
|
||||
document.body.style.transformOrigin =
|
||||
`${window.scrollX + window.innerWidth / 2}px ${window.scrollY + window.innerHeight / 2}px`;
|
||||
document.documentElement.style.overflow = "hidden";
|
||||
|
||||
// The HUD hangs off <html> so the body's 3D transform doesn't fly it away.
|
||||
superFun3Hud = superFun3MakeHud();
|
||||
document.documentElement.appendChild(superFun3Hud);
|
||||
|
||||
window.addEventListener("keydown", superFun3KeyDown);
|
||||
window.addEventListener("keyup", superFun3KeyUp);
|
||||
window.addEventListener("mousemove", superFun3MouseMove);
|
||||
document.addEventListener("pointerlockchange", superFun3LockChange);
|
||||
|
||||
superFun3Active = true;
|
||||
superFun3Frame();
|
||||
},
|
||||
disable() {
|
||||
window.cancelAnimationFrame(superFun3Raf);
|
||||
superFun3Raf = 0;
|
||||
|
||||
window.removeEventListener("keydown", superFun3KeyDown);
|
||||
window.removeEventListener("keyup", superFun3KeyUp);
|
||||
window.removeEventListener("mousemove", superFun3MouseMove);
|
||||
document.removeEventListener("pointerlockchange", superFun3LockChange);
|
||||
for (const code in superFun3Keys) superFun3Keys[code] = false;
|
||||
|
||||
if (document.pointerLockElement != null) document.exitPointerLock();
|
||||
superFun3Hud?.remove();
|
||||
superFun3Hud = null;
|
||||
|
||||
document.body.style.transform = "";
|
||||
document.body.style.transition = "";
|
||||
document.body.style.transformOrigin = "";
|
||||
document.documentElement.style.overflow = "";
|
||||
superFun3Active = false;
|
||||
},
|
||||
};
|
||||
superFunEggs.push(superFun3Freecam);
|
||||
|
||||
// --- SuperFun4: endlessly cycle the whole page through messed-up colors ---
|
||||
// Uses an animated CSS filter on <html>. filter is its own property, so this
|
||||
// layers on top of any of the transform-based eggs without fighting them.
|
||||
|
||||
let superFun4Active = false;
|
||||
let superFun4Style: HTMLStyleElement | null = null;
|
||||
|
||||
const superFun4Egg: SuperFunEgg = {
|
||||
isActive: () => superFun4Active,
|
||||
enable() {
|
||||
const style = document.createElement("style");
|
||||
style.textContent = `@keyframes superfun4-colors {
|
||||
0% { filter: hue-rotate(0deg) saturate(1.6); }
|
||||
50% { filter: hue-rotate(180deg) saturate(2.4) invert(0.15); }
|
||||
100% { filter: hue-rotate(360deg) saturate(1.6); }
|
||||
}`;
|
||||
document.head.appendChild(style);
|
||||
superFun4Style = style;
|
||||
document.documentElement.style.animation = "superfun4-colors 4s linear infinite";
|
||||
superFun4Active = true;
|
||||
},
|
||||
disable() {
|
||||
document.documentElement.style.animation = "";
|
||||
superFun4Style?.remove();
|
||||
superFun4Style = null;
|
||||
superFun4Active = false;
|
||||
},
|
||||
};
|
||||
superFunEggs.push(superFun4Egg);
|
||||
|
||||
// --- console entry points ---
|
||||
|
||||
window.SuperFun = () => superFunToggle(superFunFlip);
|
||||
window.SuperFun2 = () => superFunToggle(superFun3d);
|
||||
window.SuperFun3 = () => superFunToggle(superFun3Freecam);
|
||||
window.SuperFun4 = () => superFunToggle(superFun4Egg);
|
||||
|
||||
// Marks this file as a module so the `declare global` Window augmentation above
|
||||
// is honored. The bundler strips local exports, so nothing ships at runtime.
|
||||
export {};
|
||||
Reference in New Issue
Block a user