let on_init = [] let on_modify = [] let on_setting_change = [] let on_debug = false let state = {} let statefuls = [] function set_state(key, value, mode = "normal") { if (state[key] !== value) { state[key] = value if (mode === "normal") { render_on_states() } else if (mode === "setting") { render_on_states() for (let f of on_setting_change) f() } } } function render_on_states() { // console.log(states) for (let d of statefuls) { // console.log(d.when) let display = eval(d.when) ? "" : "none" if (d.e.style.display !== display) d.e.style.display = display } } function find(...args) { if (args.length === 1) { return document.querySelector(args[0]) } else if (args.length === 2) { return args[0].querySelector(args[1]) } else { assert(false, "expect 1-2 args") } } function find_all(...args) { if (args.length === 1) { return document.querySelectorAll(args[0]) } else if (args.length === 2) { return args[0].querySelectorAll(args[1]) } else { assert(false, "expect 1-2 args") } } function set_cookie(k, v = null) { if (v !== null) document.cookie = `${k}=${v}; path=/;` else document.cookie = `${k}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;` } function get_cookie(name) { let res = document.cookie.match("(^|;)\\s*" + name + "\\s*=\\s*([^;]+)")?.pop() || "" return res === "" ? null : res } // param > cookie > body function get_setting(params, key) { let res = params.get(key) if (res === null) res = get_cookie(key) if (res === null) res = find("#settings").getAttribute(`data-${key}`) if (res !== null) set_cookie(key, res) return res } function get_site_app_id() { let s = find("#settings") let res = window.location.hostname if (s.getAttribute("data-project-id") !== "") res = s.getAttribute("data-project-id") else if (window.location.hostname.endsWith("localhost")) res = s.getAttribute("data-local-mac") return res } function node(...args) { let node_str = "
" let styles = {} if (args.length == 1) { if (typeof args[0] === "string") { node_str = args[0] } else { styles = args[0] } } else if (args.length == 2) { node_str = args[0] styles = args[1] } const dummy = document.createElement("div") dummy.innerHTML = node_str const res = dummy.firstChild for (let [k, v] of Object.entries(styles)) { res.style[k.replaceAll("_", "-")] = v } return res } function get_webgl_texture_size() { let n = node("", { border: "none", width: "1px", height: "1px" }) let gl = n.getContext("experimental-webgl") let res = gl ? gl.getParameter(gl.MAX_TEXTURE_SIZE) : null return res } function setup_side_nav(side_nav, targets, link_class_name = "side-nav-link") { // cleanup side nav first side_nav.textContent = "" // create links let links = [] for (let i = 0; i < targets.length; i++) { targets[i].id = `side-nav-target-${i}` let link = node( `${targets[i].textContent}` ) links.push(link) side_nav.appendChild(link) } // define activation function let prev_focus = null let f_activate = x => { if (x !== prev_focus) { for (let item of links) item.classList.remove("activate") x.classList.add("activate") prev_focus = x } } // observe targets const observer = new IntersectionObserver( items => { if (items.length > 0 && items[0].isIntersecting) { f_activate(side_nav.querySelector(`#link-${items[0].target.id}`)) } }, { root: null, rootMargin: "0px 0px -90% 0px", threshold: 0.95 } ) for (let x of targets) observer.observe(x) side_nav.addEventListener("click", e => setTimeout(() => f_activate(e.target), 100)) } function setup_panorama_image(e) { let texture_size = get_webgl_texture_size() let src = e.getAttribute("data-src") if (texture_size <= 8192) { src = e.getAttribute("data-mobile-src") e.style.width = "320px" e.style.height = "320px" } if (texture_size === null) e.srcdoc = "WEBGL not supported" else e.srcdoc = `
` } function setup_album(e) { let src = JSON.parse(e.getAttribute("data-items")) let img = node("", { position: "relative", cursor: "pointer" }) e.append(img) let text = node({ position: "absolute", bottom: "5px", right: "10px", color: "white", text_shadow: "1px 1px 0px black, -1px 1px 0px black, 1px -1px 0px black, -1px -1px 0px black", z_index: 10 }) e.append(text) text.textContent = `1/${src.length}` let curr = 0 img.src = src[curr] e.addEventListener("mousedown", () => { curr += 1 if (curr >= src.length) curr = 0 img.src = src[curr] text.textContent = `${curr + 1}/${src.length}` }) } function setup_settings() { for (let e of find_all(".stateful")) statefuls.push({ e: e, when: e.getAttribute("data-when") }) let params = new URLSearchParams(new URL(window.location.href).search) let n_settings = find("#settings") // if lang is in cookie, switch to the same page of this lang let lang = get_setting(params, "lang") let language_options = JSON.parse(n_settings.getAttribute("data-language-options")) if (lang !== null && lang in language_options) { if (window.location.pathname !== language_options[lang][2]) { window.location.replace(language_options[lang][2]) } } // set dark if dark is in cookie let n_body = find("body") let set_dark = mode => { set_cookie("dark", mode) mode === "0" ? n_body.classList.remove("dark") : n_body.classList.add("dark") set_state("dark", mode !== "0", "setting") } set_dark(get_setting(params, "dark")) set_state("page", lang === null ? "src" : lang, null) set_state("page_app_id", `${get_site_app_id()}/${n_settings.getAttribute("data-src-id")}`, null) set_state("language", n_settings.getAttribute("data-language")) let n_lang_button = n_settings.children[0].children[1] let n_lang_options = n_settings.children[1] n_lang_options.addEventListener("mousedown", e => { let _lang = e.target.getAttribute("data-lang") set_cookie("lang", _lang === "src" ? null : _lang) }) n_lang_button.addEventListener("click", () => { if (state.picking_languages) set_state("picking_languages", false) else { set_state("picking_languages", true) let handler = e => { if (!n_settings.contains(e.target)) { set_state("picking_languages", false) document.removeEventListener(document, handler) } } setTimeout(() => document.addEventListener("click", handler), 200) } }) n_settings.children[0].children[0].addEventListener("click", () => set_dark(state.dark === true ? "0" : "1")) } addEventListener("DOMContentLoaded", () => { setup_settings() for (let e of find_all(".album")) setup_album(e) for (let e of find_all(".panorama-image")) setup_panorama_image(e) for (let f of on_init) f() on_modify.push(() => { for (let e of find_all(".album")) setup_album(e) }) window.addEventListener("message", e => { let d = e.data if (typeof d === "object" && "to" in d) { if (d.op === "modify") { let target_e = find(`#${d.args.target_id}`) if (target_e !== null) { target_e.innerHTML = d.args.html for (let f of on_modify) f() } } else if (d.op === "debug") { if (on_debug) { for (let e of find_all("*")) e.classList.remove("debug") } else { for (let e of find_all("*")) e.classList.add("debug") } on_debug = !on_debug } } }) })