disbrowser/src/main/ts/main.ts

163 lines
4.4 KiB
TypeScript

function center(el: HTMLElement) {
function documentOffsetTop(el: HTMLElement) {
let top = el.offsetTop;
let parent = el.offsetParent;
if (parent && parent instanceof HTMLElement) {
top += documentOffsetTop(parent);
}
return top;
}
let top = documentOffsetTop(el) - (window.innerHeight / 2);
window.scrollTo(0, top);
}
function highlight(el: HTMLElement | null) {
if (el) {
//el.scrollIntoView();
center(el);
let cl = el.classList;
let activeClass = "line-active";
cl.add(activeClass);
setTimeout(cl.remove.bind(cl, activeClass), 100);
}
}
function fromUrlPart(regex: RegExp, input: string) {
let match = regex.exec(input);
if (!match) {
return null;
}
return document.getElementById(match[1].toLowerCase());
}
function fromHash() {
return fromUrlPart(/^#([0-9A-Fa-f]{6})$/, location.hash);
}
function fromPath() {
return fromUrlPart(/^\/([0-9A-Fa-f]{6})(?:\/.*)?/, location.pathname);
}
function fromUrl() {
return fromHash() || fromPath();
}
function xhr(url: string, method: string = "GET", body: (string | null) = null) {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.onload = () => {
if (xhr.status < 400) {
resolve(xhr);
} else {
reject(xhr);
}
};
xhr.onerror = () => reject(xhr);
xhr.onabort = () => reject(xhr);
xhr.open(method, url);
xhr.send(body);
});
}
highlight(fromUrl());
window.addEventListener("hashchange", function () {
highlight(fromHash())
}, false);
let editables = document.getElementsByClassName("field-editable");
for (let i = 0; i < editables.length; i++) {
let editable = editables[i];
editable.addEventListener("change", e => {
let target = <HTMLInputElement>(e.target);
let field = target.dataset.field || "";
let address = target.dataset.address;
let game = target.dataset.game;
let value = (target).value;
xhr(`/rest/${game}/${address}/${field}`, "POST", value)
.catch((xhr: XMLHttpRequest) => alert("Error: HTTP " + xhr.status));
return false;
});
}
let popupEditables = document.getElementsByClassName("field-editable-popup");
for (let i = 0; i < popupEditables.length; i++) {
let editable = <HTMLSpanElement>(popupEditables[i]);
let first = editable.getElementsByClassName("field-editable-popup-icon")[0];
if (!first) {
continue;
}
first.addEventListener("click", e => {
let field = editable.dataset.field || "";
let address = editable.dataset.address;
let value = editable.dataset.value;
let game = editable.dataset.game;
let newValue = prompt("Label for $" + address, value);
if (newValue === null || newValue == value) {
return false;
}
xhr(`/rest/${game}/${address}/${field}`, "POST", newValue)
.then(() => location.reload())
.catch((xhr: XMLHttpRequest) => alert("Error: HTTP " + xhr.status));
return false;
});
}
class PersistentProperty<T> {
private _value: T;
private readonly key: string;
private readonly onChange: (value: T) => void;
constructor(key: string, defaultValue: T, onChange: (value: T) => void) {
this.key = key;
this.onChange = onChange;
this.value = this.parse(key, defaultValue);
}
get value(): T {
return this._value;
}
set value(value: T) {
this._value = value;
localStorage.setItem(this.key, JSON.stringify(value));
this.onChange(value);
}
private parse(key: string, defaultValue: T): T {
let value = localStorage.getItem(key);
if (value === null) {
return defaultValue;
}
let parsedValue = JSON.parse(value);
if (parsedValue === null) {
return defaultValue;
}
return parsedValue;
}
}
let darkMode = new PersistentProperty<boolean>(
"ui.presentation.darkMode",
false,
(enable) => {
if (enable) {
document.body.classList.add("dark-mode")
} else {
document.body.classList.remove("dark-mode")
}
}
);
let btnDarkMode = document.getElementById("btn-dark-mode");
if (btnDarkMode) {
btnDarkMode.addEventListener("click", () => {
darkMode.value = !darkMode.value;
})
}