180 lines
5.3 KiB
JavaScript
180 lines
5.3 KiB
JavaScript
((exports) => {
|
|
const default_theme = "classic.css";
|
|
const theme_storage_key = "jspaint theme";
|
|
const disable_seasonal_theme_key = "jspaint disable seasonal theme";
|
|
const href_for = theme => `styles/themes/${theme}`;
|
|
|
|
let iid;
|
|
function wait_for_theme_loaded(theme, callback) {
|
|
clearInterval(iid);
|
|
iid = setInterval(() => {
|
|
const theme_loaded =
|
|
getComputedStyle(document.documentElement)
|
|
.getPropertyValue("--theme-loaded")
|
|
.replace(/['"]+/g, "").trim();
|
|
if (theme_loaded === theme) {
|
|
clearInterval(iid);
|
|
callback();
|
|
}
|
|
}, 15);
|
|
}
|
|
|
|
let grinch_button;
|
|
let current_theme;
|
|
try {
|
|
const grinch = localStorage[disable_seasonal_theme_key] === "true";
|
|
const is_december = new Date().getMonth() === 11;
|
|
if (is_december && !grinch) {
|
|
current_theme = "winter.css"; // overriding theme preference until you disable the seasonal theme
|
|
wait_for_theme_loaded(current_theme, () => { // could just wait for DOM to load, but theme is needed for the button styling
|
|
make_grinch_button();
|
|
});
|
|
} else {
|
|
current_theme = localStorage[theme_storage_key] || default_theme;
|
|
}
|
|
} catch (error) {
|
|
console.error(error);
|
|
current_theme = default_theme;
|
|
}
|
|
|
|
const theme_link = document.createElement("link");
|
|
theme_link.rel = "stylesheet";
|
|
theme_link.type = "text/css";
|
|
theme_link.href = href_for(current_theme);
|
|
theme_link.id = "theme-link";
|
|
document.head.appendChild(theme_link);
|
|
|
|
update_not_for_modern_theme();
|
|
|
|
exports.get_theme = () => current_theme;
|
|
|
|
exports.set_theme = theme => {
|
|
current_theme = theme;
|
|
|
|
try {
|
|
localStorage[theme_storage_key] = theme;
|
|
localStorage[disable_seasonal_theme_key] = "true"; // any theme change disables seasonal theme (unless of course you select the seasonal theme)
|
|
grinch_button?.remove();
|
|
// eslint-disable-next-line no-empty
|
|
} catch (error) { }
|
|
|
|
const signal_theme_load = () => {
|
|
$(window).triggerHandler("theme-load");
|
|
$(window).trigger("resize"); // not exactly, but get dynamic cursor to update its offset
|
|
};
|
|
|
|
wait_for_theme_loaded(theme, signal_theme_load);
|
|
theme_link.href = href_for(theme);
|
|
|
|
update_not_for_modern_theme();
|
|
|
|
signal_theme_load();
|
|
};
|
|
|
|
function update_not_for_modern_theme() {
|
|
const not_for_modern = document.querySelectorAll("link.not-for-modern");
|
|
for (const link of not_for_modern) {
|
|
link.disabled = current_theme === "modern.css";
|
|
}
|
|
}
|
|
|
|
function make_grinch_button() {
|
|
const button = document.createElement("button");
|
|
button.ariaLabel = "Disable seasonal theme";
|
|
button.className = "grinch-button";
|
|
let clicked = false;
|
|
let smile = 0;
|
|
let momentum = 0;
|
|
let smile_target = 0;
|
|
let anim_id;
|
|
const num_frames = 38;
|
|
const frame_width = 100;
|
|
button.onclick = () => {
|
|
if (smile === smile_target) {
|
|
steal_christmas();
|
|
}
|
|
clicked = true;
|
|
};
|
|
button.onmouseleave = () => {
|
|
smile_target = clicked ? 1 : 0;
|
|
animate();
|
|
document.removeEventListener('touchmove', document_touchmove);
|
|
};
|
|
button.onmouseenter = () => {
|
|
smile_target = 1;
|
|
momentum = Math.max(momentum, 0.02); // for the immediacy of the hover effect
|
|
animate();
|
|
};
|
|
button.onpointerdown = (event) => {
|
|
if (event.pointerType === "touch") {
|
|
button.onmouseenter();
|
|
document.addEventListener('touchmove', document_touchmove);
|
|
}
|
|
};
|
|
// Not using pointerleave because it includes when the finger is lifted off the screen
|
|
// Maybe it would be easier to detect that case with event.button(s) though.
|
|
function document_touchmove(event) {
|
|
var touch = event.touches[0];
|
|
if (button !== document.elementFromPoint(touch.pageX, touch.pageY)) {
|
|
// finger left the button
|
|
clicked = false;
|
|
button.onmouseleave();
|
|
}
|
|
}
|
|
|
|
function animate() {
|
|
cancelAnimationFrame(anim_id);
|
|
smile += momentum * 0.5;
|
|
momentum *= 0.9; // set to 0.99 to test smile getting stuck (should be fixed)
|
|
if (smile_target) {
|
|
momentum += 0.001;
|
|
} else {
|
|
if (smile < 0.4) {
|
|
momentum -= 0.0005; // slowing down the last bit of un-smiling (feels more natural; I wish there were more frames though)
|
|
} else {
|
|
momentum -= 0.001;
|
|
}
|
|
}
|
|
if (smile > 1) {
|
|
smile = 1;
|
|
momentum = 0;
|
|
if (clicked) {
|
|
steal_christmas();
|
|
}
|
|
} else if (smile < 0) {
|
|
smile = 0;
|
|
momentum = 0;
|
|
}
|
|
if (smile !== smile_target) {
|
|
anim_id = requestAnimationFrame(animate);
|
|
}
|
|
button.style.backgroundPosition = `${-Math.floor(smile * (num_frames - 1)) * frame_width}px 0px`;
|
|
}
|
|
function on_zoom_etc() {
|
|
// scale to nearest pixel-perfect size
|
|
button.style.transform = `scale(${Math.max(1, Math.floor(devicePixelRatio)) / devicePixelRatio})`;
|
|
button.style.transformOrigin = "bottom right";
|
|
button.style.imageRendering = "pixelated";
|
|
}
|
|
window.addEventListener("resize", on_zoom_etc);
|
|
on_zoom_etc();
|
|
function steal_christmas() {
|
|
let new_theme;
|
|
try {
|
|
localStorage[disable_seasonal_theme_key] = "true";
|
|
new_theme = localStorage[theme_storage_key] || default_theme;
|
|
// eslint-disable-next-line no-empty
|
|
} catch (error) { }
|
|
if (new_theme === "winter.css") {
|
|
new_theme = default_theme;
|
|
}
|
|
set_theme(new_theme);
|
|
button.remove();
|
|
window.removeEventListener("resize", on_zoom_etc);
|
|
document.removeEventListener('touchmove', document_touchmove);
|
|
}
|
|
document.body.appendChild(button);
|
|
grinch_button = button;
|
|
}
|
|
})(window);
|