2024-12-08 12:45:29 +01:00
|
|
|
|
import { CommandLine } from "treehouse/command-line.js";
|
|
|
|
|
|
2024-12-08 12:45:29 +01:00
|
|
|
|
class PictureUpload extends HTMLElement {
|
|
|
|
|
constructor() {
|
|
|
|
|
super();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
connectedCallback() {
|
|
|
|
|
this.gotoInit();
|
|
|
|
|
|
|
|
|
|
this.preview = this.querySelector("img[name='preview']");
|
|
|
|
|
|
|
|
|
|
this.addEventListener("click", (event) => {
|
|
|
|
|
if (event.target == this || event.target.parentElement == this) {
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
this.focus();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.addEventListener("paste", async (event) => {
|
|
|
|
|
if (event.clipboardData.items.length != 1) {
|
|
|
|
|
console.error("only one item is supported");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let item = event.clipboardData.items[0];
|
|
|
|
|
await this.paste(item);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gotoInit() {
|
|
|
|
|
this.setState("init");
|
|
|
|
|
this.innerHTML = `
|
2024-12-08 12:45:29 +01:00
|
|
|
|
<div class="nothing-pasted" tabindex="0">
|
2024-12-08 12:45:29 +01:00
|
|
|
|
paste or drop an image here to make a picture out of it
|
|
|
|
|
</div>
|
|
|
|
|
`;
|
2024-12-08 12:45:29 +01:00
|
|
|
|
this.querySelector(".nothing-pasted").focus();
|
2024-12-08 12:45:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async gotoHavePicture(imageType, imageFile) {
|
|
|
|
|
this.setState("have-picture");
|
|
|
|
|
this.innerHTML = `
|
2024-12-08 12:45:29 +01:00
|
|
|
|
<form name="upload" class="have-picture">
|
2024-12-08 12:45:29 +01:00
|
|
|
|
<img name="preview" class="pic" alt="preview">
|
|
|
|
|
<p>
|
|
|
|
|
<span name="preview-width"></span> × <span name="preview-height"></span> px (<span name="file-size"></span>)
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
<p>
|
|
|
|
|
<label for="label">label</label>
|
|
|
|
|
<input name="label" type="text" placeholder="untitled"></input>
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
<p>
|
|
|
|
|
<label for="compression">compression</label>
|
|
|
|
|
<select name="compression">
|
|
|
|
|
<option value="Lossless">lossless</option>
|
|
|
|
|
</select>
|
|
|
|
|
</p>
|
|
|
|
|
|
2024-12-08 12:45:29 +01:00
|
|
|
|
<button type="submit" name="upload">upload</button>
|
2024-12-08 12:45:29 +01:00
|
|
|
|
</div>
|
|
|
|
|
`;
|
|
|
|
|
|
2024-12-08 12:45:29 +01:00
|
|
|
|
let uploadForm = this.querySelector("form[name='upload']");
|
2024-12-08 12:45:29 +01:00
|
|
|
|
let preview = this.querySelector("img[name='preview']");
|
|
|
|
|
let previewWidth = this.querySelector("[name='preview-width']");
|
|
|
|
|
let previewHeight = this.querySelector("[name='preview-height']");
|
|
|
|
|
let fileSize = this.querySelector("[name='file-size']");
|
|
|
|
|
let label = this.querySelector("[name='label']");
|
|
|
|
|
let compression = this.querySelector("[name='compression']");
|
|
|
|
|
|
|
|
|
|
fileSize.textContent = formatSizeSI(imageFile.size);
|
2024-12-08 12:45:29 +01:00
|
|
|
|
label.focus();
|
2024-12-08 12:45:29 +01:00
|
|
|
|
|
|
|
|
|
let url = URL.createObjectURL(imageFile);
|
|
|
|
|
preview.src = url;
|
|
|
|
|
|
|
|
|
|
createImageBitmap(imageFile).then((bitmap) => {
|
|
|
|
|
console.log(bitmap);
|
|
|
|
|
previewWidth.textContent = bitmap.width.toString();
|
|
|
|
|
previewHeight.textContent = bitmap.height.toString();
|
|
|
|
|
});
|
|
|
|
|
|
2024-12-08 12:45:29 +01:00
|
|
|
|
uploadForm.addEventListener("submit", async (event) => {
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
|
2024-12-08 12:45:29 +01:00
|
|
|
|
let params = new URLSearchParams({
|
|
|
|
|
label: label.value,
|
|
|
|
|
format: imageType,
|
|
|
|
|
compression: compression.value,
|
|
|
|
|
});
|
|
|
|
|
let response = await fetch(`/dev/picture-upload?${params}`, {
|
|
|
|
|
method: "POST",
|
|
|
|
|
body: imageFile,
|
|
|
|
|
});
|
|
|
|
|
let json = await response.json();
|
|
|
|
|
if (json.error != null) {
|
|
|
|
|
console.error(json.error);
|
|
|
|
|
} else {
|
|
|
|
|
await navigator.clipboard.writeText(json.ulid);
|
|
|
|
|
await this.gotoCopiedToClipboard();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async gotoCopiedToClipboard() {
|
|
|
|
|
this.setState("copied-to-clipboard");
|
|
|
|
|
this.innerHTML = `
|
|
|
|
|
<div class="copied-to-clipboard">ulid copied to clipboard; the window will now refresh</div>
|
|
|
|
|
`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setState(name) {
|
|
|
|
|
this.setAttribute("data-state", name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async paste(item) {
|
|
|
|
|
console.log(item);
|
|
|
|
|
if (!isSupportedImageType(item.type)) {
|
|
|
|
|
console.error("unsupported mime type", item.type);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let file = item.getAsFile();
|
|
|
|
|
if (file == null) {
|
|
|
|
|
console.error("data transfer does not contain a file");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await this.gotoHavePicture(item.type, file);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
customElements.define("th-picture-upload", PictureUpload);
|
|
|
|
|
|
|
|
|
|
function isSupportedImageType(mime) {
|
|
|
|
|
return (
|
|
|
|
|
mime == "image/png" ||
|
|
|
|
|
mime == "image/jpeg" ||
|
|
|
|
|
mime == "image/svg+xml" ||
|
|
|
|
|
mime == "image/webp"
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function formatSizeSI(bytes) {
|
|
|
|
|
return new Intl.NumberFormat(undefined, {
|
|
|
|
|
style: "unit",
|
|
|
|
|
unit: "byte",
|
|
|
|
|
notation: "compact",
|
|
|
|
|
unitDisplay: "narrow",
|
|
|
|
|
}).format(bytes);
|
|
|
|
|
}
|
2024-12-08 12:45:29 +01:00
|
|
|
|
|
|
|
|
|
if (TREEHOUSE_DEV) {
|
|
|
|
|
CommandLine.registerCommand({
|
|
|
|
|
aliases: ["addpic"],
|
|
|
|
|
description: "add a picture interactively and copy its ulid",
|
|
|
|
|
run() {
|
|
|
|
|
let dialog = document.body.appendChild(document.createElement("dialog"));
|
|
|
|
|
dialog.addEventListener("keydown", (event) => {
|
|
|
|
|
if (event.key == "Escape") dialog.close();
|
|
|
|
|
});
|
|
|
|
|
dialog.addEventListener("close", () => {
|
|
|
|
|
dialog.remove();
|
|
|
|
|
});
|
|
|
|
|
dialog.appendChild(new PictureUpload());
|
|
|
|
|
dialog.show();
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
}
|